blob: 54df8cac2474799ba464ed61dc094f09373b375d [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 Salz0405ab52012-03-16 15:26:52 +080015import sys
Jon Salz0405ab52012-03-16 15:26:52 +080016import threading
17import time
18import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080019import uuid
Hung-Te Linf2f78f72012-02-08 19:27:11 +080020from collections import deque
21from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080022
Jon Salz0697cbf2012-07-04 15:14:04 +080023import factory_common # pylint: disable=W0611
Jon Salz83591782012-06-26 11:09:58 +080024from cros.factory.goofy.prespawner import Prespawner
25from cros.factory.test import factory
26from cros.factory.test import state
27from cros.factory.test.factory import TestState
28from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080029from cros.factory.goofy import test_steps
30from cros.factory.goofy.event_log_watcher import EventLogWatcher
31from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080032from cros.factory.test import utils
33from cros.factory.test.event import Event
34from cros.factory.test.event import EventClient
35from cros.factory.test.event import EventServer
Jon Salzee85d522012-07-17 14:34:46 +080036from cros.factory import event_log
Jon Salz83591782012-06-26 11:09:58 +080037from cros.factory.event_log import EventLog
38from cros.factory.goofy.invocation import TestInvocation
Jon Salz16d10542012-07-23 12:18:45 +080039from cros.factory.goofy.goofy_rpc import GoofyRPC
Jon Salz83591782012-06-26 11:09:58 +080040from cros.factory.goofy import system
41from cros.factory.goofy import test_environment
Jon Salz8fa8e832012-07-13 19:04:09 +080042from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080043from cros.factory.goofy.web_socket_manager import WebSocketManager
Jon Salz78c32392012-07-25 14:18:29 +080044from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080045
46
Jon Salz2f757d42012-06-27 17:06:42 +080047DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
48CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080049HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
50
Jon Salz8796e362012-05-24 11:39:09 +080051# File that suppresses reboot if present (e.g., for development).
52NO_REBOOT_FILE = '/var/log/factory.noreboot'
53
Jon Salz5c344f62012-07-13 14:31:16 +080054# Value for tests_after_shutdown that forces auto-run (e.g., after
55# a factory update, when the available set of tests might change).
56FORCE_AUTO_RUN = 'force_auto_run'
57
cychiang21886742012-07-05 15:16:32 +080058RUN_QUEUE_TIMEOUT_SECS = 10
59
Jon Salz758e6cc2012-04-03 15:47:07 +080060GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
61You are running Goofy inside the chroot. Autotests are not supported.
62
63To use Goofy in the chroot, first install an Xvnc server:
64
Jon Salz0697cbf2012-07-04 15:14:04 +080065 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080066
67...and then start a VNC X server outside the chroot:
68
Jon Salz0697cbf2012-07-04 15:14:04 +080069 vncserver :10 &
70 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080071
72...and run Goofy as follows:
73
Jon Salz0697cbf2012-07-04 15:14:04 +080074 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080075''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080076suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080077
78def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080079 '''
80 Returns the HWID config tag, or an empty string if none can be found.
81 '''
82 if 'CROS_HWID' in os.environ:
83 return os.environ['CROS_HWID']
84 if os.path.exists(HWID_CFG_PATH):
85 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
86 return hwid_cfg_handle.read().strip()
87 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080088
89
90def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080091 '''
92 Returns the path to the active test list, based on the HWID config tag.
93 '''
94 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +080095
Jon Salz0697cbf2012-07-04 15:14:04 +080096 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
Jon Salz2f757d42012-06-27 17:06:42 +080097
Jon Salz0697cbf2012-07-04 15:14:04 +080098 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
99 search_files = ['test_list', 'test_list.all']
100 if hwid_cfg:
101 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800102
Jon Salz0697cbf2012-07-04 15:14:04 +0800103 for d in search_dirs:
104 for f in search_files:
105 test_list = os.path.join(d, f)
106 if os.path.exists(test_list):
107 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800108
Jon Salz0697cbf2012-07-04 15:14:04 +0800109 logging.warn('Cannot find test lists named any of %s in any of %s',
110 search_files, search_dirs)
111 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800112
Jon Salz73e0fd02012-04-04 11:46:38 +0800113_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800114
115class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800116 '''
117 The main factory flow.
118
119 Note that all methods in this class must be invoked from the main
120 (event) thread. Other threads, such as callbacks and TestInvocation
121 methods, should instead post events on the run queue.
122
123 TODO: Unit tests. (chrome-os-partner:7409)
124
125 Properties:
126 uuid: A unique UUID for this invocation of Goofy.
127 state_instance: An instance of FactoryState.
128 state_server: The FactoryState XML/RPC server.
129 state_server_thread: A thread running state_server.
130 event_server: The EventServer socket server.
131 event_server_thread: A thread running event_server.
132 event_client: A client to the event server.
133 connection_manager: The connection_manager object.
134 network_enabled: Whether the connection_manager is currently
135 enabling connections.
136 ui_process: The factory ui process object.
137 run_queue: A queue of callbacks to invoke from the main thread.
138 invocations: A map from FactoryTest objects to the corresponding
139 TestInvocations objects representing active tests.
140 tests_to_run: A deque of tests that should be run when the current
141 test(s) complete.
142 options: Command-line options.
143 args: Command-line args.
144 test_list: The test list.
145 event_handlers: Map of Event.Type to the method used to handle that
146 event. If the method has an 'event' argument, the event is passed
147 to the handler.
148 exceptions: Exceptions encountered in invocation threads.
149 '''
150 def __init__(self):
151 self.uuid = str(uuid.uuid4())
152 self.state_instance = None
153 self.state_server = None
154 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800155 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800156 self.event_server = None
157 self.event_server_thread = None
158 self.event_client = None
159 self.connection_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800160 self.time_sanitizer = None
161 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800162 self.log_watcher = None
163 self.network_enabled = True
164 self.event_log = None
165 self.prespawner = None
166 self.ui_process = None
167 self.run_queue = Queue.Queue()
168 self.invocations = {}
169 self.tests_to_run = deque()
170 self.visible_test = None
171 self.chrome = None
172
173 self.options = None
174 self.args = None
175 self.test_list = None
176 self.on_ui_startup = []
177 self.env = None
178 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800179 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800180 self.last_sync_time = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800181
Jon Salz85a39882012-07-05 16:45:04 +0800182 def test_or_root(event, parent_or_group=True):
183 '''Returns the test affected by a particular event.
184
185 Args:
186 event: The event containing an optional 'path' attribute.
187 parent_on_group: If True, returns the top-level parent for a test (the
188 root node of the tests that need to be run together if the given test
189 path is to be run).
190 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800191 try:
192 path = event.path
193 except AttributeError:
194 path = None
195
196 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800197 test = self.test_list.lookup_path(path)
198 if parent_or_group:
199 test = test.get_top_level_parent_or_group()
200 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800201 else:
202 return self.test_list
203
204 self.event_handlers = {
205 Event.Type.SWITCH_TEST: self.handle_switch_test,
206 Event.Type.SHOW_NEXT_ACTIVE_TEST:
207 lambda event: self.show_next_active_test(),
208 Event.Type.RESTART_TESTS:
209 lambda event: self.restart_tests(root=test_or_root(event)),
210 Event.Type.AUTO_RUN:
211 lambda event: self.auto_run(root=test_or_root(event)),
212 Event.Type.RE_RUN_FAILED:
213 lambda event: self.re_run_failed(root=test_or_root(event)),
214 Event.Type.RUN_TESTS_WITH_STATUS:
215 lambda event: self.run_tests_with_status(
216 event.status,
217 root=test_or_root(event)),
218 Event.Type.REVIEW:
219 lambda event: self.show_review_information(),
220 Event.Type.UPDATE_SYSTEM_INFO:
221 lambda event: self.update_system_info(),
222 Event.Type.UPDATE_FACTORY:
223 lambda event: self.update_factory(),
224 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800225 lambda event: self.stop(root=test_or_root(event, False),
226 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800227 Event.Type.SET_VISIBLE_TEST:
228 lambda event: self.set_visible_test(
229 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800230 }
231
232 self.exceptions = []
233 self.web_socket_manager = None
234
235 def destroy(self):
236 if self.chrome:
237 self.chrome.kill()
238 self.chrome = None
239 if self.ui_process:
240 utils.kill_process_tree(self.ui_process, 'ui')
241 self.ui_process = None
242 if self.web_socket_manager:
243 logging.info('Stopping web sockets')
244 self.web_socket_manager.close()
245 self.web_socket_manager = None
246 if self.state_server_thread:
247 logging.info('Stopping state server')
248 self.state_server.shutdown()
249 self.state_server_thread.join()
250 self.state_server.server_close()
251 self.state_server_thread = None
252 if self.state_instance:
253 self.state_instance.close()
254 if self.event_server_thread:
255 logging.info('Stopping event server')
256 self.event_server.shutdown() # pylint: disable=E1101
257 self.event_server_thread.join()
258 self.event_server.server_close()
259 self.event_server_thread = None
260 if self.log_watcher:
261 if self.log_watcher.IsThreadStarted():
262 self.log_watcher.StopWatchThread()
263 self.log_watcher = None
264 if self.prespawner:
265 logging.info('Stopping prespawner')
266 self.prespawner.stop()
267 self.prespawner = None
268 if self.event_client:
269 logging.info('Closing event client')
270 self.event_client.close()
271 self.event_client = None
272 if self.event_log:
273 self.event_log.Close()
274 self.event_log = None
275 self.check_exceptions()
276 logging.info('Done destroying Goofy')
277
278 def start_state_server(self):
279 self.state_instance, self.state_server = (
280 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800281 self.goofy_rpc = GoofyRPC(self)
282 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800283 logging.info('Starting state server')
284 self.state_server_thread = threading.Thread(
285 target=self.state_server.serve_forever,
286 name='StateServer')
287 self.state_server_thread.start()
288
289 def start_event_server(self):
290 self.event_server = EventServer()
291 logging.info('Starting factory event server')
292 self.event_server_thread = threading.Thread(
293 target=self.event_server.serve_forever,
294 name='EventServer') # pylint: disable=E1101
295 self.event_server_thread.start()
296
297 self.event_client = EventClient(
298 callback=self.handle_event, event_loop=self.run_queue)
299
300 self.web_socket_manager = WebSocketManager(self.uuid)
301 self.state_server.add_handler("/event",
302 self.web_socket_manager.handle_web_socket)
303
304 def start_ui(self):
305 ui_proc_args = [
306 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
307 self.options.test_list]
308 if self.options.verbose:
309 ui_proc_args.append('-v')
310 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800311 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800312 logging.info('Waiting for UI to come up...')
313 self.event_client.wait(
314 lambda event: event.type == Event.Type.UI_READY)
315 logging.info('UI has started')
316
317 def set_visible_test(self, test):
318 if self.visible_test == test:
319 return
320
321 if test:
322 test.update_state(visible=True)
323 if self.visible_test:
324 self.visible_test.update_state(visible=False)
325 self.visible_test = test
326
327 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800328 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800329 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800330
Jon Salz0697cbf2012-07-04 15:14:04 +0800331 @param test: The ShutdownStep.
332 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800333 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800334 test_state = test.update_state(increment_shutdown_count=1)
335 logging.info('Detected shutdown (%d of %d)',
336 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800337
Jon Salz0697cbf2012-07-04 15:14:04 +0800338 def log_and_update_state(status, error_msg, **kw):
339 self.event_log.Log('rebooted',
340 status=status, error_msg=error_msg, **kw)
341 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800342
Jon Salz0697cbf2012-07-04 15:14:04 +0800343 if not self.last_shutdown_time:
344 log_and_update_state(status=TestState.FAILED,
345 error_msg='Unable to read shutdown_time')
346 return
Jon Salz258a40c2012-04-19 12:34:01 +0800347
Jon Salz0697cbf2012-07-04 15:14:04 +0800348 now = time.time()
349 logging.info('%.03f s passed since reboot',
350 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800351
Jon Salz0697cbf2012-07-04 15:14:04 +0800352 if self.last_shutdown_time > now:
353 test.update_state(status=TestState.FAILED,
354 error_msg='Time moved backward during reboot')
355 elif (isinstance(test, factory.RebootStep) and
356 self.test_list.options.max_reboot_time_secs and
357 (now - self.last_shutdown_time >
358 self.test_list.options.max_reboot_time_secs)):
359 # A reboot took too long; fail. (We don't check this for
360 # HaltSteps, because the machine could be halted for a
361 # very long time, and even unplugged with battery backup,
362 # thus hosing the clock.)
363 log_and_update_state(
364 status=TestState.FAILED,
365 error_msg=('More than %d s elapsed during reboot '
366 '(%.03f s, from %s to %s)' % (
367 self.test_list.options.max_reboot_time_secs,
368 now - self.last_shutdown_time,
369 utils.TimeString(self.last_shutdown_time),
370 utils.TimeString(now))),
371 duration=(now-self.last_shutdown_time))
372 elif test_state.shutdown_count == test.iterations:
373 # Good!
374 log_and_update_state(status=TestState.PASSED,
375 duration=(now - self.last_shutdown_time),
376 error_msg='')
377 elif test_state.shutdown_count > test.iterations:
378 # Shut down too many times
379 log_and_update_state(status=TestState.FAILED,
380 error_msg='Too many shutdowns')
381 elif utils.are_shift_keys_depressed():
382 logging.info('Shift keys are depressed; cancelling restarts')
383 # Abort shutdown
384 log_and_update_state(
385 status=TestState.FAILED,
386 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800387 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800388 else:
389 def handler():
390 if self._prompt_cancel_shutdown(
391 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800392 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800393 log_and_update_state(
394 status=TestState.FAILED,
395 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800396 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800397 return
Jon Salz0405ab52012-03-16 15:26:52 +0800398
Jon Salz0697cbf2012-07-04 15:14:04 +0800399 # Time to shutdown again
400 log_and_update_state(
401 status=TestState.ACTIVE,
402 error_msg='',
403 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800404
Jon Salz0697cbf2012-07-04 15:14:04 +0800405 self.event_log.Log('shutdown', operation='reboot')
406 self.state_instance.set_shared_data('shutdown_time',
407 time.time())
408 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800409
Jon Salz0697cbf2012-07-04 15:14:04 +0800410 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800411
Jon Salz0697cbf2012-07-04 15:14:04 +0800412 def _prompt_cancel_shutdown(self, test, iteration):
413 if self.options.ui != 'chrome':
414 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800415
Jon Salz0697cbf2012-07-04 15:14:04 +0800416 pending_shutdown_data = {
417 'delay_secs': test.delay_secs,
418 'time': time.time() + test.delay_secs,
419 'operation': test.operation,
420 'iteration': iteration,
421 'iterations': test.iterations,
422 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800423
Jon Salz0697cbf2012-07-04 15:14:04 +0800424 # Create a new (threaded) event client since we
425 # don't want to use the event loop for this.
426 with EventClient() as event_client:
427 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
428 **pending_shutdown_data))
429 aborted = event_client.wait(
430 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
431 timeout=test.delay_secs) is not None
432 if aborted:
433 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
434 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800435
Jon Salz0697cbf2012-07-04 15:14:04 +0800436 def init_states(self):
437 '''
438 Initializes all states on startup.
439 '''
440 for test in self.test_list.get_all_tests():
441 # Make sure the state server knows about all the tests,
442 # defaulting to an untested state.
443 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800444
Jon Salz0697cbf2012-07-04 15:14:04 +0800445 var_log_messages = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800446
Jon Salz0697cbf2012-07-04 15:14:04 +0800447 # Any 'active' tests should be marked as failed now.
448 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800449 if not test.is_leaf():
450 # Don't bother with parents; they will be updated when their
451 # children are updated.
452 continue
453
Jon Salz0697cbf2012-07-04 15:14:04 +0800454 test_state = test.get_state()
455 if test_state.status != TestState.ACTIVE:
456 continue
457 if isinstance(test, factory.ShutdownStep):
458 # Shutdown while the test was active - that's good.
459 self.handle_shutdown_complete(test, test_state)
460 else:
461 # Unexpected shutdown. Grab /var/log/messages for context.
462 if var_log_messages is None:
463 try:
464 var_log_messages = (
465 utils.var_log_messages_before_reboot())
466 # Write it to the log, to make it easier to
467 # correlate with /var/log/messages.
468 logging.info(
469 'Unexpected shutdown. '
470 'Tail of /var/log/messages before last reboot:\n'
471 '%s', ('\n'.join(
472 ' ' + x for x in var_log_messages)))
473 except: # pylint: disable=W0702
474 logging.exception('Unable to grok /var/log/messages')
475 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800476
Jon Salz0697cbf2012-07-04 15:14:04 +0800477 error_msg = 'Unexpected shutdown while test was running'
478 self.event_log.Log('end_test',
479 path=test.path,
480 status=TestState.FAILED,
481 invocation=test.get_state().invocation,
482 error_msg=error_msg,
483 var_log_messages='\n'.join(var_log_messages))
484 test.update_state(
485 status=TestState.FAILED,
486 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800487
Jon Salz50efe942012-07-26 11:54:10 +0800488 if not test.never_fails:
489 # For "never_fails" tests (such as "Start"), don't cancel
490 # pending tests, since reboot is expected.
491 factory.console.info('Unexpected shutdown while test %s '
492 'running; cancelling any pending tests',
493 test.path)
494 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800495
Jon Salz0697cbf2012-07-04 15:14:04 +0800496 def show_next_active_test(self):
497 '''
498 Rotates to the next visible active test.
499 '''
500 self.reap_completed_tests()
501 active_tests = [
502 t for t in self.test_list.walk()
503 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
504 if not active_tests:
505 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800506
Jon Salz0697cbf2012-07-04 15:14:04 +0800507 try:
508 next_test = active_tests[
509 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
510 except ValueError: # visible_test not present in active_tests
511 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800512
Jon Salz0697cbf2012-07-04 15:14:04 +0800513 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800514
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 def handle_event(self, event):
516 '''
517 Handles an event from the event server.
518 '''
519 handler = self.event_handlers.get(event.type)
520 if handler:
521 handler(event)
522 else:
523 # We don't register handlers for all event types - just ignore
524 # this event.
525 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800526
Jon Salz0697cbf2012-07-04 15:14:04 +0800527 def run_next_test(self):
528 '''
529 Runs the next eligible test (or tests) in self.tests_to_run.
530 '''
531 self.reap_completed_tests()
532 while self.tests_to_run:
533 logging.debug('Tests to run: %s',
534 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800535
Jon Salz0697cbf2012-07-04 15:14:04 +0800536 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800537
Jon Salz0697cbf2012-07-04 15:14:04 +0800538 if test in self.invocations:
539 logging.info('Next test %s is already running', test.path)
540 self.tests_to_run.popleft()
541 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800542
Jon Salza1412922012-07-23 16:04:17 +0800543 for requirement in test.require_run:
544 for i in requirement.test.walk():
545 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800546 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800547 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800548 return
549
Jon Salz0697cbf2012-07-04 15:14:04 +0800550 if self.invocations and not (test.backgroundable and all(
551 [x.backgroundable for x in self.invocations])):
552 logging.debug('Waiting for non-backgroundable tests to '
553 'complete before running %s', test.path)
554 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800555
Jon Salz0697cbf2012-07-04 15:14:04 +0800556 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800557
Jon Salz304a75d2012-07-06 11:14:15 +0800558 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800559 for requirement in test.require_run:
560 for i in requirement.test.walk():
561 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800562 # We've hit this test itself; stop checking
563 break
Jon Salza1412922012-07-23 16:04:17 +0800564 if ((i.get_state().status == TestState.UNTESTED) or
565 (requirement.passed and i.get_state().status !=
566 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800567 # Found an untested test; move on to the next
568 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800569 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800570 break
571
572 if untested:
573 untested_paths = ', '.join(sorted([x.path for x in untested]))
574 if self.state_instance.get_shared_data('engineering_mode',
575 optional=True):
576 # In engineering mode, we'll let it go.
577 factory.console.warn('In engineering mode; running '
578 '%s even though required tests '
579 '[%s] have not completed',
580 test.path, untested_paths)
581 else:
582 # Not in engineering mode; mark it failed.
583 error_msg = ('Required tests [%s] have not been run yet'
584 % untested_paths)
585 factory.console.error('Not running %s: %s',
586 test.path, error_msg)
587 test.update_state(status=TestState.FAILED,
588 error_msg=error_msg)
589 continue
590
Jon Salz0697cbf2012-07-04 15:14:04 +0800591 if isinstance(test, factory.ShutdownStep):
592 if os.path.exists(NO_REBOOT_FILE):
593 test.update_state(
594 status=TestState.FAILED, increment_count=1,
595 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800596 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800597 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800598
Jon Salz0697cbf2012-07-04 15:14:04 +0800599 test.update_state(status=TestState.ACTIVE, increment_count=1,
600 error_msg='', shutdown_count=0)
601 if self._prompt_cancel_shutdown(test, 1):
602 self.event_log.Log('reboot_cancelled')
603 test.update_state(
604 status=TestState.FAILED, increment_count=1,
605 error_msg='Shutdown aborted by operator',
606 shutdown_count=0)
607 return
Jon Salz2f757d42012-06-27 17:06:42 +0800608
Jon Salz0697cbf2012-07-04 15:14:04 +0800609 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800610 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800611 'tests_after_shutdown',
612 [t.path for t in self.tests_to_run])
613 # Save shutdown time
614 self.state_instance.set_shared_data('shutdown_time',
615 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800616
Jon Salz0697cbf2012-07-04 15:14:04 +0800617 with self.env.lock:
618 self.event_log.Log('shutdown', operation=test.operation)
619 shutdown_result = self.env.shutdown(test.operation)
620 if shutdown_result:
621 # That's all, folks!
622 self.run_queue.put(None)
623 return
624 else:
625 # Just pass (e.g., in the chroot).
626 test.update_state(status=TestState.PASSED)
627 self.state_instance.set_shared_data(
628 'tests_after_shutdown', None)
629 # Send event with no fields to indicate that there is no
630 # longer a pending shutdown.
631 self.event_client.post_event(Event(
632 Event.Type.PENDING_SHUTDOWN))
633 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800634
Jon Salz1acc8742012-07-17 17:45:55 +0800635 self._run_test(test, test.iterations)
636
637 def _run_test(self, test, iterations_left=None):
638 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
639 new_state = test.update_state(
640 status=TestState.ACTIVE, increment_count=1, error_msg='',
641 invocation=invoc.uuid, iterations_left=iterations_left)
642 invoc.count = new_state.count
643
644 self.invocations[test] = invoc
645 if self.visible_test is None and test.has_ui:
646 self.set_visible_test(test)
647 self.check_connection_manager()
648 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800649
Jon Salz0697cbf2012-07-04 15:14:04 +0800650 def check_connection_manager(self):
651 exclusive_tests = [
652 test.path
653 for test in self.invocations
654 if test.is_exclusive(
655 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
656 if exclusive_tests:
657 # Make sure networking is disabled.
658 if self.network_enabled:
659 logging.info('Disabling network, as requested by %s',
660 exclusive_tests)
661 self.connection_manager.DisableNetworking()
662 self.network_enabled = False
663 else:
664 # Make sure networking is enabled.
665 if not self.network_enabled:
666 logging.info('Re-enabling network')
667 self.connection_manager.EnableNetworking()
668 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800669
cychiang21886742012-07-05 15:16:32 +0800670 def check_for_updates(self):
671 '''
672 Schedules an asynchronous check for updates if necessary.
673 '''
674 if not self.test_list.options.update_period_secs:
675 # Not enabled.
676 return
677
678 now = time.time()
679 if self.last_update_check and (
680 now - self.last_update_check <
681 self.test_list.options.update_period_secs):
682 # Not yet time for another check.
683 return
684
685 self.last_update_check = now
686
687 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
688 if reached_shopfloor:
689 new_update_md5sum = md5sum if needs_update else None
690 if system.SystemInfo.update_md5sum != new_update_md5sum:
691 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
692 system.SystemInfo.update_md5sum = new_update_md5sum
693 self.run_queue.put(self.update_system_info)
694
695 updater.CheckForUpdateAsync(
696 handle_check_for_update,
697 self.test_list.options.shopfloor_timeout_secs)
698
Jon Salza6711d72012-07-18 14:33:03 +0800699 def cancel_pending_tests(self):
700 '''Cancels any tests in the run queue.'''
701 self.run_tests([])
702
Jon Salz0697cbf2012-07-04 15:14:04 +0800703 def run_tests(self, subtrees, untested_only=False):
704 '''
705 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800706
Jon Salz0697cbf2012-07-04 15:14:04 +0800707 The tests are run in order unless one fails (then stops).
708 Backgroundable tests are run simultaneously; when a foreground test is
709 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800710
Jon Salz0697cbf2012-07-04 15:14:04 +0800711 @param subtrees: Node or nodes containing tests to run (may either be
712 a single test or a list). Duplicates will be ignored.
713 '''
714 if type(subtrees) != list:
715 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800716
Jon Salz0697cbf2012-07-04 15:14:04 +0800717 # Nodes we've seen so far, to avoid duplicates.
718 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800719
Jon Salz0697cbf2012-07-04 15:14:04 +0800720 self.tests_to_run = deque()
721 for subtree in subtrees:
722 for test in subtree.walk():
723 if test in seen:
724 continue
725 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800726
Jon Salz0697cbf2012-07-04 15:14:04 +0800727 if not test.is_leaf():
728 continue
729 if (untested_only and
730 test.get_state().status != TestState.UNTESTED):
731 continue
732 self.tests_to_run.append(test)
733 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800734
Jon Salz0697cbf2012-07-04 15:14:04 +0800735 def reap_completed_tests(self):
736 '''
737 Removes completed tests from the set of active tests.
738
739 Also updates the visible test if it was reaped.
740 '''
741 for t, v in dict(self.invocations).iteritems():
742 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800743 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800744 del self.invocations[t]
745
Jon Salz1acc8742012-07-17 17:45:55 +0800746 if new_state.iterations_left and new_state.status == TestState.PASSED:
747 # Play it again, Sam!
748 self._run_test(t)
749
Jon Salz0697cbf2012-07-04 15:14:04 +0800750 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800751 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800752 self.set_visible_test(None)
753 # Make the first running test, if any, the visible test
754 for t in self.test_list.walk():
755 if t in self.invocations:
756 self.set_visible_test(t)
757 break
758
Jon Salz85a39882012-07-05 16:45:04 +0800759 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800760 '''
761 Kills and waits for all active tests.
762
Jon Salz85a39882012-07-05 16:45:04 +0800763 Args:
764 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800765 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800766 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800767 '''
768 self.reap_completed_tests()
769 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800770 if root and not test.has_ancestor(root):
771 continue
772
Jon Salz0697cbf2012-07-04 15:14:04 +0800773 factory.console.info('Killing active test %s...' % test.path)
774 invoc.abort_and_join()
775 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800776 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800777 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800778
Jon Salz0697cbf2012-07-04 15:14:04 +0800779 if not abort:
780 test.update_state(status=TestState.UNTESTED)
781 self.reap_completed_tests()
782
Jon Salz85a39882012-07-05 16:45:04 +0800783 def stop(self, root=None, fail=False):
784 self.kill_active_tests(fail, root)
785 # Remove any tests in the run queue under the root.
786 self.tests_to_run = deque([x for x in self.tests_to_run
787 if root and not x.has_ancestor(root)])
788 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800789
790 def abort_active_tests(self):
791 self.kill_active_tests(True)
792
793 def main(self):
794 try:
795 self.init()
796 self.event_log.Log('goofy_init',
797 success=True)
798 except:
799 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800800 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800801 self.event_log.Log('goofy_init',
802 success=False,
803 trace=traceback.format_exc())
804 except: # pylint: disable=W0702
805 pass
806 raise
807
808 self.run()
809
810 def update_system_info(self):
811 '''Updates system info.'''
812 system_info = system.SystemInfo()
813 self.state_instance.set_shared_data('system_info', system_info.__dict__)
814 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
815 system_info=system_info.__dict__))
816 logging.info('System info: %r', system_info.__dict__)
817
Jon Salz5c344f62012-07-13 14:31:16 +0800818 def update_factory(self, auto_run_on_restart=False):
819 '''Commences updating factory software.'''
Jon Salz0697cbf2012-07-04 15:14:04 +0800820 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800821 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800822
Jon Salz5c344f62012-07-13 14:31:16 +0800823 def pre_update_hook():
824 if auto_run_on_restart:
825 self.state_instance.set_shared_data('tests_after_shutdown',
826 FORCE_AUTO_RUN)
827 self.state_instance.close()
828
Jon Salz0697cbf2012-07-04 15:14:04 +0800829 try:
Jon Salz5c344f62012-07-13 14:31:16 +0800830 if updater.TryUpdate(pre_update_hook=pre_update_hook):
Jon Salz0697cbf2012-07-04 15:14:04 +0800831 self.env.shutdown('reboot')
832 except: # pylint: disable=W0702
833 factory.console.exception('Unable to update')
834
835 def init(self, args=None, env=None):
836 '''Initializes Goofy.
837
838 Args:
839 args: A list of command-line arguments. Uses sys.argv if
840 args is None.
841 env: An Environment instance to use (or None to choose
842 FakeChrootEnvironment or DUTEnvironment as appropriate).
843 '''
844 parser = OptionParser()
845 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800846 action='store_true',
847 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800848 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800849 metavar='FILE',
850 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800851 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800852 action='store_true',
853 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800854 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800855 choices=['none', 'gtk', 'chrome'],
856 default=('chrome' if utils.in_chroot() else 'gtk'),
857 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800858 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800859 type='int', default=1,
860 help=('Factor by which to scale UI '
861 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800862 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800863 metavar='FILE',
864 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800865 (self.options, self.args) = parser.parse_args(args)
866
Jon Salz46b89562012-07-05 11:49:22 +0800867 # Make sure factory directories exist.
868 factory.get_log_root()
869 factory.get_state_root()
870 factory.get_test_data_root()
871
Jon Salz0697cbf2012-07-04 15:14:04 +0800872 global _inited_logging # pylint: disable=W0603
873 if not _inited_logging:
874 factory.init_logging('goofy', verbose=self.options.verbose)
875 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800876
Jon Salzee85d522012-07-17 14:34:46 +0800877 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +0800878 self.event_log = EventLog('goofy')
879
880 if (not suppress_chroot_warning and
881 factory.in_chroot() and
882 self.options.ui == 'gtk' and
883 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
884 # That's not going to work! Tell the user how to run
885 # this way.
886 logging.warn(GOOFY_IN_CHROOT_WARNING)
887 time.sleep(1)
888
889 if env:
890 self.env = env
891 elif factory.in_chroot():
892 self.env = test_environment.FakeChrootEnvironment()
893 logging.warn(
894 'Using chroot environment: will not actually run autotests')
895 else:
896 self.env = test_environment.DUTEnvironment()
897 self.env.goofy = self
898
899 if self.options.restart:
900 state.clear_state()
901
902 if self.options.print_test_list:
903 print (factory.read_test_list(
904 self.options.print_test_list,
905 test_classes=dict(test_steps.__dict__)).
906 __repr__(recursive=True))
907 return
908
909 if self.options.ui_scale_factor != 1 and utils.in_qemu():
910 logging.warn(
911 'In QEMU; ignoring ui_scale_factor argument')
912 self.options.ui_scale_factor = 1
913
914 logging.info('Started')
915
916 self.start_state_server()
917 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
918 self.state_instance.set_shared_data('ui_scale_factor',
919 self.options.ui_scale_factor)
920 self.last_shutdown_time = (
921 self.state_instance.get_shared_data('shutdown_time', optional=True))
922 self.state_instance.del_shared_data('shutdown_time', optional=True)
923
924 if not self.options.test_list:
925 self.options.test_list = find_test_list()
926 if not self.options.test_list:
927 logging.error('No test list. Aborting.')
928 sys.exit(1)
929 logging.info('Using test list %s', self.options.test_list)
930
931 self.test_list = factory.read_test_list(
932 self.options.test_list,
933 self.state_instance,
934 test_classes=dict(test_steps.__dict__))
935 if not self.state_instance.has_shared_data('ui_lang'):
936 self.state_instance.set_shared_data('ui_lang',
937 self.test_list.options.ui_lang)
938 self.state_instance.set_shared_data(
939 'test_list_options',
940 self.test_list.options.__dict__)
941 self.state_instance.test_list = self.test_list
942
Jon Salz8fa8e832012-07-13 19:04:09 +0800943 if self.test_list.options.time_sanitizer:
944 self.time_sanitizer = time_sanitizer.TimeSanitizer(
945 base_time=time_sanitizer.GetBaseTimeFromFile(
946 # lsb-factory is written by the factory install shim during
947 # installation, so it should have a good time obtained from
948 # the mini-Omaha server.
949 '/usr/local/etc/lsb-factory'))
950 self.time_sanitizer.RunOnce()
951
Jon Salz0697cbf2012-07-04 15:14:04 +0800952 self.init_states()
953 self.start_event_server()
954 self.connection_manager = self.env.create_connection_manager(
955 self.test_list.options.wlans)
956 # Note that we create a log watcher even if
957 # sync_event_log_period_secs isn't set (no background
958 # syncing), since we may use it to flush event logs as well.
959 self.log_watcher = EventLogWatcher(
960 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +0800961 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +0800962 if self.test_list.options.sync_event_log_period_secs:
963 self.log_watcher.StartWatchThread()
964
965 self.update_system_info()
966
967 os.environ['CROS_FACTORY'] = '1'
968 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
969
970 # Set CROS_UI since some behaviors in ui.py depend on the
971 # particular UI in use. TODO(jsalz): Remove this (and all
972 # places it is used) when the GTK UI is removed.
973 os.environ['CROS_UI'] = self.options.ui
974
975 if self.options.ui == 'chrome':
976 self.env.launch_chrome()
977 logging.info('Waiting for a web socket connection')
978 self.web_socket_manager.wait()
979
980 # Wait for the test widget size to be set; this is done in
981 # an asynchronous RPC so there is a small chance that the
982 # web socket might be opened first.
983 for _ in range(100): # 10 s
984 try:
985 if self.state_instance.get_shared_data('test_widget_size'):
986 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800987 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +0800988 pass # Retry
989 time.sleep(0.1) # 100 ms
990 else:
991 logging.warn('Never received test_widget_size from UI')
992 elif self.options.ui == 'gtk':
993 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800994
Jon Salz0697cbf2012-07-04 15:14:04 +0800995 def state_change_callback(test, test_state):
996 self.event_client.post_event(
997 Event(Event.Type.STATE_CHANGE,
998 path=test.path, state=test_state))
999 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001000
Jon Salza6711d72012-07-18 14:33:03 +08001001 for handler in self.on_ui_startup:
1002 handler()
1003
1004 self.prespawner = Prespawner()
1005 self.prespawner.start()
1006
Jon Salz0697cbf2012-07-04 15:14:04 +08001007 try:
1008 tests_after_shutdown = self.state_instance.get_shared_data(
1009 'tests_after_shutdown')
1010 except KeyError:
1011 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001012
Jon Salz5c344f62012-07-13 14:31:16 +08001013 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1014 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001015 logging.info('Resuming tests after shutdown: %s',
1016 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001017 self.tests_to_run.extend(
1018 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1019 self.run_queue.put(self.run_next_test)
1020 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001021 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001022 self.run_queue.put(
1023 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001024 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001025
Jon Salz0697cbf2012-07-04 15:14:04 +08001026 def run(self):
1027 '''Runs Goofy.'''
1028 # Process events forever.
1029 while self.run_once(True):
1030 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001031
Jon Salz0697cbf2012-07-04 15:14:04 +08001032 def run_once(self, block=False):
1033 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001034
Jon Salz0697cbf2012-07-04 15:14:04 +08001035 Args:
1036 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001037
Jon Salz0697cbf2012-07-04 15:14:04 +08001038 Returns:
1039 True to keep going or False to shut down.
1040 '''
1041 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001042 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001043 # Nothing on the run queue.
1044 self._run_queue_idle()
1045 if block:
1046 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001047 try:
1048 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1049 except Queue.Empty:
1050 # Keep going (calling _run_queue_idle() again at the top of
1051 # the loop)
1052 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001053 # ...and grab anything else that showed up at the same
1054 # time.
1055 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001056 else:
1057 break
Jon Salz51528e12012-07-02 18:54:45 +08001058
Jon Salz0697cbf2012-07-04 15:14:04 +08001059 for event in events:
1060 if not event:
1061 # Shutdown request.
1062 self.run_queue.task_done()
1063 return False
Jon Salz51528e12012-07-02 18:54:45 +08001064
Jon Salz0697cbf2012-07-04 15:14:04 +08001065 try:
1066 event()
Jon Salz85a39882012-07-05 16:45:04 +08001067 except: # pylint: disable=W0702
1068 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001069 self.record_exception(traceback.format_exception_only(
1070 *sys.exc_info()[:2]))
1071 # But keep going
1072 finally:
1073 self.run_queue.task_done()
1074 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001075
Jon Salz8fa8e832012-07-13 19:04:09 +08001076 def sync_time_in_background(self):
1077 '''Attempts to sync time with the shopfloor server.'''
1078 if ((not self.test_list.options.sync_time_period_secs) or
1079 (not self.time_sanitizer) or
1080 self.time_synced or
1081 factory.in_chroot()):
1082 # Not enabled or already succeeded.
1083 return
1084
1085 now = time.time()
1086 if self.last_sync_time and (
1087 now - self.last_sync_time <
1088 self.test_list.options.sync_time_period_secs):
1089 # Not yet time for another check.
1090 return
1091 self.last_sync_time = now
1092
1093 def target():
1094 try:
1095 self.time_sanitizer.SyncWithShopfloor()
1096 self.time_synced = True
1097 except: # pylint: disable=W0702
1098 # Oh well. Log an error (but no trace)
1099 logging.info(
1100 'Unable to get time from shopfloor server: %s',
1101 utils.FormatExceptionOnly())
1102
1103 thread = threading.Thread(target=target)
1104 thread.daemon = True
1105 thread.start()
1106
Jon Salz0697cbf2012-07-04 15:14:04 +08001107 def _run_queue_idle(self):
1108 '''Invoked when the run queue has no events.'''
1109 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001110 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001111 self.sync_time_in_background()
Jon Salz57717ca2012-04-04 16:47:25 +08001112
Jon Salz16d10542012-07-23 12:18:45 +08001113 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001114 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001115
Jon Salz0697cbf2012-07-04 15:14:04 +08001116 Attempts to upload the event logs to the shopfloor server.
1117 '''
1118 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1119 start_time = time.time()
1120 logging.info('Syncing %s', description)
1121 shopfloor_client = shopfloor.get_instance(
1122 detect=True,
1123 timeout=self.test_list.options.shopfloor_timeout_secs)
1124 shopfloor_client.UploadEvent(log_name, chunk)
1125 logging.info(
1126 'Successfully synced %s in %.03f s',
1127 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001128
Jon Salz0697cbf2012-07-04 15:14:04 +08001129 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1130 root=None):
1131 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001132
Jon Salz0697cbf2012-07-04 15:14:04 +08001133 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001134
Jon Salz0697cbf2012-07-04 15:14:04 +08001135 Args:
1136 starting_at: If provided, only auto-runs tests beginning with
1137 this test.
1138 '''
1139 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001140
Jon Salz0697cbf2012-07-04 15:14:04 +08001141 if starting_at:
1142 # Make sure they passed a test, not a string.
1143 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001144
Jon Salz0697cbf2012-07-04 15:14:04 +08001145 tests_to_reset = []
1146 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001147
Jon Salz0697cbf2012-07-04 15:14:04 +08001148 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001149
Jon Salz0697cbf2012-07-04 15:14:04 +08001150 for test in root.get_top_level_tests():
1151 if starting_at:
1152 if test == starting_at:
1153 # We've found starting_at; do auto-run on all
1154 # subsequent tests.
1155 found_starting_at = True
1156 if not found_starting_at:
1157 # Don't start this guy yet
1158 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001159
Jon Salz0697cbf2012-07-04 15:14:04 +08001160 status = test.get_state().status
1161 if status == TestState.ACTIVE or status in statuses_to_run:
1162 # Reset the test (later; we will need to abort
1163 # all active tests first).
1164 tests_to_reset.append(test)
1165 if status in statuses_to_run:
1166 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001167
Jon Salz0697cbf2012-07-04 15:14:04 +08001168 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001169
Jon Salz0697cbf2012-07-04 15:14:04 +08001170 # Reset all statuses of the tests to run (in case any tests were active;
1171 # we want them to be run again).
1172 for test_to_reset in tests_to_reset:
1173 for test in test_to_reset.walk():
1174 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001175
Jon Salz0697cbf2012-07-04 15:14:04 +08001176 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001177
Jon Salz0697cbf2012-07-04 15:14:04 +08001178 def restart_tests(self, root=None):
1179 '''Restarts all tests.'''
1180 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001181
Jon Salz0697cbf2012-07-04 15:14:04 +08001182 self.abort_active_tests()
1183 for test in root.walk():
1184 test.update_state(status=TestState.UNTESTED)
1185 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001186
Jon Salz0697cbf2012-07-04 15:14:04 +08001187 def auto_run(self, starting_at=None, root=None):
1188 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001189
Jon Salz0697cbf2012-07-04 15:14:04 +08001190 Args:
1191 starting_at: If provide, only auto-runs tests beginning with
1192 this test.
1193 '''
1194 root = root or self.test_list
1195 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1196 starting_at=starting_at,
1197 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001198
Jon Salz0697cbf2012-07-04 15:14:04 +08001199 def re_run_failed(self, root=None):
1200 '''Re-runs failed tests.'''
1201 root = root or self.test_list
1202 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001203
Jon Salz0697cbf2012-07-04 15:14:04 +08001204 def show_review_information(self):
1205 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001206
Jon Salz0697cbf2012-07-04 15:14:04 +08001207 The information screene is rendered by main UI program (ui.py), so in
1208 goofy we only need to kill all active tests, set them as untested, and
1209 clear remaining tests.
1210 '''
1211 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001212 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001213
Jon Salz0697cbf2012-07-04 15:14:04 +08001214 def handle_switch_test(self, event):
1215 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001216
Jon Salz0697cbf2012-07-04 15:14:04 +08001217 @param event: The SWITCH_TEST event.
1218 '''
1219 test = self.test_list.lookup_path(event.path)
1220 if not test:
1221 logging.error('Unknown test %r', event.key)
1222 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001223
Jon Salz0697cbf2012-07-04 15:14:04 +08001224 invoc = self.invocations.get(test)
1225 if invoc and test.backgroundable:
1226 # Already running: just bring to the front if it
1227 # has a UI.
1228 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001229 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001230 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001231
Jon Salz0697cbf2012-07-04 15:14:04 +08001232 self.abort_active_tests()
1233 for t in test.walk():
1234 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001235
Jon Salz0697cbf2012-07-04 15:14:04 +08001236 if self.test_list.options.auto_run_on_keypress:
1237 self.auto_run(starting_at=test)
1238 else:
1239 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001240
Jon Salz0697cbf2012-07-04 15:14:04 +08001241 def wait(self):
1242 '''Waits for all pending invocations.
1243
1244 Useful for testing.
1245 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001246 while self.invocations:
1247 for k, v in self.invocations.iteritems():
1248 logging.info('Waiting for %s to complete...', k)
1249 v.thread.join()
1250 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001251
1252 def check_exceptions(self):
1253 '''Raises an error if any exceptions have occurred in
1254 invocation threads.'''
1255 if self.exceptions:
1256 raise RuntimeError('Exception in invocation thread: %r' %
1257 self.exceptions)
1258
1259 def record_exception(self, msg):
1260 '''Records an exception in an invocation thread.
1261
1262 An exception with the given message will be rethrown when
1263 Goofy is destroyed.'''
1264 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001265
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001266
1267if __name__ == '__main__':
Jon Salz0697cbf2012-07-04 15:14:04 +08001268 Goofy().main()