blob: b9604b21ea3ccaf33f505f7ba3a06a375b0d98e8 [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
Hung-Te Linf2f78f72012-02-08 19:27:11 +08002# -*- coding: utf-8 -*-
3#
Jon Salz37eccbd2012-05-25 16:06:52 +08004# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08005# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8'''
9The main factory flow that runs the factory test and finalizes a device.
10'''
11
Jon Salz0405ab52012-03-16 15:26:52 +080012import logging
13import os
Jon Salz73e0fd02012-04-04 11:46:38 +080014import Queue
Jon Salz77c151e2012-08-28 07:20:37 +080015import signal
Jon Salz0405ab52012-03-16 15:26:52 +080016import sys
Jon Salz0405ab52012-03-16 15:26:52 +080017import threading
18import time
19import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080020import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080021from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080022from collections import deque
23from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080024
Jon Salz0697cbf2012-07-04 15:14:04 +080025import factory_common # pylint: disable=W0611
jcliangcd688182012-08-20 21:01:26 +080026from cros.factory import event_log
27from cros.factory import system
28from cros.factory.event_log import EventLog
29from cros.factory.goofy import test_environment
30from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080031from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080032from cros.factory.goofy.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080033from cros.factory.goofy.goofy_rpc import GoofyRPC
34from cros.factory.goofy.invocation import TestInvocation
35from cros.factory.goofy.prespawner import Prespawner
36from cros.factory.goofy.web_socket_manager import WebSocketManager
37from cros.factory.system.charge_manager import ChargeManager
38from cros.factory.test import factory
39from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080040from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080041from cros.factory.test import utils
42from cros.factory.test.event import Event
43from cros.factory.test.event import EventClient
44from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080045from cros.factory.test.factory import TestState
Jon Salz78c32392012-07-25 14:18:29 +080046from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080047
48
Jon Salz2f757d42012-06-27 17:06:42 +080049DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
50CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080051HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
52
Jon Salz8796e362012-05-24 11:39:09 +080053# File that suppresses reboot if present (e.g., for development).
54NO_REBOOT_FILE = '/var/log/factory.noreboot'
55
Jon Salz5c344f62012-07-13 14:31:16 +080056# Value for tests_after_shutdown that forces auto-run (e.g., after
57# a factory update, when the available set of tests might change).
58FORCE_AUTO_RUN = 'force_auto_run'
59
cychiang21886742012-07-05 15:16:32 +080060RUN_QUEUE_TIMEOUT_SECS = 10
61
Jon Salz758e6cc2012-04-03 15:47:07 +080062GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
63You are running Goofy inside the chroot. Autotests are not supported.
64
65To use Goofy in the chroot, first install an Xvnc server:
66
Jon Salz0697cbf2012-07-04 15:14:04 +080067 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080068
69...and then start a VNC X server outside the chroot:
70
Jon Salz0697cbf2012-07-04 15:14:04 +080071 vncserver :10 &
72 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080073
74...and run Goofy as follows:
75
Jon Salz0697cbf2012-07-04 15:14:04 +080076 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080077''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080078suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080079
80def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080081 '''
82 Returns the HWID config tag, or an empty string if none can be found.
83 '''
84 if 'CROS_HWID' in os.environ:
85 return os.environ['CROS_HWID']
86 if os.path.exists(HWID_CFG_PATH):
87 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
88 return hwid_cfg_handle.read().strip()
89 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080090
91
92def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080093 '''
94 Returns the path to the active test list, based on the HWID config tag.
95 '''
96 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +080097
Jon Salz0697cbf2012-07-04 15:14:04 +080098 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
Jon Salz2f757d42012-06-27 17:06:42 +080099
Jon Salz0697cbf2012-07-04 15:14:04 +0800100 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
101 search_files = ['test_list', 'test_list.all']
102 if hwid_cfg:
103 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800104
Jon Salz0697cbf2012-07-04 15:14:04 +0800105 for d in search_dirs:
106 for f in search_files:
107 test_list = os.path.join(d, f)
108 if os.path.exists(test_list):
109 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800110
Jon Salz0697cbf2012-07-04 15:14:04 +0800111 logging.warn('Cannot find test lists named any of %s in any of %s',
112 search_files, search_dirs)
113 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800114
Jon Salz73e0fd02012-04-04 11:46:38 +0800115_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800116
117class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800118 '''
119 The main factory flow.
120
121 Note that all methods in this class must be invoked from the main
122 (event) thread. Other threads, such as callbacks and TestInvocation
123 methods, should instead post events on the run queue.
124
125 TODO: Unit tests. (chrome-os-partner:7409)
126
127 Properties:
128 uuid: A unique UUID for this invocation of Goofy.
129 state_instance: An instance of FactoryState.
130 state_server: The FactoryState XML/RPC server.
131 state_server_thread: A thread running state_server.
132 event_server: The EventServer socket server.
133 event_server_thread: A thread running event_server.
134 event_client: A client to the event server.
135 connection_manager: The connection_manager object.
136 network_enabled: Whether the connection_manager is currently
137 enabling connections.
138 ui_process: The factory ui process object.
139 run_queue: A queue of callbacks to invoke from the main thread.
140 invocations: A map from FactoryTest objects to the corresponding
141 TestInvocations objects representing active tests.
142 tests_to_run: A deque of tests that should be run when the current
143 test(s) complete.
144 options: Command-line options.
145 args: Command-line args.
146 test_list: The test list.
147 event_handlers: Map of Event.Type to the method used to handle that
148 event. If the method has an 'event' argument, the event is passed
149 to the handler.
150 exceptions: Exceptions encountered in invocation threads.
151 '''
152 def __init__(self):
153 self.uuid = str(uuid.uuid4())
154 self.state_instance = None
155 self.state_server = None
156 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800157 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800158 self.event_server = None
159 self.event_server_thread = None
160 self.event_client = None
161 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800162 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800163 self.time_sanitizer = None
164 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800165 self.log_watcher = None
166 self.network_enabled = True
167 self.event_log = None
168 self.prespawner = None
169 self.ui_process = None
170 self.run_queue = Queue.Queue()
171 self.invocations = {}
172 self.tests_to_run = deque()
173 self.visible_test = None
174 self.chrome = None
175
176 self.options = None
177 self.args = None
178 self.test_list = None
179 self.on_ui_startup = []
180 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800181 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800182 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800183 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800184 self.last_sync_time = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800185
Jon Salz85a39882012-07-05 16:45:04 +0800186 def test_or_root(event, parent_or_group=True):
187 '''Returns the test affected by a particular event.
188
189 Args:
190 event: The event containing an optional 'path' attribute.
191 parent_on_group: If True, returns the top-level parent for a test (the
192 root node of the tests that need to be run together if the given test
193 path is to be run).
194 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800195 try:
196 path = event.path
197 except AttributeError:
198 path = None
199
200 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800201 test = self.test_list.lookup_path(path)
202 if parent_or_group:
203 test = test.get_top_level_parent_or_group()
204 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800205 else:
206 return self.test_list
207
208 self.event_handlers = {
209 Event.Type.SWITCH_TEST: self.handle_switch_test,
210 Event.Type.SHOW_NEXT_ACTIVE_TEST:
211 lambda event: self.show_next_active_test(),
212 Event.Type.RESTART_TESTS:
213 lambda event: self.restart_tests(root=test_or_root(event)),
214 Event.Type.AUTO_RUN:
215 lambda event: self.auto_run(root=test_or_root(event)),
216 Event.Type.RE_RUN_FAILED:
217 lambda event: self.re_run_failed(root=test_or_root(event)),
218 Event.Type.RUN_TESTS_WITH_STATUS:
219 lambda event: self.run_tests_with_status(
220 event.status,
221 root=test_or_root(event)),
222 Event.Type.REVIEW:
223 lambda event: self.show_review_information(),
224 Event.Type.UPDATE_SYSTEM_INFO:
225 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800226 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800227 lambda event: self.stop(root=test_or_root(event, False),
228 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800229 Event.Type.SET_VISIBLE_TEST:
230 lambda event: self.set_visible_test(
231 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800232 }
233
234 self.exceptions = []
235 self.web_socket_manager = None
236
237 def destroy(self):
238 if self.chrome:
239 self.chrome.kill()
240 self.chrome = None
241 if self.ui_process:
242 utils.kill_process_tree(self.ui_process, 'ui')
243 self.ui_process = None
244 if self.web_socket_manager:
245 logging.info('Stopping web sockets')
246 self.web_socket_manager.close()
247 self.web_socket_manager = None
248 if self.state_server_thread:
249 logging.info('Stopping state server')
250 self.state_server.shutdown()
251 self.state_server_thread.join()
252 self.state_server.server_close()
253 self.state_server_thread = None
254 if self.state_instance:
255 self.state_instance.close()
256 if self.event_server_thread:
257 logging.info('Stopping event server')
258 self.event_server.shutdown() # pylint: disable=E1101
259 self.event_server_thread.join()
260 self.event_server.server_close()
261 self.event_server_thread = None
262 if self.log_watcher:
263 if self.log_watcher.IsThreadStarted():
264 self.log_watcher.StopWatchThread()
265 self.log_watcher = None
266 if self.prespawner:
267 logging.info('Stopping prespawner')
268 self.prespawner.stop()
269 self.prespawner = None
270 if self.event_client:
271 logging.info('Closing event client')
272 self.event_client.close()
273 self.event_client = None
274 if self.event_log:
275 self.event_log.Close()
276 self.event_log = None
277 self.check_exceptions()
278 logging.info('Done destroying Goofy')
279
280 def start_state_server(self):
281 self.state_instance, self.state_server = (
282 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800283 self.goofy_rpc = GoofyRPC(self)
284 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800285 logging.info('Starting state server')
286 self.state_server_thread = threading.Thread(
287 target=self.state_server.serve_forever,
288 name='StateServer')
289 self.state_server_thread.start()
290
291 def start_event_server(self):
292 self.event_server = EventServer()
293 logging.info('Starting factory event server')
294 self.event_server_thread = threading.Thread(
295 target=self.event_server.serve_forever,
296 name='EventServer') # pylint: disable=E1101
297 self.event_server_thread.start()
298
299 self.event_client = EventClient(
300 callback=self.handle_event, event_loop=self.run_queue)
301
302 self.web_socket_manager = WebSocketManager(self.uuid)
303 self.state_server.add_handler("/event",
304 self.web_socket_manager.handle_web_socket)
305
306 def start_ui(self):
307 ui_proc_args = [
308 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
309 self.options.test_list]
310 if self.options.verbose:
311 ui_proc_args.append('-v')
312 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800313 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800314 logging.info('Waiting for UI to come up...')
315 self.event_client.wait(
316 lambda event: event.type == Event.Type.UI_READY)
317 logging.info('UI has started')
318
319 def set_visible_test(self, test):
320 if self.visible_test == test:
321 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800322 if test and not test.has_ui:
323 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800324
325 if test:
326 test.update_state(visible=True)
327 if self.visible_test:
328 self.visible_test.update_state(visible=False)
329 self.visible_test = test
330
331 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800332 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800333 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800334
Jon Salz0697cbf2012-07-04 15:14:04 +0800335 @param test: The ShutdownStep.
336 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800337 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800338 test_state = test.update_state(increment_shutdown_count=1)
339 logging.info('Detected shutdown (%d of %d)',
340 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800341
Jon Salz0697cbf2012-07-04 15:14:04 +0800342 def log_and_update_state(status, error_msg, **kw):
343 self.event_log.Log('rebooted',
344 status=status, error_msg=error_msg, **kw)
345 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800346
Jon Salz0697cbf2012-07-04 15:14:04 +0800347 if not self.last_shutdown_time:
348 log_and_update_state(status=TestState.FAILED,
349 error_msg='Unable to read shutdown_time')
350 return
Jon Salz258a40c2012-04-19 12:34:01 +0800351
Jon Salz0697cbf2012-07-04 15:14:04 +0800352 now = time.time()
353 logging.info('%.03f s passed since reboot',
354 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800355
Jon Salz0697cbf2012-07-04 15:14:04 +0800356 if self.last_shutdown_time > now:
357 test.update_state(status=TestState.FAILED,
358 error_msg='Time moved backward during reboot')
359 elif (isinstance(test, factory.RebootStep) and
360 self.test_list.options.max_reboot_time_secs and
361 (now - self.last_shutdown_time >
362 self.test_list.options.max_reboot_time_secs)):
363 # A reboot took too long; fail. (We don't check this for
364 # HaltSteps, because the machine could be halted for a
365 # very long time, and even unplugged with battery backup,
366 # thus hosing the clock.)
367 log_and_update_state(
368 status=TestState.FAILED,
369 error_msg=('More than %d s elapsed during reboot '
370 '(%.03f s, from %s to %s)' % (
371 self.test_list.options.max_reboot_time_secs,
372 now - self.last_shutdown_time,
373 utils.TimeString(self.last_shutdown_time),
374 utils.TimeString(now))),
375 duration=(now-self.last_shutdown_time))
376 elif test_state.shutdown_count == test.iterations:
377 # Good!
378 log_and_update_state(status=TestState.PASSED,
379 duration=(now - self.last_shutdown_time),
380 error_msg='')
381 elif test_state.shutdown_count > test.iterations:
382 # Shut down too many times
383 log_and_update_state(status=TestState.FAILED,
384 error_msg='Too many shutdowns')
385 elif utils.are_shift_keys_depressed():
386 logging.info('Shift keys are depressed; cancelling restarts')
387 # Abort shutdown
388 log_and_update_state(
389 status=TestState.FAILED,
390 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800391 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800392 else:
393 def handler():
394 if self._prompt_cancel_shutdown(
395 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800396 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800397 log_and_update_state(
398 status=TestState.FAILED,
399 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800400 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800401 return
Jon Salz0405ab52012-03-16 15:26:52 +0800402
Jon Salz0697cbf2012-07-04 15:14:04 +0800403 # Time to shutdown again
404 log_and_update_state(
405 status=TestState.ACTIVE,
406 error_msg='',
407 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800408
Jon Salz0697cbf2012-07-04 15:14:04 +0800409 self.event_log.Log('shutdown', operation='reboot')
410 self.state_instance.set_shared_data('shutdown_time',
411 time.time())
412 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800413
Jon Salz0697cbf2012-07-04 15:14:04 +0800414 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800415
Jon Salz0697cbf2012-07-04 15:14:04 +0800416 def _prompt_cancel_shutdown(self, test, iteration):
417 if self.options.ui != 'chrome':
418 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800419
Jon Salz0697cbf2012-07-04 15:14:04 +0800420 pending_shutdown_data = {
421 'delay_secs': test.delay_secs,
422 'time': time.time() + test.delay_secs,
423 'operation': test.operation,
424 'iteration': iteration,
425 'iterations': test.iterations,
426 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800427
Jon Salz0697cbf2012-07-04 15:14:04 +0800428 # Create a new (threaded) event client since we
429 # don't want to use the event loop for this.
430 with EventClient() as event_client:
431 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
432 **pending_shutdown_data))
433 aborted = event_client.wait(
434 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
435 timeout=test.delay_secs) is not None
436 if aborted:
437 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
438 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800439
Jon Salz0697cbf2012-07-04 15:14:04 +0800440 def init_states(self):
441 '''
442 Initializes all states on startup.
443 '''
444 for test in self.test_list.get_all_tests():
445 # Make sure the state server knows about all the tests,
446 # defaulting to an untested state.
447 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800448
Jon Salz0697cbf2012-07-04 15:14:04 +0800449 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800450 mosys_log = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800451
Jon Salz0697cbf2012-07-04 15:14:04 +0800452 # Any 'active' tests should be marked as failed now.
453 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800454 if not test.is_leaf():
455 # Don't bother with parents; they will be updated when their
456 # children are updated.
457 continue
458
Jon Salz0697cbf2012-07-04 15:14:04 +0800459 test_state = test.get_state()
460 if test_state.status != TestState.ACTIVE:
461 continue
462 if isinstance(test, factory.ShutdownStep):
463 # Shutdown while the test was active - that's good.
464 self.handle_shutdown_complete(test, test_state)
465 else:
466 # Unexpected shutdown. Grab /var/log/messages for context.
467 if var_log_messages is None:
468 try:
469 var_log_messages = (
470 utils.var_log_messages_before_reboot())
471 # Write it to the log, to make it easier to
472 # correlate with /var/log/messages.
473 logging.info(
474 'Unexpected shutdown. '
475 'Tail of /var/log/messages before last reboot:\n'
476 '%s', ('\n'.join(
477 ' ' + x for x in var_log_messages)))
478 except: # pylint: disable=W0702
479 logging.exception('Unable to grok /var/log/messages')
480 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800481
Vic Yanga9c32212012-08-16 20:07:54 +0800482 if mosys_log is None:
483 mosys_log = utils.Spawn(['mosys', 'eventlog', 'list'],
484 read_stdout=True, log_stderr_on_error=True).stdout_data
485 # Write it to the log also.
486 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
487
Jon Salz0697cbf2012-07-04 15:14:04 +0800488 error_msg = 'Unexpected shutdown while test was running'
489 self.event_log.Log('end_test',
490 path=test.path,
491 status=TestState.FAILED,
492 invocation=test.get_state().invocation,
493 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800494 var_log_messages='\n'.join(var_log_messages),
495 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800496 test.update_state(
497 status=TestState.FAILED,
498 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800499
Jon Salz50efe942012-07-26 11:54:10 +0800500 if not test.never_fails:
501 # For "never_fails" tests (such as "Start"), don't cancel
502 # pending tests, since reboot is expected.
503 factory.console.info('Unexpected shutdown while test %s '
504 'running; cancelling any pending tests',
505 test.path)
506 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800507
Jon Salz0697cbf2012-07-04 15:14:04 +0800508 def show_next_active_test(self):
509 '''
510 Rotates to the next visible active test.
511 '''
512 self.reap_completed_tests()
513 active_tests = [
514 t for t in self.test_list.walk()
515 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
516 if not active_tests:
517 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800518
Jon Salz0697cbf2012-07-04 15:14:04 +0800519 try:
520 next_test = active_tests[
521 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
522 except ValueError: # visible_test not present in active_tests
523 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800524
Jon Salz0697cbf2012-07-04 15:14:04 +0800525 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800526
Jon Salz0697cbf2012-07-04 15:14:04 +0800527 def handle_event(self, event):
528 '''
529 Handles an event from the event server.
530 '''
531 handler = self.event_handlers.get(event.type)
532 if handler:
533 handler(event)
534 else:
535 # We don't register handlers for all event types - just ignore
536 # this event.
537 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800538
Jon Salz0697cbf2012-07-04 15:14:04 +0800539 def run_next_test(self):
540 '''
541 Runs the next eligible test (or tests) in self.tests_to_run.
542 '''
543 self.reap_completed_tests()
544 while self.tests_to_run:
545 logging.debug('Tests to run: %s',
546 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800547
Jon Salz0697cbf2012-07-04 15:14:04 +0800548 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800549
Jon Salz0697cbf2012-07-04 15:14:04 +0800550 if test in self.invocations:
551 logging.info('Next test %s is already running', test.path)
552 self.tests_to_run.popleft()
553 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800554
Jon Salza1412922012-07-23 16:04:17 +0800555 for requirement in test.require_run:
556 for i in requirement.test.walk():
557 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800558 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800559 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800560 return
561
Jon Salz0697cbf2012-07-04 15:14:04 +0800562 if self.invocations and not (test.backgroundable and all(
563 [x.backgroundable for x in self.invocations])):
564 logging.debug('Waiting for non-backgroundable tests to '
565 'complete before running %s', test.path)
566 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800567
Jon Salz0697cbf2012-07-04 15:14:04 +0800568 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800569
Jon Salz304a75d2012-07-06 11:14:15 +0800570 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800571 for requirement in test.require_run:
572 for i in requirement.test.walk():
573 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800574 # We've hit this test itself; stop checking
575 break
Jon Salza1412922012-07-23 16:04:17 +0800576 if ((i.get_state().status == TestState.UNTESTED) or
577 (requirement.passed and i.get_state().status !=
578 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800579 # Found an untested test; move on to the next
580 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800581 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800582 break
583
584 if untested:
585 untested_paths = ', '.join(sorted([x.path for x in untested]))
586 if self.state_instance.get_shared_data('engineering_mode',
587 optional=True):
588 # In engineering mode, we'll let it go.
589 factory.console.warn('In engineering mode; running '
590 '%s even though required tests '
591 '[%s] have not completed',
592 test.path, untested_paths)
593 else:
594 # Not in engineering mode; mark it failed.
595 error_msg = ('Required tests [%s] have not been run yet'
596 % untested_paths)
597 factory.console.error('Not running %s: %s',
598 test.path, error_msg)
599 test.update_state(status=TestState.FAILED,
600 error_msg=error_msg)
601 continue
602
Jon Salz0697cbf2012-07-04 15:14:04 +0800603 if isinstance(test, factory.ShutdownStep):
604 if os.path.exists(NO_REBOOT_FILE):
605 test.update_state(
606 status=TestState.FAILED, increment_count=1,
607 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800608 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800609 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800610
Jon Salz0697cbf2012-07-04 15:14:04 +0800611 test.update_state(status=TestState.ACTIVE, increment_count=1,
612 error_msg='', shutdown_count=0)
613 if self._prompt_cancel_shutdown(test, 1):
614 self.event_log.Log('reboot_cancelled')
615 test.update_state(
616 status=TestState.FAILED, increment_count=1,
617 error_msg='Shutdown aborted by operator',
618 shutdown_count=0)
619 return
Jon Salz2f757d42012-06-27 17:06:42 +0800620
Jon Salz0697cbf2012-07-04 15:14:04 +0800621 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800622 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800623 'tests_after_shutdown',
624 [t.path for t in self.tests_to_run])
625 # Save shutdown time
626 self.state_instance.set_shared_data('shutdown_time',
627 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800628
Jon Salz0697cbf2012-07-04 15:14:04 +0800629 with self.env.lock:
630 self.event_log.Log('shutdown', operation=test.operation)
631 shutdown_result = self.env.shutdown(test.operation)
632 if shutdown_result:
633 # That's all, folks!
634 self.run_queue.put(None)
635 return
636 else:
637 # Just pass (e.g., in the chroot).
638 test.update_state(status=TestState.PASSED)
639 self.state_instance.set_shared_data(
640 'tests_after_shutdown', None)
641 # Send event with no fields to indicate that there is no
642 # longer a pending shutdown.
643 self.event_client.post_event(Event(
644 Event.Type.PENDING_SHUTDOWN))
645 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800646
Jon Salz1acc8742012-07-17 17:45:55 +0800647 self._run_test(test, test.iterations)
648
649 def _run_test(self, test, iterations_left=None):
650 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
651 new_state = test.update_state(
652 status=TestState.ACTIVE, increment_count=1, error_msg='',
653 invocation=invoc.uuid, iterations_left=iterations_left)
654 invoc.count = new_state.count
655
656 self.invocations[test] = invoc
657 if self.visible_test is None and test.has_ui:
658 self.set_visible_test(test)
659 self.check_connection_manager()
660 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800661
Jon Salz0697cbf2012-07-04 15:14:04 +0800662 def check_connection_manager(self):
663 exclusive_tests = [
664 test.path
665 for test in self.invocations
666 if test.is_exclusive(
667 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
668 if exclusive_tests:
669 # Make sure networking is disabled.
670 if self.network_enabled:
671 logging.info('Disabling network, as requested by %s',
672 exclusive_tests)
673 self.connection_manager.DisableNetworking()
674 self.network_enabled = False
675 else:
676 # Make sure networking is enabled.
677 if not self.network_enabled:
678 logging.info('Re-enabling network')
679 self.connection_manager.EnableNetworking()
680 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800681
cychiang21886742012-07-05 15:16:32 +0800682 def check_for_updates(self):
683 '''
684 Schedules an asynchronous check for updates if necessary.
685 '''
686 if not self.test_list.options.update_period_secs:
687 # Not enabled.
688 return
689
690 now = time.time()
691 if self.last_update_check and (
692 now - self.last_update_check <
693 self.test_list.options.update_period_secs):
694 # Not yet time for another check.
695 return
696
697 self.last_update_check = now
698
699 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
700 if reached_shopfloor:
701 new_update_md5sum = md5sum if needs_update else None
702 if system.SystemInfo.update_md5sum != new_update_md5sum:
703 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
704 system.SystemInfo.update_md5sum = new_update_md5sum
705 self.run_queue.put(self.update_system_info)
706
707 updater.CheckForUpdateAsync(
708 handle_check_for_update,
709 self.test_list.options.shopfloor_timeout_secs)
710
Jon Salza6711d72012-07-18 14:33:03 +0800711 def cancel_pending_tests(self):
712 '''Cancels any tests in the run queue.'''
713 self.run_tests([])
714
Jon Salz0697cbf2012-07-04 15:14:04 +0800715 def run_tests(self, subtrees, untested_only=False):
716 '''
717 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800718
Jon Salz0697cbf2012-07-04 15:14:04 +0800719 The tests are run in order unless one fails (then stops).
720 Backgroundable tests are run simultaneously; when a foreground test is
721 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800722
Jon Salz0697cbf2012-07-04 15:14:04 +0800723 @param subtrees: Node or nodes containing tests to run (may either be
724 a single test or a list). Duplicates will be ignored.
725 '''
726 if type(subtrees) != list:
727 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800728
Jon Salz0697cbf2012-07-04 15:14:04 +0800729 # Nodes we've seen so far, to avoid duplicates.
730 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800731
Jon Salz0697cbf2012-07-04 15:14:04 +0800732 self.tests_to_run = deque()
733 for subtree in subtrees:
734 for test in subtree.walk():
735 if test in seen:
736 continue
737 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800738
Jon Salz0697cbf2012-07-04 15:14:04 +0800739 if not test.is_leaf():
740 continue
741 if (untested_only and
742 test.get_state().status != TestState.UNTESTED):
743 continue
744 self.tests_to_run.append(test)
745 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800746
Jon Salz0697cbf2012-07-04 15:14:04 +0800747 def reap_completed_tests(self):
748 '''
749 Removes completed tests from the set of active tests.
750
751 Also updates the visible test if it was reaped.
752 '''
753 for t, v in dict(self.invocations).iteritems():
754 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800755 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800756 del self.invocations[t]
757
Jon Salz1acc8742012-07-17 17:45:55 +0800758 if new_state.iterations_left and new_state.status == TestState.PASSED:
759 # Play it again, Sam!
760 self._run_test(t)
761
Jon Salz0697cbf2012-07-04 15:14:04 +0800762 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800763 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800764 self.set_visible_test(None)
765 # Make the first running test, if any, the visible test
766 for t in self.test_list.walk():
767 if t in self.invocations:
768 self.set_visible_test(t)
769 break
770
Jon Salz85a39882012-07-05 16:45:04 +0800771 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800772 '''
773 Kills and waits for all active tests.
774
Jon Salz85a39882012-07-05 16:45:04 +0800775 Args:
776 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800777 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800778 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800779 '''
780 self.reap_completed_tests()
781 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800782 if root and not test.has_ancestor(root):
783 continue
784
Jon Salz0697cbf2012-07-04 15:14:04 +0800785 factory.console.info('Killing active test %s...' % test.path)
786 invoc.abort_and_join()
787 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800788 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800789 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800790
Jon Salz0697cbf2012-07-04 15:14:04 +0800791 if not abort:
792 test.update_state(status=TestState.UNTESTED)
793 self.reap_completed_tests()
794
Jon Salz85a39882012-07-05 16:45:04 +0800795 def stop(self, root=None, fail=False):
796 self.kill_active_tests(fail, root)
797 # Remove any tests in the run queue under the root.
798 self.tests_to_run = deque([x for x in self.tests_to_run
799 if root and not x.has_ancestor(root)])
800 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800801
802 def abort_active_tests(self):
803 self.kill_active_tests(True)
804
805 def main(self):
806 try:
807 self.init()
808 self.event_log.Log('goofy_init',
809 success=True)
810 except:
811 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800812 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800813 self.event_log.Log('goofy_init',
814 success=False,
815 trace=traceback.format_exc())
816 except: # pylint: disable=W0702
817 pass
818 raise
819
820 self.run()
821
822 def update_system_info(self):
823 '''Updates system info.'''
824 system_info = system.SystemInfo()
825 self.state_instance.set_shared_data('system_info', system_info.__dict__)
826 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
827 system_info=system_info.__dict__))
828 logging.info('System info: %r', system_info.__dict__)
829
Jon Salzeb42f0d2012-07-27 19:14:04 +0800830 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
831 '''Commences updating factory software.
832
833 Args:
834 auto_run_on_restart: Auto-run when the machine comes back up.
835 post_update_hook: Code to call after update but immediately before
836 restart.
837
838 Returns:
839 Never if the update was successful (we just reboot).
840 False if the update was unnecessary (no update available).
841 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800842 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800843 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800844
Jon Salz5c344f62012-07-13 14:31:16 +0800845 def pre_update_hook():
846 if auto_run_on_restart:
847 self.state_instance.set_shared_data('tests_after_shutdown',
848 FORCE_AUTO_RUN)
849 self.state_instance.close()
850
Jon Salzeb42f0d2012-07-27 19:14:04 +0800851 if updater.TryUpdate(pre_update_hook=pre_update_hook):
852 if post_update_hook:
853 post_update_hook()
854 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800855
Jon Salz77c151e2012-08-28 07:20:37 +0800856 def handle_sigint(self, signum, frame):
857 logging.error('Received SIGINT')
858 self.run_queue.put(None)
859 raise KeyboardInterrupt()
860
Jon Salz0697cbf2012-07-04 15:14:04 +0800861 def init(self, args=None, env=None):
862 '''Initializes Goofy.
863
864 Args:
865 args: A list of command-line arguments. Uses sys.argv if
866 args is None.
867 env: An Environment instance to use (or None to choose
868 FakeChrootEnvironment or DUTEnvironment as appropriate).
869 '''
Jon Salz77c151e2012-08-28 07:20:37 +0800870 signal.signal(signal.SIGINT, self.handle_sigint)
871
Jon Salz0697cbf2012-07-04 15:14:04 +0800872 parser = OptionParser()
873 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800874 action='store_true',
875 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800876 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800877 metavar='FILE',
878 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800879 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800880 action='store_true',
881 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800882 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800883 choices=['none', 'gtk', 'chrome'],
884 default=('chrome' if utils.in_chroot() else 'gtk'),
885 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800886 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800887 type='int', default=1,
888 help=('Factor by which to scale UI '
889 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800890 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800891 metavar='FILE',
892 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800893 (self.options, self.args) = parser.parse_args(args)
894
Jon Salz46b89562012-07-05 11:49:22 +0800895 # Make sure factory directories exist.
896 factory.get_log_root()
897 factory.get_state_root()
898 factory.get_test_data_root()
899
Jon Salz0697cbf2012-07-04 15:14:04 +0800900 global _inited_logging # pylint: disable=W0603
901 if not _inited_logging:
902 factory.init_logging('goofy', verbose=self.options.verbose)
903 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800904
Jon Salzee85d522012-07-17 14:34:46 +0800905 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +0800906 self.event_log = EventLog('goofy')
907
908 if (not suppress_chroot_warning and
909 factory.in_chroot() and
910 self.options.ui == 'gtk' and
911 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
912 # That's not going to work! Tell the user how to run
913 # this way.
914 logging.warn(GOOFY_IN_CHROOT_WARNING)
915 time.sleep(1)
916
917 if env:
918 self.env = env
919 elif factory.in_chroot():
920 self.env = test_environment.FakeChrootEnvironment()
921 logging.warn(
922 'Using chroot environment: will not actually run autotests')
923 else:
924 self.env = test_environment.DUTEnvironment()
925 self.env.goofy = self
926
927 if self.options.restart:
928 state.clear_state()
929
930 if self.options.print_test_list:
Jon Salzeb42f0d2012-07-27 19:14:04 +0800931 print factory.read_test_list(
932 self.options.print_test_list).__repr__(recursive=True)
Jon Salz0697cbf2012-07-04 15:14:04 +0800933 return
934
935 if self.options.ui_scale_factor != 1 and utils.in_qemu():
936 logging.warn(
937 'In QEMU; ignoring ui_scale_factor argument')
938 self.options.ui_scale_factor = 1
939
940 logging.info('Started')
941
942 self.start_state_server()
943 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
944 self.state_instance.set_shared_data('ui_scale_factor',
945 self.options.ui_scale_factor)
946 self.last_shutdown_time = (
947 self.state_instance.get_shared_data('shutdown_time', optional=True))
948 self.state_instance.del_shared_data('shutdown_time', optional=True)
949
950 if not self.options.test_list:
951 self.options.test_list = find_test_list()
952 if not self.options.test_list:
953 logging.error('No test list. Aborting.')
954 sys.exit(1)
955 logging.info('Using test list %s', self.options.test_list)
956
957 self.test_list = factory.read_test_list(
958 self.options.test_list,
Jon Salzeb42f0d2012-07-27 19:14:04 +0800959 self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800960 if not self.state_instance.has_shared_data('ui_lang'):
961 self.state_instance.set_shared_data('ui_lang',
962 self.test_list.options.ui_lang)
963 self.state_instance.set_shared_data(
964 'test_list_options',
965 self.test_list.options.__dict__)
966 self.state_instance.test_list = self.test_list
967
Jon Salz8fa8e832012-07-13 19:04:09 +0800968 if self.test_list.options.time_sanitizer:
969 self.time_sanitizer = time_sanitizer.TimeSanitizer(
970 base_time=time_sanitizer.GetBaseTimeFromFile(
971 # lsb-factory is written by the factory install shim during
972 # installation, so it should have a good time obtained from
973 # the mini-Omaha server.
974 '/usr/local/etc/lsb-factory'))
975 self.time_sanitizer.RunOnce()
976
Jon Salz0697cbf2012-07-04 15:14:04 +0800977 self.init_states()
978 self.start_event_server()
979 self.connection_manager = self.env.create_connection_manager(
980 self.test_list.options.wlans)
981 # Note that we create a log watcher even if
982 # sync_event_log_period_secs isn't set (no background
983 # syncing), since we may use it to flush event logs as well.
984 self.log_watcher = EventLogWatcher(
985 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +0800986 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +0800987 if self.test_list.options.sync_event_log_period_secs:
988 self.log_watcher.StartWatchThread()
989
990 self.update_system_info()
991
Vic Yang4953fc12012-07-26 16:19:53 +0800992 assert ((self.test_list.options.min_charge_pct is None) ==
993 (self.test_list.options.max_charge_pct is None))
994 if (self.test_list.options.min_charge_pct and
995 self.test_list.options.max_charge_pct):
996 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
997 self.test_list.options.max_charge_pct)
998
Jon Salz0697cbf2012-07-04 15:14:04 +0800999 os.environ['CROS_FACTORY'] = '1'
1000 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1001
1002 # Set CROS_UI since some behaviors in ui.py depend on the
1003 # particular UI in use. TODO(jsalz): Remove this (and all
1004 # places it is used) when the GTK UI is removed.
1005 os.environ['CROS_UI'] = self.options.ui
1006
1007 if self.options.ui == 'chrome':
1008 self.env.launch_chrome()
1009 logging.info('Waiting for a web socket connection')
1010 self.web_socket_manager.wait()
1011
1012 # Wait for the test widget size to be set; this is done in
1013 # an asynchronous RPC so there is a small chance that the
1014 # web socket might be opened first.
1015 for _ in range(100): # 10 s
1016 try:
1017 if self.state_instance.get_shared_data('test_widget_size'):
1018 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001019 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001020 pass # Retry
1021 time.sleep(0.1) # 100 ms
1022 else:
1023 logging.warn('Never received test_widget_size from UI')
1024 elif self.options.ui == 'gtk':
1025 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001026
Jon Salz0697cbf2012-07-04 15:14:04 +08001027 def state_change_callback(test, test_state):
1028 self.event_client.post_event(
1029 Event(Event.Type.STATE_CHANGE,
1030 path=test.path, state=test_state))
1031 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001032
Jon Salza6711d72012-07-18 14:33:03 +08001033 for handler in self.on_ui_startup:
1034 handler()
1035
1036 self.prespawner = Prespawner()
1037 self.prespawner.start()
1038
Jon Salz0697cbf2012-07-04 15:14:04 +08001039 try:
1040 tests_after_shutdown = self.state_instance.get_shared_data(
1041 'tests_after_shutdown')
1042 except KeyError:
1043 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001044
Jon Salz5c344f62012-07-13 14:31:16 +08001045 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1046 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001047 logging.info('Resuming tests after shutdown: %s',
1048 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001049 self.tests_to_run.extend(
1050 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1051 self.run_queue.put(self.run_next_test)
1052 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001053 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001054 self.run_queue.put(
1055 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001056 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001057
Jon Salz0697cbf2012-07-04 15:14:04 +08001058 def run(self):
1059 '''Runs Goofy.'''
1060 # Process events forever.
1061 while self.run_once(True):
1062 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001063
Jon Salz0697cbf2012-07-04 15:14:04 +08001064 def run_once(self, block=False):
1065 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001066
Jon Salz0697cbf2012-07-04 15:14:04 +08001067 Args:
1068 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001069
Jon Salz0697cbf2012-07-04 15:14:04 +08001070 Returns:
1071 True to keep going or False to shut down.
1072 '''
1073 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001074 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001075 # Nothing on the run queue.
1076 self._run_queue_idle()
1077 if block:
1078 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001079 try:
1080 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1081 except Queue.Empty:
1082 # Keep going (calling _run_queue_idle() again at the top of
1083 # the loop)
1084 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001085 # ...and grab anything else that showed up at the same
1086 # time.
1087 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001088 else:
1089 break
Jon Salz51528e12012-07-02 18:54:45 +08001090
Jon Salz0697cbf2012-07-04 15:14:04 +08001091 for event in events:
1092 if not event:
1093 # Shutdown request.
1094 self.run_queue.task_done()
1095 return False
Jon Salz51528e12012-07-02 18:54:45 +08001096
Jon Salz0697cbf2012-07-04 15:14:04 +08001097 try:
1098 event()
Jon Salz85a39882012-07-05 16:45:04 +08001099 except: # pylint: disable=W0702
1100 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001101 self.record_exception(traceback.format_exception_only(
1102 *sys.exc_info()[:2]))
1103 # But keep going
1104 finally:
1105 self.run_queue.task_done()
1106 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001107
Jon Salz8fa8e832012-07-13 19:04:09 +08001108 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001109 '''Writes out current time and tries to sync with shopfloor server.'''
1110 if not self.time_sanitizer:
1111 return
1112
1113 # Write out the current time.
1114 self.time_sanitizer.SaveTime()
1115
Jon Salz8fa8e832012-07-13 19:04:09 +08001116 if ((not self.test_list.options.sync_time_period_secs) or
1117 (not self.time_sanitizer) or
1118 self.time_synced or
1119 factory.in_chroot()):
1120 # Not enabled or already succeeded.
1121 return
1122
1123 now = time.time()
1124 if self.last_sync_time and (
1125 now - self.last_sync_time <
1126 self.test_list.options.sync_time_period_secs):
1127 # Not yet time for another check.
1128 return
1129 self.last_sync_time = now
1130
1131 def target():
1132 try:
1133 self.time_sanitizer.SyncWithShopfloor()
1134 self.time_synced = True
1135 except: # pylint: disable=W0702
1136 # Oh well. Log an error (but no trace)
1137 logging.info(
1138 'Unable to get time from shopfloor server: %s',
1139 utils.FormatExceptionOnly())
1140
1141 thread = threading.Thread(target=target)
1142 thread.daemon = True
1143 thread.start()
1144
Jon Salz0697cbf2012-07-04 15:14:04 +08001145 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001146 '''Invoked when the run queue has no events.
1147
1148 This method must not raise exception.
1149 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001150 now = time.time()
1151 if (self.last_idle and
1152 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1153 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1154 # 1) seconds.
1155 return
1156
1157 self.last_idle = now
1158
Jon Salz0697cbf2012-07-04 15:14:04 +08001159 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001160 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001161 self.sync_time_in_background()
Vic Yang4953fc12012-07-26 16:19:53 +08001162 if self.charge_manager:
1163 self.charge_manager.AdjustChargeState()
Jon Salz57717ca2012-04-04 16:47:25 +08001164
Jon Salz16d10542012-07-23 12:18:45 +08001165 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001166 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001167
Jon Salz0697cbf2012-07-04 15:14:04 +08001168 Attempts to upload the event logs to the shopfloor server.
1169 '''
1170 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1171 start_time = time.time()
1172 logging.info('Syncing %s', description)
1173 shopfloor_client = shopfloor.get_instance(
1174 detect=True,
1175 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzb10cf512012-08-09 17:29:21 +08001176 shopfloor_client.UploadEvent(log_name, Binary(chunk))
Jon Salz0697cbf2012-07-04 15:14:04 +08001177 logging.info(
1178 'Successfully synced %s in %.03f s',
1179 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001180
Jon Salz0697cbf2012-07-04 15:14:04 +08001181 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1182 root=None):
1183 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001184
Jon Salz0697cbf2012-07-04 15:14:04 +08001185 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001186
Jon Salz0697cbf2012-07-04 15:14:04 +08001187 Args:
1188 starting_at: If provided, only auto-runs tests beginning with
1189 this test.
1190 '''
1191 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001192
Jon Salz0697cbf2012-07-04 15:14:04 +08001193 if starting_at:
1194 # Make sure they passed a test, not a string.
1195 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001196
Jon Salz0697cbf2012-07-04 15:14:04 +08001197 tests_to_reset = []
1198 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001199
Jon Salz0697cbf2012-07-04 15:14:04 +08001200 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001201
Jon Salz0697cbf2012-07-04 15:14:04 +08001202 for test in root.get_top_level_tests():
1203 if starting_at:
1204 if test == starting_at:
1205 # We've found starting_at; do auto-run on all
1206 # subsequent tests.
1207 found_starting_at = True
1208 if not found_starting_at:
1209 # Don't start this guy yet
1210 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001211
Jon Salz0697cbf2012-07-04 15:14:04 +08001212 status = test.get_state().status
1213 if status == TestState.ACTIVE or status in statuses_to_run:
1214 # Reset the test (later; we will need to abort
1215 # all active tests first).
1216 tests_to_reset.append(test)
1217 if status in statuses_to_run:
1218 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001219
Jon Salz0697cbf2012-07-04 15:14:04 +08001220 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001221
Jon Salz0697cbf2012-07-04 15:14:04 +08001222 # Reset all statuses of the tests to run (in case any tests were active;
1223 # we want them to be run again).
1224 for test_to_reset in tests_to_reset:
1225 for test in test_to_reset.walk():
1226 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001227
Jon Salz0697cbf2012-07-04 15:14:04 +08001228 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001229
Jon Salz0697cbf2012-07-04 15:14:04 +08001230 def restart_tests(self, root=None):
1231 '''Restarts all tests.'''
1232 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001233
Jon Salz0697cbf2012-07-04 15:14:04 +08001234 self.abort_active_tests()
1235 for test in root.walk():
1236 test.update_state(status=TestState.UNTESTED)
1237 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001238
Jon Salz0697cbf2012-07-04 15:14:04 +08001239 def auto_run(self, starting_at=None, root=None):
1240 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001241
Jon Salz0697cbf2012-07-04 15:14:04 +08001242 Args:
1243 starting_at: If provide, only auto-runs tests beginning with
1244 this test.
1245 '''
1246 root = root or self.test_list
1247 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1248 starting_at=starting_at,
1249 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001250
Jon Salz0697cbf2012-07-04 15:14:04 +08001251 def re_run_failed(self, root=None):
1252 '''Re-runs failed tests.'''
1253 root = root or self.test_list
1254 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001255
Jon Salz0697cbf2012-07-04 15:14:04 +08001256 def show_review_information(self):
1257 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001258
Jon Salz0697cbf2012-07-04 15:14:04 +08001259 The information screene is rendered by main UI program (ui.py), so in
1260 goofy we only need to kill all active tests, set them as untested, and
1261 clear remaining tests.
1262 '''
1263 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001264 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001265
Jon Salz0697cbf2012-07-04 15:14:04 +08001266 def handle_switch_test(self, event):
1267 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001268
Jon Salz0697cbf2012-07-04 15:14:04 +08001269 @param event: The SWITCH_TEST event.
1270 '''
1271 test = self.test_list.lookup_path(event.path)
1272 if not test:
1273 logging.error('Unknown test %r', event.key)
1274 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001275
Jon Salz0697cbf2012-07-04 15:14:04 +08001276 invoc = self.invocations.get(test)
1277 if invoc and test.backgroundable:
1278 # Already running: just bring to the front if it
1279 # has a UI.
1280 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001281 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001282 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001283
Jon Salz0697cbf2012-07-04 15:14:04 +08001284 self.abort_active_tests()
1285 for t in test.walk():
1286 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001287
Jon Salz0697cbf2012-07-04 15:14:04 +08001288 if self.test_list.options.auto_run_on_keypress:
1289 self.auto_run(starting_at=test)
1290 else:
1291 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001292
Jon Salz0697cbf2012-07-04 15:14:04 +08001293 def wait(self):
1294 '''Waits for all pending invocations.
1295
1296 Useful for testing.
1297 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001298 while self.invocations:
1299 for k, v in self.invocations.iteritems():
1300 logging.info('Waiting for %s to complete...', k)
1301 v.thread.join()
1302 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001303
1304 def check_exceptions(self):
1305 '''Raises an error if any exceptions have occurred in
1306 invocation threads.'''
1307 if self.exceptions:
1308 raise RuntimeError('Exception in invocation thread: %r' %
1309 self.exceptions)
1310
1311 def record_exception(self, msg):
1312 '''Records an exception in an invocation thread.
1313
1314 An exception with the given message will be rethrown when
1315 Goofy is destroyed.'''
1316 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001317
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001318
1319if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001320 goofy = Goofy()
1321 try:
1322 goofy.main()
1323 finally:
1324 goofy.destroy()