blob: 83e2ace6821e558eb028d9f26dd781cbf3182729 [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 subprocess
16import sys
Jon Salz0405ab52012-03-16 15:26:52 +080017import threading
18import time
19import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080020import uuid
Hung-Te Linf2f78f72012-02-08 19:27:11 +080021from collections import deque
22from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080023
Jon Salz0697cbf2012-07-04 15:14:04 +080024import factory_common # pylint: disable=W0611
Jon Salz83591782012-06-26 11:09:58 +080025from cros.factory.goofy.prespawner import Prespawner
26from cros.factory.test import factory
27from cros.factory.test import state
28from cros.factory.test.factory import TestState
29from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080030from cros.factory.goofy import test_steps
31from cros.factory.goofy.event_log_watcher import EventLogWatcher
32from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080033from cros.factory.test import utils
34from cros.factory.test.event import Event
35from cros.factory.test.event import EventClient
36from cros.factory.test.event import EventServer
37from cros.factory.event_log import EventLog
38from cros.factory.goofy.invocation import TestInvocation
39from cros.factory.goofy import system
40from cros.factory.goofy import test_environment
Jon Salz8fa8e832012-07-13 19:04:09 +080041from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080042from cros.factory.goofy.web_socket_manager import WebSocketManager
Hung-Te Linf2f78f72012-02-08 19:27:11 +080043
44
Jon Salz2f757d42012-06-27 17:06:42 +080045DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
46CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080047HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
48
Jon Salz8796e362012-05-24 11:39:09 +080049# File that suppresses reboot if present (e.g., for development).
50NO_REBOOT_FILE = '/var/log/factory.noreboot'
51
Jon Salz5c344f62012-07-13 14:31:16 +080052# Value for tests_after_shutdown that forces auto-run (e.g., after
53# a factory update, when the available set of tests might change).
54FORCE_AUTO_RUN = 'force_auto_run'
55
cychiang21886742012-07-05 15:16:32 +080056RUN_QUEUE_TIMEOUT_SECS = 10
57
Jon Salz758e6cc2012-04-03 15:47:07 +080058GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
59You are running Goofy inside the chroot. Autotests are not supported.
60
61To use Goofy in the chroot, first install an Xvnc server:
62
Jon Salz0697cbf2012-07-04 15:14:04 +080063 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080064
65...and then start a VNC X server outside the chroot:
66
Jon Salz0697cbf2012-07-04 15:14:04 +080067 vncserver :10 &
68 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080069
70...and run Goofy as follows:
71
Jon Salz0697cbf2012-07-04 15:14:04 +080072 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080073''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080074suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080075
76def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080077 '''
78 Returns the HWID config tag, or an empty string if none can be found.
79 '''
80 if 'CROS_HWID' in os.environ:
81 return os.environ['CROS_HWID']
82 if os.path.exists(HWID_CFG_PATH):
83 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
84 return hwid_cfg_handle.read().strip()
85 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080086
87
88def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080089 '''
90 Returns the path to the active test list, based on the HWID config tag.
91 '''
92 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +080093
Jon Salz0697cbf2012-07-04 15:14:04 +080094 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
Jon Salz2f757d42012-06-27 17:06:42 +080095
Jon Salz0697cbf2012-07-04 15:14:04 +080096 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
97 search_files = ['test_list', 'test_list.all']
98 if hwid_cfg:
99 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800100
Jon Salz0697cbf2012-07-04 15:14:04 +0800101 for d in search_dirs:
102 for f in search_files:
103 test_list = os.path.join(d, f)
104 if os.path.exists(test_list):
105 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800106
Jon Salz0697cbf2012-07-04 15:14:04 +0800107 logging.warn('Cannot find test lists named any of %s in any of %s',
108 search_files, search_dirs)
109 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800110
Jon Salz73e0fd02012-04-04 11:46:38 +0800111_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800112
113class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800114 '''
115 The main factory flow.
116
117 Note that all methods in this class must be invoked from the main
118 (event) thread. Other threads, such as callbacks and TestInvocation
119 methods, should instead post events on the run queue.
120
121 TODO: Unit tests. (chrome-os-partner:7409)
122
123 Properties:
124 uuid: A unique UUID for this invocation of Goofy.
125 state_instance: An instance of FactoryState.
126 state_server: The FactoryState XML/RPC server.
127 state_server_thread: A thread running state_server.
128 event_server: The EventServer socket server.
129 event_server_thread: A thread running event_server.
130 event_client: A client to the event server.
131 connection_manager: The connection_manager object.
132 network_enabled: Whether the connection_manager is currently
133 enabling connections.
134 ui_process: The factory ui process object.
135 run_queue: A queue of callbacks to invoke from the main thread.
136 invocations: A map from FactoryTest objects to the corresponding
137 TestInvocations objects representing active tests.
138 tests_to_run: A deque of tests that should be run when the current
139 test(s) complete.
140 options: Command-line options.
141 args: Command-line args.
142 test_list: The test list.
143 event_handlers: Map of Event.Type to the method used to handle that
144 event. If the method has an 'event' argument, the event is passed
145 to the handler.
146 exceptions: Exceptions encountered in invocation threads.
147 '''
148 def __init__(self):
149 self.uuid = str(uuid.uuid4())
150 self.state_instance = None
151 self.state_server = None
152 self.state_server_thread = None
153 self.event_server = None
154 self.event_server_thread = None
155 self.event_client = None
156 self.connection_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800157 self.time_sanitizer = None
158 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800159 self.log_watcher = None
160 self.network_enabled = True
161 self.event_log = None
162 self.prespawner = None
163 self.ui_process = None
164 self.run_queue = Queue.Queue()
165 self.invocations = {}
166 self.tests_to_run = deque()
167 self.visible_test = None
168 self.chrome = None
169
170 self.options = None
171 self.args = None
172 self.test_list = None
173 self.on_ui_startup = []
174 self.env = None
175 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800176 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800177 self.last_sync_time = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800178
Jon Salz85a39882012-07-05 16:45:04 +0800179 def test_or_root(event, parent_or_group=True):
180 '''Returns the test affected by a particular event.
181
182 Args:
183 event: The event containing an optional 'path' attribute.
184 parent_on_group: If True, returns the top-level parent for a test (the
185 root node of the tests that need to be run together if the given test
186 path is to be run).
187 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800188 try:
189 path = event.path
190 except AttributeError:
191 path = None
192
193 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800194 test = self.test_list.lookup_path(path)
195 if parent_or_group:
196 test = test.get_top_level_parent_or_group()
197 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800198 else:
199 return self.test_list
200
201 self.event_handlers = {
202 Event.Type.SWITCH_TEST: self.handle_switch_test,
203 Event.Type.SHOW_NEXT_ACTIVE_TEST:
204 lambda event: self.show_next_active_test(),
205 Event.Type.RESTART_TESTS:
206 lambda event: self.restart_tests(root=test_or_root(event)),
207 Event.Type.AUTO_RUN:
208 lambda event: self.auto_run(root=test_or_root(event)),
209 Event.Type.RE_RUN_FAILED:
210 lambda event: self.re_run_failed(root=test_or_root(event)),
211 Event.Type.RUN_TESTS_WITH_STATUS:
212 lambda event: self.run_tests_with_status(
213 event.status,
214 root=test_or_root(event)),
215 Event.Type.REVIEW:
216 lambda event: self.show_review_information(),
217 Event.Type.UPDATE_SYSTEM_INFO:
218 lambda event: self.update_system_info(),
219 Event.Type.UPDATE_FACTORY:
220 lambda event: self.update_factory(),
221 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800222 lambda event: self.stop(root=test_or_root(event, False),
223 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800224 Event.Type.SET_VISIBLE_TEST:
225 lambda event: self.set_visible_test(
226 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800227 }
228
229 self.exceptions = []
230 self.web_socket_manager = None
231
232 def destroy(self):
233 if self.chrome:
234 self.chrome.kill()
235 self.chrome = None
236 if self.ui_process:
237 utils.kill_process_tree(self.ui_process, 'ui')
238 self.ui_process = None
239 if self.web_socket_manager:
240 logging.info('Stopping web sockets')
241 self.web_socket_manager.close()
242 self.web_socket_manager = None
243 if self.state_server_thread:
244 logging.info('Stopping state server')
245 self.state_server.shutdown()
246 self.state_server_thread.join()
247 self.state_server.server_close()
248 self.state_server_thread = None
249 if self.state_instance:
250 self.state_instance.close()
251 if self.event_server_thread:
252 logging.info('Stopping event server')
253 self.event_server.shutdown() # pylint: disable=E1101
254 self.event_server_thread.join()
255 self.event_server.server_close()
256 self.event_server_thread = None
257 if self.log_watcher:
258 if self.log_watcher.IsThreadStarted():
259 self.log_watcher.StopWatchThread()
260 self.log_watcher = None
261 if self.prespawner:
262 logging.info('Stopping prespawner')
263 self.prespawner.stop()
264 self.prespawner = None
265 if self.event_client:
266 logging.info('Closing event client')
267 self.event_client.close()
268 self.event_client = None
269 if self.event_log:
270 self.event_log.Close()
271 self.event_log = None
272 self.check_exceptions()
273 logging.info('Done destroying Goofy')
274
275 def start_state_server(self):
276 self.state_instance, self.state_server = (
277 state.create_server(bind_address='0.0.0.0'))
278 logging.info('Starting state server')
279 self.state_server_thread = threading.Thread(
280 target=self.state_server.serve_forever,
281 name='StateServer')
282 self.state_server_thread.start()
283
284 def start_event_server(self):
285 self.event_server = EventServer()
286 logging.info('Starting factory event server')
287 self.event_server_thread = threading.Thread(
288 target=self.event_server.serve_forever,
289 name='EventServer') # pylint: disable=E1101
290 self.event_server_thread.start()
291
292 self.event_client = EventClient(
293 callback=self.handle_event, event_loop=self.run_queue)
294
295 self.web_socket_manager = WebSocketManager(self.uuid)
296 self.state_server.add_handler("/event",
297 self.web_socket_manager.handle_web_socket)
298
299 def start_ui(self):
300 ui_proc_args = [
301 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
302 self.options.test_list]
303 if self.options.verbose:
304 ui_proc_args.append('-v')
305 logging.info('Starting ui %s', ui_proc_args)
306 self.ui_process = subprocess.Popen(ui_proc_args)
307 logging.info('Waiting for UI to come up...')
308 self.event_client.wait(
309 lambda event: event.type == Event.Type.UI_READY)
310 logging.info('UI has started')
311
312 def set_visible_test(self, test):
313 if self.visible_test == test:
314 return
315
316 if test:
317 test.update_state(visible=True)
318 if self.visible_test:
319 self.visible_test.update_state(visible=False)
320 self.visible_test = test
321
322 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800323 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800324 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800325
Jon Salz0697cbf2012-07-04 15:14:04 +0800326 @param test: The ShutdownStep.
327 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800328 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800329 test_state = test.update_state(increment_shutdown_count=1)
330 logging.info('Detected shutdown (%d of %d)',
331 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800332
Jon Salz0697cbf2012-07-04 15:14:04 +0800333 def log_and_update_state(status, error_msg, **kw):
334 self.event_log.Log('rebooted',
335 status=status, error_msg=error_msg, **kw)
336 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800337
Jon Salz0697cbf2012-07-04 15:14:04 +0800338 if not self.last_shutdown_time:
339 log_and_update_state(status=TestState.FAILED,
340 error_msg='Unable to read shutdown_time')
341 return
Jon Salz258a40c2012-04-19 12:34:01 +0800342
Jon Salz0697cbf2012-07-04 15:14:04 +0800343 now = time.time()
344 logging.info('%.03f s passed since reboot',
345 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800346
Jon Salz0697cbf2012-07-04 15:14:04 +0800347 if self.last_shutdown_time > now:
348 test.update_state(status=TestState.FAILED,
349 error_msg='Time moved backward during reboot')
350 elif (isinstance(test, factory.RebootStep) and
351 self.test_list.options.max_reboot_time_secs and
352 (now - self.last_shutdown_time >
353 self.test_list.options.max_reboot_time_secs)):
354 # A reboot took too long; fail. (We don't check this for
355 # HaltSteps, because the machine could be halted for a
356 # very long time, and even unplugged with battery backup,
357 # thus hosing the clock.)
358 log_and_update_state(
359 status=TestState.FAILED,
360 error_msg=('More than %d s elapsed during reboot '
361 '(%.03f s, from %s to %s)' % (
362 self.test_list.options.max_reboot_time_secs,
363 now - self.last_shutdown_time,
364 utils.TimeString(self.last_shutdown_time),
365 utils.TimeString(now))),
366 duration=(now-self.last_shutdown_time))
367 elif test_state.shutdown_count == test.iterations:
368 # Good!
369 log_and_update_state(status=TestState.PASSED,
370 duration=(now - self.last_shutdown_time),
371 error_msg='')
372 elif test_state.shutdown_count > test.iterations:
373 # Shut down too many times
374 log_and_update_state(status=TestState.FAILED,
375 error_msg='Too many shutdowns')
376 elif utils.are_shift_keys_depressed():
377 logging.info('Shift keys are depressed; cancelling restarts')
378 # Abort shutdown
379 log_and_update_state(
380 status=TestState.FAILED,
381 error_msg='Shutdown aborted with double shift keys')
382 else:
383 def handler():
384 if self._prompt_cancel_shutdown(
385 test, test_state.shutdown_count + 1):
386 log_and_update_state(
387 status=TestState.FAILED,
388 error_msg='Shutdown aborted by operator')
389 return
Jon Salz0405ab52012-03-16 15:26:52 +0800390
Jon Salz0697cbf2012-07-04 15:14:04 +0800391 # Time to shutdown again
392 log_and_update_state(
393 status=TestState.ACTIVE,
394 error_msg='',
395 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800396
Jon Salz0697cbf2012-07-04 15:14:04 +0800397 self.event_log.Log('shutdown', operation='reboot')
398 self.state_instance.set_shared_data('shutdown_time',
399 time.time())
400 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800401
Jon Salz0697cbf2012-07-04 15:14:04 +0800402 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800403
Jon Salz0697cbf2012-07-04 15:14:04 +0800404 def _prompt_cancel_shutdown(self, test, iteration):
405 if self.options.ui != 'chrome':
406 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800407
Jon Salz0697cbf2012-07-04 15:14:04 +0800408 pending_shutdown_data = {
409 'delay_secs': test.delay_secs,
410 'time': time.time() + test.delay_secs,
411 'operation': test.operation,
412 'iteration': iteration,
413 'iterations': test.iterations,
414 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800415
Jon Salz0697cbf2012-07-04 15:14:04 +0800416 # Create a new (threaded) event client since we
417 # don't want to use the event loop for this.
418 with EventClient() as event_client:
419 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
420 **pending_shutdown_data))
421 aborted = event_client.wait(
422 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
423 timeout=test.delay_secs) is not None
424 if aborted:
425 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
426 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800427
Jon Salz0697cbf2012-07-04 15:14:04 +0800428 def init_states(self):
429 '''
430 Initializes all states on startup.
431 '''
432 for test in self.test_list.get_all_tests():
433 # Make sure the state server knows about all the tests,
434 # defaulting to an untested state.
435 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800436
Jon Salz0697cbf2012-07-04 15:14:04 +0800437 var_log_messages = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800438
Jon Salz0697cbf2012-07-04 15:14:04 +0800439 # Any 'active' tests should be marked as failed now.
440 for test in self.test_list.walk():
441 test_state = test.get_state()
442 if test_state.status != TestState.ACTIVE:
443 continue
444 if isinstance(test, factory.ShutdownStep):
445 # Shutdown while the test was active - that's good.
446 self.handle_shutdown_complete(test, test_state)
447 else:
448 # Unexpected shutdown. Grab /var/log/messages for context.
449 if var_log_messages is None:
450 try:
451 var_log_messages = (
452 utils.var_log_messages_before_reboot())
453 # Write it to the log, to make it easier to
454 # correlate with /var/log/messages.
455 logging.info(
456 'Unexpected shutdown. '
457 'Tail of /var/log/messages before last reboot:\n'
458 '%s', ('\n'.join(
459 ' ' + x for x in var_log_messages)))
460 except: # pylint: disable=W0702
461 logging.exception('Unable to grok /var/log/messages')
462 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800463
Jon Salz0697cbf2012-07-04 15:14:04 +0800464 error_msg = 'Unexpected shutdown while test was running'
465 self.event_log.Log('end_test',
466 path=test.path,
467 status=TestState.FAILED,
468 invocation=test.get_state().invocation,
469 error_msg=error_msg,
470 var_log_messages='\n'.join(var_log_messages))
471 test.update_state(
472 status=TestState.FAILED,
473 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800474
Jon Salz0697cbf2012-07-04 15:14:04 +0800475 def show_next_active_test(self):
476 '''
477 Rotates to the next visible active test.
478 '''
479 self.reap_completed_tests()
480 active_tests = [
481 t for t in self.test_list.walk()
482 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
483 if not active_tests:
484 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800485
Jon Salz0697cbf2012-07-04 15:14:04 +0800486 try:
487 next_test = active_tests[
488 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
489 except ValueError: # visible_test not present in active_tests
490 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800491
Jon Salz0697cbf2012-07-04 15:14:04 +0800492 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800493
Jon Salz0697cbf2012-07-04 15:14:04 +0800494 def handle_event(self, event):
495 '''
496 Handles an event from the event server.
497 '''
498 handler = self.event_handlers.get(event.type)
499 if handler:
500 handler(event)
501 else:
502 # We don't register handlers for all event types - just ignore
503 # this event.
504 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800505
Jon Salz0697cbf2012-07-04 15:14:04 +0800506 def run_next_test(self):
507 '''
508 Runs the next eligible test (or tests) in self.tests_to_run.
509 '''
510 self.reap_completed_tests()
511 while self.tests_to_run:
512 logging.debug('Tests to run: %s',
513 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800514
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800516
Jon Salz0697cbf2012-07-04 15:14:04 +0800517 if test in self.invocations:
518 logging.info('Next test %s is already running', test.path)
519 self.tests_to_run.popleft()
520 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800521
Jon Salz304a75d2012-07-06 11:14:15 +0800522 for i in test.require_run:
523 for j in i.walk():
524 if j.get_state().status == TestState.ACTIVE:
525 logging.info('Waiting for active test %s to complete '
526 'before running %s', j.path, test.path)
527 return
528
Jon Salz0697cbf2012-07-04 15:14:04 +0800529 if self.invocations and not (test.backgroundable and all(
530 [x.backgroundable for x in self.invocations])):
531 logging.debug('Waiting for non-backgroundable tests to '
532 'complete before running %s', test.path)
533 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800534
Jon Salz0697cbf2012-07-04 15:14:04 +0800535 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800536
Jon Salz304a75d2012-07-06 11:14:15 +0800537 untested = set()
538 for i in test.require_run:
539 for j in i.walk():
540 if j == test:
541 # We've hit this test itself; stop checking
542 break
543 if j.get_state().status == TestState.UNTESTED:
544 # Found an untested test; move on to the next
545 # element in require_run.
546 untested.add(j)
547 break
548
549 if untested:
550 untested_paths = ', '.join(sorted([x.path for x in untested]))
551 if self.state_instance.get_shared_data('engineering_mode',
552 optional=True):
553 # In engineering mode, we'll let it go.
554 factory.console.warn('In engineering mode; running '
555 '%s even though required tests '
556 '[%s] have not completed',
557 test.path, untested_paths)
558 else:
559 # Not in engineering mode; mark it failed.
560 error_msg = ('Required tests [%s] have not been run yet'
561 % untested_paths)
562 factory.console.error('Not running %s: %s',
563 test.path, error_msg)
564 test.update_state(status=TestState.FAILED,
565 error_msg=error_msg)
566 continue
567
Jon Salz0697cbf2012-07-04 15:14:04 +0800568 if isinstance(test, factory.ShutdownStep):
569 if os.path.exists(NO_REBOOT_FILE):
570 test.update_state(
571 status=TestState.FAILED, increment_count=1,
572 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800573 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800574 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800575
Jon Salz0697cbf2012-07-04 15:14:04 +0800576 test.update_state(status=TestState.ACTIVE, increment_count=1,
577 error_msg='', shutdown_count=0)
578 if self._prompt_cancel_shutdown(test, 1):
579 self.event_log.Log('reboot_cancelled')
580 test.update_state(
581 status=TestState.FAILED, increment_count=1,
582 error_msg='Shutdown aborted by operator',
583 shutdown_count=0)
584 return
Jon Salz2f757d42012-06-27 17:06:42 +0800585
Jon Salz0697cbf2012-07-04 15:14:04 +0800586 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800587 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800588 'tests_after_shutdown',
589 [t.path for t in self.tests_to_run])
590 # Save shutdown time
591 self.state_instance.set_shared_data('shutdown_time',
592 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800593
Jon Salz0697cbf2012-07-04 15:14:04 +0800594 with self.env.lock:
595 self.event_log.Log('shutdown', operation=test.operation)
596 shutdown_result = self.env.shutdown(test.operation)
597 if shutdown_result:
598 # That's all, folks!
599 self.run_queue.put(None)
600 return
601 else:
602 # Just pass (e.g., in the chroot).
603 test.update_state(status=TestState.PASSED)
604 self.state_instance.set_shared_data(
605 'tests_after_shutdown', None)
606 # Send event with no fields to indicate that there is no
607 # longer a pending shutdown.
608 self.event_client.post_event(Event(
609 Event.Type.PENDING_SHUTDOWN))
610 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800611
Jon Salz0697cbf2012-07-04 15:14:04 +0800612 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
613 self.invocations[test] = invoc
614 if self.visible_test is None and test.has_ui:
615 self.set_visible_test(test)
616 self.check_connection_manager()
617 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800618
Jon Salz0697cbf2012-07-04 15:14:04 +0800619 def check_connection_manager(self):
620 exclusive_tests = [
621 test.path
622 for test in self.invocations
623 if test.is_exclusive(
624 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
625 if exclusive_tests:
626 # Make sure networking is disabled.
627 if self.network_enabled:
628 logging.info('Disabling network, as requested by %s',
629 exclusive_tests)
630 self.connection_manager.DisableNetworking()
631 self.network_enabled = False
632 else:
633 # Make sure networking is enabled.
634 if not self.network_enabled:
635 logging.info('Re-enabling network')
636 self.connection_manager.EnableNetworking()
637 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800638
cychiang21886742012-07-05 15:16:32 +0800639 def check_for_updates(self):
640 '''
641 Schedules an asynchronous check for updates if necessary.
642 '''
643 if not self.test_list.options.update_period_secs:
644 # Not enabled.
645 return
646
647 now = time.time()
648 if self.last_update_check and (
649 now - self.last_update_check <
650 self.test_list.options.update_period_secs):
651 # Not yet time for another check.
652 return
653
654 self.last_update_check = now
655
656 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
657 if reached_shopfloor:
658 new_update_md5sum = md5sum if needs_update else None
659 if system.SystemInfo.update_md5sum != new_update_md5sum:
660 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
661 system.SystemInfo.update_md5sum = new_update_md5sum
662 self.run_queue.put(self.update_system_info)
663
664 updater.CheckForUpdateAsync(
665 handle_check_for_update,
666 self.test_list.options.shopfloor_timeout_secs)
667
Jon Salz0697cbf2012-07-04 15:14:04 +0800668 def run_tests(self, subtrees, untested_only=False):
669 '''
670 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800671
Jon Salz0697cbf2012-07-04 15:14:04 +0800672 The tests are run in order unless one fails (then stops).
673 Backgroundable tests are run simultaneously; when a foreground test is
674 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800675
Jon Salz0697cbf2012-07-04 15:14:04 +0800676 @param subtrees: Node or nodes containing tests to run (may either be
677 a single test or a list). Duplicates will be ignored.
678 '''
679 if type(subtrees) != list:
680 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800681
Jon Salz0697cbf2012-07-04 15:14:04 +0800682 # Nodes we've seen so far, to avoid duplicates.
683 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800684
Jon Salz0697cbf2012-07-04 15:14:04 +0800685 self.tests_to_run = deque()
686 for subtree in subtrees:
687 for test in subtree.walk():
688 if test in seen:
689 continue
690 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800691
Jon Salz0697cbf2012-07-04 15:14:04 +0800692 if not test.is_leaf():
693 continue
694 if (untested_only and
695 test.get_state().status != TestState.UNTESTED):
696 continue
697 self.tests_to_run.append(test)
698 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800699
Jon Salz0697cbf2012-07-04 15:14:04 +0800700 def reap_completed_tests(self):
701 '''
702 Removes completed tests from the set of active tests.
703
704 Also updates the visible test if it was reaped.
705 '''
706 for t, v in dict(self.invocations).iteritems():
707 if v.is_completed():
708 del self.invocations[t]
709
710 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800711 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800712 self.set_visible_test(None)
713 # Make the first running test, if any, the visible test
714 for t in self.test_list.walk():
715 if t in self.invocations:
716 self.set_visible_test(t)
717 break
718
Jon Salz85a39882012-07-05 16:45:04 +0800719 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800720 '''
721 Kills and waits for all active tests.
722
Jon Salz85a39882012-07-05 16:45:04 +0800723 Args:
724 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800725 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800726 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800727 '''
728 self.reap_completed_tests()
729 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800730 if root and not test.has_ancestor(root):
731 continue
732
Jon Salz0697cbf2012-07-04 15:14:04 +0800733 factory.console.info('Killing active test %s...' % test.path)
734 invoc.abort_and_join()
735 factory.console.info('Killed %s' % test.path)
736 del self.invocations[test]
737 if not abort:
738 test.update_state(status=TestState.UNTESTED)
739 self.reap_completed_tests()
740
Jon Salz85a39882012-07-05 16:45:04 +0800741 def stop(self, root=None, fail=False):
742 self.kill_active_tests(fail, root)
743 # Remove any tests in the run queue under the root.
744 self.tests_to_run = deque([x for x in self.tests_to_run
745 if root and not x.has_ancestor(root)])
746 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800747
748 def abort_active_tests(self):
749 self.kill_active_tests(True)
750
751 def main(self):
752 try:
753 self.init()
754 self.event_log.Log('goofy_init',
755 success=True)
756 except:
757 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800758 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800759 self.event_log.Log('goofy_init',
760 success=False,
761 trace=traceback.format_exc())
762 except: # pylint: disable=W0702
763 pass
764 raise
765
766 self.run()
767
768 def update_system_info(self):
769 '''Updates system info.'''
770 system_info = system.SystemInfo()
771 self.state_instance.set_shared_data('system_info', system_info.__dict__)
772 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
773 system_info=system_info.__dict__))
774 logging.info('System info: %r', system_info.__dict__)
775
Jon Salz5c344f62012-07-13 14:31:16 +0800776 def update_factory(self, auto_run_on_restart=False):
777 '''Commences updating factory software.'''
Jon Salz0697cbf2012-07-04 15:14:04 +0800778 self.kill_active_tests(False)
779 self.run_tests([])
780
Jon Salz5c344f62012-07-13 14:31:16 +0800781 def pre_update_hook():
782 if auto_run_on_restart:
783 self.state_instance.set_shared_data('tests_after_shutdown',
784 FORCE_AUTO_RUN)
785 self.state_instance.close()
786
Jon Salz0697cbf2012-07-04 15:14:04 +0800787 try:
Jon Salz5c344f62012-07-13 14:31:16 +0800788 if updater.TryUpdate(pre_update_hook=pre_update_hook):
Jon Salz0697cbf2012-07-04 15:14:04 +0800789 self.env.shutdown('reboot')
790 except: # pylint: disable=W0702
791 factory.console.exception('Unable to update')
792
793 def init(self, args=None, env=None):
794 '''Initializes Goofy.
795
796 Args:
797 args: A list of command-line arguments. Uses sys.argv if
798 args is None.
799 env: An Environment instance to use (or None to choose
800 FakeChrootEnvironment or DUTEnvironment as appropriate).
801 '''
802 parser = OptionParser()
803 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800804 action='store_true',
805 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800806 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800807 metavar='FILE',
808 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800809 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800810 action='store_true',
811 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800812 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800813 choices=['none', 'gtk', 'chrome'],
814 default=('chrome' if utils.in_chroot() else 'gtk'),
815 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800816 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800817 type='int', default=1,
818 help=('Factor by which to scale UI '
819 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800820 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800821 metavar='FILE',
822 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800823 (self.options, self.args) = parser.parse_args(args)
824
Jon Salz46b89562012-07-05 11:49:22 +0800825 # Make sure factory directories exist.
826 factory.get_log_root()
827 factory.get_state_root()
828 factory.get_test_data_root()
829
Jon Salz0697cbf2012-07-04 15:14:04 +0800830 global _inited_logging # pylint: disable=W0603
831 if not _inited_logging:
832 factory.init_logging('goofy', verbose=self.options.verbose)
833 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800834
Jon Salz0697cbf2012-07-04 15:14:04 +0800835 self.event_log = EventLog('goofy')
836
837 if (not suppress_chroot_warning and
838 factory.in_chroot() and
839 self.options.ui == 'gtk' and
840 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
841 # That's not going to work! Tell the user how to run
842 # this way.
843 logging.warn(GOOFY_IN_CHROOT_WARNING)
844 time.sleep(1)
845
846 if env:
847 self.env = env
848 elif factory.in_chroot():
849 self.env = test_environment.FakeChrootEnvironment()
850 logging.warn(
851 'Using chroot environment: will not actually run autotests')
852 else:
853 self.env = test_environment.DUTEnvironment()
854 self.env.goofy = self
855
856 if self.options.restart:
857 state.clear_state()
858
859 if self.options.print_test_list:
860 print (factory.read_test_list(
861 self.options.print_test_list,
862 test_classes=dict(test_steps.__dict__)).
863 __repr__(recursive=True))
864 return
865
866 if self.options.ui_scale_factor != 1 and utils.in_qemu():
867 logging.warn(
868 'In QEMU; ignoring ui_scale_factor argument')
869 self.options.ui_scale_factor = 1
870
871 logging.info('Started')
872
873 self.start_state_server()
874 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
875 self.state_instance.set_shared_data('ui_scale_factor',
876 self.options.ui_scale_factor)
877 self.last_shutdown_time = (
878 self.state_instance.get_shared_data('shutdown_time', optional=True))
879 self.state_instance.del_shared_data('shutdown_time', optional=True)
880
881 if not self.options.test_list:
882 self.options.test_list = find_test_list()
883 if not self.options.test_list:
884 logging.error('No test list. Aborting.')
885 sys.exit(1)
886 logging.info('Using test list %s', self.options.test_list)
887
888 self.test_list = factory.read_test_list(
889 self.options.test_list,
890 self.state_instance,
891 test_classes=dict(test_steps.__dict__))
892 if not self.state_instance.has_shared_data('ui_lang'):
893 self.state_instance.set_shared_data('ui_lang',
894 self.test_list.options.ui_lang)
895 self.state_instance.set_shared_data(
896 'test_list_options',
897 self.test_list.options.__dict__)
898 self.state_instance.test_list = self.test_list
899
Jon Salz8fa8e832012-07-13 19:04:09 +0800900 if self.test_list.options.time_sanitizer:
901 self.time_sanitizer = time_sanitizer.TimeSanitizer(
902 base_time=time_sanitizer.GetBaseTimeFromFile(
903 # lsb-factory is written by the factory install shim during
904 # installation, so it should have a good time obtained from
905 # the mini-Omaha server.
906 '/usr/local/etc/lsb-factory'))
907 self.time_sanitizer.RunOnce()
908
Jon Salz0697cbf2012-07-04 15:14:04 +0800909 self.init_states()
910 self.start_event_server()
911 self.connection_manager = self.env.create_connection_manager(
912 self.test_list.options.wlans)
913 # Note that we create a log watcher even if
914 # sync_event_log_period_secs isn't set (no background
915 # syncing), since we may use it to flush event logs as well.
916 self.log_watcher = EventLogWatcher(
917 self.test_list.options.sync_event_log_period_secs,
918 handle_event_logs_callback=self._handle_event_logs)
919 if self.test_list.options.sync_event_log_period_secs:
920 self.log_watcher.StartWatchThread()
921
922 self.update_system_info()
923
924 os.environ['CROS_FACTORY'] = '1'
925 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
926
927 # Set CROS_UI since some behaviors in ui.py depend on the
928 # particular UI in use. TODO(jsalz): Remove this (and all
929 # places it is used) when the GTK UI is removed.
930 os.environ['CROS_UI'] = self.options.ui
931
932 if self.options.ui == 'chrome':
933 self.env.launch_chrome()
934 logging.info('Waiting for a web socket connection')
935 self.web_socket_manager.wait()
936
937 # Wait for the test widget size to be set; this is done in
938 # an asynchronous RPC so there is a small chance that the
939 # web socket might be opened first.
940 for _ in range(100): # 10 s
941 try:
942 if self.state_instance.get_shared_data('test_widget_size'):
943 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800944 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +0800945 pass # Retry
946 time.sleep(0.1) # 100 ms
947 else:
948 logging.warn('Never received test_widget_size from UI')
949 elif self.options.ui == 'gtk':
950 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800951
Jon Salz0697cbf2012-07-04 15:14:04 +0800952 for handler in self.on_ui_startup:
953 handler()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800954
Jon Salz0697cbf2012-07-04 15:14:04 +0800955 self.prespawner = Prespawner()
956 self.prespawner.start()
Jon Salz73e0fd02012-04-04 11:46:38 +0800957
Jon Salz0697cbf2012-07-04 15:14:04 +0800958 def state_change_callback(test, test_state):
959 self.event_client.post_event(
960 Event(Event.Type.STATE_CHANGE,
961 path=test.path, state=test_state))
962 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +0800963
Jon Salz0697cbf2012-07-04 15:14:04 +0800964 try:
965 tests_after_shutdown = self.state_instance.get_shared_data(
966 'tests_after_shutdown')
967 except KeyError:
968 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +0800969
Jon Salz5c344f62012-07-13 14:31:16 +0800970 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
971 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +0800972 logging.info('Resuming tests after shutdown: %s',
973 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +0800974 self.tests_to_run.extend(
975 self.test_list.lookup_path(t) for t in tests_after_shutdown)
976 self.run_queue.put(self.run_next_test)
977 else:
Jon Salz5c344f62012-07-13 14:31:16 +0800978 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +0800979 self.run_queue.put(
980 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +0800981 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800982
Jon Salz0697cbf2012-07-04 15:14:04 +0800983 def run(self):
984 '''Runs Goofy.'''
985 # Process events forever.
986 while self.run_once(True):
987 pass
Jon Salz73e0fd02012-04-04 11:46:38 +0800988
Jon Salz0697cbf2012-07-04 15:14:04 +0800989 def run_once(self, block=False):
990 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800991
Jon Salz0697cbf2012-07-04 15:14:04 +0800992 Args:
993 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +0800994
Jon Salz0697cbf2012-07-04 15:14:04 +0800995 Returns:
996 True to keep going or False to shut down.
997 '''
998 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +0800999 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001000 # Nothing on the run queue.
1001 self._run_queue_idle()
1002 if block:
1003 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001004 try:
1005 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1006 except Queue.Empty:
1007 # Keep going (calling _run_queue_idle() again at the top of
1008 # the loop)
1009 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001010 # ...and grab anything else that showed up at the same
1011 # time.
1012 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001013 else:
1014 break
Jon Salz51528e12012-07-02 18:54:45 +08001015
Jon Salz0697cbf2012-07-04 15:14:04 +08001016 for event in events:
1017 if not event:
1018 # Shutdown request.
1019 self.run_queue.task_done()
1020 return False
Jon Salz51528e12012-07-02 18:54:45 +08001021
Jon Salz0697cbf2012-07-04 15:14:04 +08001022 try:
1023 event()
Jon Salz85a39882012-07-05 16:45:04 +08001024 except: # pylint: disable=W0702
1025 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001026 self.record_exception(traceback.format_exception_only(
1027 *sys.exc_info()[:2]))
1028 # But keep going
1029 finally:
1030 self.run_queue.task_done()
1031 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001032
Jon Salz8fa8e832012-07-13 19:04:09 +08001033 def sync_time_in_background(self):
1034 '''Attempts to sync time with the shopfloor server.'''
1035 if ((not self.test_list.options.sync_time_period_secs) or
1036 (not self.time_sanitizer) or
1037 self.time_synced or
1038 factory.in_chroot()):
1039 # Not enabled or already succeeded.
1040 return
1041
1042 now = time.time()
1043 if self.last_sync_time and (
1044 now - self.last_sync_time <
1045 self.test_list.options.sync_time_period_secs):
1046 # Not yet time for another check.
1047 return
1048 self.last_sync_time = now
1049
1050 def target():
1051 try:
1052 self.time_sanitizer.SyncWithShopfloor()
1053 self.time_synced = True
1054 except: # pylint: disable=W0702
1055 # Oh well. Log an error (but no trace)
1056 logging.info(
1057 'Unable to get time from shopfloor server: %s',
1058 utils.FormatExceptionOnly())
1059
1060 thread = threading.Thread(target=target)
1061 thread.daemon = True
1062 thread.start()
1063
Jon Salz0697cbf2012-07-04 15:14:04 +08001064 def _run_queue_idle(self):
1065 '''Invoked when the run queue has no events.'''
1066 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001067 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001068 self.sync_time_in_background()
Jon Salz57717ca2012-04-04 16:47:25 +08001069
Jon Salz0697cbf2012-07-04 15:14:04 +08001070 def _handle_event_logs(self, log_name, chunk):
1071 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001072
Jon Salz0697cbf2012-07-04 15:14:04 +08001073 Attempts to upload the event logs to the shopfloor server.
1074 '''
1075 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1076 start_time = time.time()
1077 logging.info('Syncing %s', description)
1078 shopfloor_client = shopfloor.get_instance(
1079 detect=True,
1080 timeout=self.test_list.options.shopfloor_timeout_secs)
1081 shopfloor_client.UploadEvent(log_name, chunk)
1082 logging.info(
1083 'Successfully synced %s in %.03f s',
1084 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001085
Jon Salz0697cbf2012-07-04 15:14:04 +08001086 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1087 root=None):
1088 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001089
Jon Salz0697cbf2012-07-04 15:14:04 +08001090 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001091
Jon Salz0697cbf2012-07-04 15:14:04 +08001092 Args:
1093 starting_at: If provided, only auto-runs tests beginning with
1094 this test.
1095 '''
1096 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001097
Jon Salz0697cbf2012-07-04 15:14:04 +08001098 if starting_at:
1099 # Make sure they passed a test, not a string.
1100 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001101
Jon Salz0697cbf2012-07-04 15:14:04 +08001102 tests_to_reset = []
1103 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001104
Jon Salz0697cbf2012-07-04 15:14:04 +08001105 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001106
Jon Salz0697cbf2012-07-04 15:14:04 +08001107 for test in root.get_top_level_tests():
1108 if starting_at:
1109 if test == starting_at:
1110 # We've found starting_at; do auto-run on all
1111 # subsequent tests.
1112 found_starting_at = True
1113 if not found_starting_at:
1114 # Don't start this guy yet
1115 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001116
Jon Salz0697cbf2012-07-04 15:14:04 +08001117 status = test.get_state().status
1118 if status == TestState.ACTIVE or status in statuses_to_run:
1119 # Reset the test (later; we will need to abort
1120 # all active tests first).
1121 tests_to_reset.append(test)
1122 if status in statuses_to_run:
1123 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001124
Jon Salz0697cbf2012-07-04 15:14:04 +08001125 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001126
Jon Salz0697cbf2012-07-04 15:14:04 +08001127 # Reset all statuses of the tests to run (in case any tests were active;
1128 # we want them to be run again).
1129 for test_to_reset in tests_to_reset:
1130 for test in test_to_reset.walk():
1131 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001132
Jon Salz0697cbf2012-07-04 15:14:04 +08001133 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001134
Jon Salz0697cbf2012-07-04 15:14:04 +08001135 def restart_tests(self, root=None):
1136 '''Restarts all tests.'''
1137 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001138
Jon Salz0697cbf2012-07-04 15:14:04 +08001139 self.abort_active_tests()
1140 for test in root.walk():
1141 test.update_state(status=TestState.UNTESTED)
1142 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001143
Jon Salz0697cbf2012-07-04 15:14:04 +08001144 def auto_run(self, starting_at=None, root=None):
1145 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001146
Jon Salz0697cbf2012-07-04 15:14:04 +08001147 Args:
1148 starting_at: If provide, only auto-runs tests beginning with
1149 this test.
1150 '''
1151 root = root or self.test_list
1152 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1153 starting_at=starting_at,
1154 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001155
Jon Salz0697cbf2012-07-04 15:14:04 +08001156 def re_run_failed(self, root=None):
1157 '''Re-runs failed tests.'''
1158 root = root or self.test_list
1159 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001160
Jon Salz0697cbf2012-07-04 15:14:04 +08001161 def show_review_information(self):
1162 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001163
Jon Salz0697cbf2012-07-04 15:14:04 +08001164 The information screene is rendered by main UI program (ui.py), so in
1165 goofy we only need to kill all active tests, set them as untested, and
1166 clear remaining tests.
1167 '''
1168 self.kill_active_tests(False)
1169 self.run_tests([])
Jon Salz57717ca2012-04-04 16:47:25 +08001170
Jon Salz0697cbf2012-07-04 15:14:04 +08001171 def handle_switch_test(self, event):
1172 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001173
Jon Salz0697cbf2012-07-04 15:14:04 +08001174 @param event: The SWITCH_TEST event.
1175 '''
1176 test = self.test_list.lookup_path(event.path)
1177 if not test:
1178 logging.error('Unknown test %r', event.key)
1179 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001180
Jon Salz0697cbf2012-07-04 15:14:04 +08001181 invoc = self.invocations.get(test)
1182 if invoc and test.backgroundable:
1183 # Already running: just bring to the front if it
1184 # has a UI.
1185 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001186 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001187 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001188
Jon Salz0697cbf2012-07-04 15:14:04 +08001189 self.abort_active_tests()
1190 for t in test.walk():
1191 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001192
Jon Salz0697cbf2012-07-04 15:14:04 +08001193 if self.test_list.options.auto_run_on_keypress:
1194 self.auto_run(starting_at=test)
1195 else:
1196 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001197
Jon Salz0697cbf2012-07-04 15:14:04 +08001198 def wait(self):
1199 '''Waits for all pending invocations.
1200
1201 Useful for testing.
1202 '''
1203 for k, v in self.invocations.iteritems():
1204 logging.info('Waiting for %s to complete...', k)
1205 v.thread.join()
1206
1207 def check_exceptions(self):
1208 '''Raises an error if any exceptions have occurred in
1209 invocation threads.'''
1210 if self.exceptions:
1211 raise RuntimeError('Exception in invocation thread: %r' %
1212 self.exceptions)
1213
1214 def record_exception(self, msg):
1215 '''Records an exception in an invocation thread.
1216
1217 An exception with the given message will be rethrown when
1218 Goofy is destroyed.'''
1219 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001220
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001221
1222if __name__ == '__main__':
Jon Salz0697cbf2012-07-04 15:14:04 +08001223 Goofy().main()