blob: cbdcb985bfb363ee7fe6e0fcfd9751b62ab0bc99 [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
2#
3# -*- coding: utf-8 -*-
4#
Jon Salz37eccbd2012-05-25 16:06:52 +08005# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08006# Use of this source code is governed by a BSD-style license that can be
7# found in the LICENSE file.
8
9'''
10The main factory flow that runs the factory test and finalizes a device.
11'''
12
Jon Salz73e0fd02012-04-04 11:46:38 +080013import array
14import fcntl
15import glob
Jon Salz0405ab52012-03-16 15:26:52 +080016import logging
17import os
Jon Salz258a40c2012-04-19 12:34:01 +080018import cPickle as pickle
Jon Salz0405ab52012-03-16 15:26:52 +080019import pipes
Jon Salz73e0fd02012-04-04 11:46:38 +080020import Queue
Jon Salz0405ab52012-03-16 15:26:52 +080021import re
22import signal
23import subprocess
24import sys
25import tempfile
26import threading
27import time
28import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080029import unittest
30import uuid
Hung-Te Linf2f78f72012-02-08 19:27:11 +080031from collections import deque
32from optparse import OptionParser
Jon Salz258a40c2012-04-19 12:34:01 +080033from StringIO import StringIO
Hung-Te Linf2f78f72012-02-08 19:27:11 +080034
35import factory_common
Jon Salz83591782012-06-26 11:09:58 +080036from cros.factory.goofy.prespawner import Prespawner
37from cros.factory.test import factory
38from cros.factory.test import state
39from cros.factory.test.factory import TestState
40from cros.factory.goofy import updater
41from cros.factory.test import utils
42from cros.factory.test.event import Event
43from cros.factory.test.event import EventClient
44from cros.factory.test.event import EventServer
45from cros.factory.event_log import EventLog
46from cros.factory.goofy.invocation import TestInvocation
47from cros.factory.goofy import system
48from cros.factory.goofy import test_environment
49from cros.factory.goofy.web_socket_manager import WebSocketManager
Hung-Te Linf2f78f72012-02-08 19:27:11 +080050
51
Jon Salz2f757d42012-06-27 17:06:42 +080052DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
53CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080054HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
55
Jon Salz8796e362012-05-24 11:39:09 +080056# File that suppresses reboot if present (e.g., for development).
57NO_REBOOT_FILE = '/var/log/factory.noreboot'
58
Jon Salz758e6cc2012-04-03 15:47:07 +080059GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
60You are running Goofy inside the chroot. Autotests are not supported.
61
62To use Goofy in the chroot, first install an Xvnc server:
63
64 sudo apt-get install tightvncserver
65
66...and then start a VNC X server outside the chroot:
67
68 vncserver :10 &
69 vncviewer :10
70
71...and run Goofy as follows:
72
73 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
74''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080075suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080076
77def get_hwid_cfg():
78 '''
79 Returns the HWID config tag, or an empty string if none can be found.
80 '''
81 if 'CROS_HWID' in os.environ:
82 return os.environ['CROS_HWID']
83 if os.path.exists(HWID_CFG_PATH):
84 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
85 return hwid_cfg_handle.read().strip()
86 return ''
87
88
89def find_test_list():
90 '''
91 Returns the path to the active test list, based on the HWID config tag.
92 '''
93 hwid_cfg = get_hwid_cfg()
94
Jon Salz2f757d42012-06-27 17:06:42 +080095 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
96
97 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
98 search_files = ['test_list', 'test_list.all']
Hung-Te Linf2f78f72012-02-08 19:27:11 +080099 if hwid_cfg:
Jon Salz2f757d42012-06-27 17:06:42 +0800100 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800101
Jon Salz2f757d42012-06-27 17:06:42 +0800102 for d in search_dirs:
103 for f in search_files:
104 test_list = os.path.join(d, f)
105 if os.path.exists(test_list):
106 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800107
Jon Salz2f757d42012-06-27 17:06:42 +0800108 logging.warn('Cannot find test lists named any of %s in any of %s',
109 search_files, search_dirs)
110 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800111
Jon Salz73e0fd02012-04-04 11:46:38 +0800112_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800113
114class Goofy(object):
115 '''
116 The main factory flow.
117
118 Note that all methods in this class must be invoked from the main
119 (event) thread. Other threads, such as callbacks and TestInvocation
120 methods, should instead post events on the run queue.
121
122 TODO: Unit tests. (chrome-os-partner:7409)
123
124 Properties:
Jon Salz258a40c2012-04-19 12:34:01 +0800125 uuid: A unique UUID for this invocation of Goofy.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800126 state_instance: An instance of FactoryState.
127 state_server: The FactoryState XML/RPC server.
128 state_server_thread: A thread running state_server.
129 event_server: The EventServer socket server.
130 event_server_thread: A thread running event_server.
131 event_client: A client to the event server.
Jon Salz7c15e8b2012-06-19 17:10:37 +0800132 connection_manager: The connection_manager object.
133 network_enabled: Whether the connection_manager is currently
134 enabling connections.
Hung-Te Lin6bb48552012-02-09 14:37:43 +0800135 ui_process: The factory ui process object.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800136 run_queue: A queue of callbacks to invoke from the main thread.
137 invocations: A map from FactoryTest objects to the corresponding
138 TestInvocations objects representing active tests.
139 tests_to_run: A deque of tests that should be run when the current
140 test(s) complete.
141 options: Command-line options.
142 args: Command-line args.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800143 test_list: The test list.
Jon Salz0405ab52012-03-16 15:26:52 +0800144 event_handlers: Map of Event.Type to the method used to handle that
145 event. If the method has an 'event' argument, the event is passed
146 to the handler.
Jon Salz73e0fd02012-04-04 11:46:38 +0800147 exceptions: Exceptions encountered in invocation threads.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800148 '''
149 def __init__(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800150 self.uuid = str(uuid.uuid4())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800151 self.state_instance = None
152 self.state_server = None
153 self.state_server_thread = None
154 self.event_server = None
155 self.event_server_thread = None
156 self.event_client = None
Jon Salz7c15e8b2012-06-19 17:10:37 +0800157 self.connection_manager = None
158 self.network_enabled = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800159 self.event_log = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800160 self.prespawner = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800161 self.ui_process = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800162 self.run_queue = Queue.Queue()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800163 self.invocations = {}
164 self.tests_to_run = deque()
165 self.visible_test = None
Jon Salz258a40c2012-04-19 12:34:01 +0800166 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800167
168 self.options = None
169 self.args = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800170 self.test_list = None
Jon Salz94eb56f2012-06-12 18:01:12 +0800171 self.on_ui_startup = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800172
Jon Salz258a40c2012-04-19 12:34:01 +0800173 def test_or_root(event):
174 '''Returns the top-level parent for a test (the root node of the
175 tests that need to be run together if the given test path is to
176 be run).'''
177 try:
178 path = event.path
Jon Salzd2ed6cb2012-05-02 09:35:14 +0800179 except AttributeError:
Jon Salz258a40c2012-04-19 12:34:01 +0800180 path = None
181
182 if path:
Jon Salzf617b282012-05-24 14:14:04 +0800183 return (self.test_list.lookup_path(path).
184 get_top_level_parent_or_group())
Jon Salz258a40c2012-04-19 12:34:01 +0800185 else:
186 return self.test_list
187
Jon Salz0405ab52012-03-16 15:26:52 +0800188 self.event_handlers = {
189 Event.Type.SWITCH_TEST: self.handle_switch_test,
Jon Salz968e90b2012-03-18 16:12:43 +0800190 Event.Type.SHOW_NEXT_ACTIVE_TEST:
191 lambda event: self.show_next_active_test(),
192 Event.Type.RESTART_TESTS:
Jon Salz258a40c2012-04-19 12:34:01 +0800193 lambda event: self.restart_tests(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800194 Event.Type.AUTO_RUN:
Jon Salz258a40c2012-04-19 12:34:01 +0800195 lambda event: self.auto_run(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800196 Event.Type.RE_RUN_FAILED:
Jon Salz258a40c2012-04-19 12:34:01 +0800197 lambda event: self.re_run_failed(root=test_or_root(event)),
Jon Salz2eaae372012-06-14 18:11:27 +0800198 Event.Type.RUN_TESTS_WITH_STATUS:
199 lambda event: self.run_tests_with_status(
200 event.status,
201 root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800202 Event.Type.REVIEW:
203 lambda event: self.show_review_information(),
Jon Salz5f2a0672012-05-22 17:14:06 +0800204 Event.Type.UPDATE_SYSTEM_INFO:
205 lambda event: self.update_system_info(),
Jon Salz37eccbd2012-05-25 16:06:52 +0800206 Event.Type.UPDATE_FACTORY:
207 lambda event: self.update_factory(),
Jon Salzf00cdc82012-05-28 18:56:17 +0800208 Event.Type.STOP:
209 lambda event: self.stop(),
Jon Salz0405ab52012-03-16 15:26:52 +0800210 }
211
Jon Salz73e0fd02012-04-04 11:46:38 +0800212 self.exceptions = []
Jon Salz258a40c2012-04-19 12:34:01 +0800213 self.web_socket_manager = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800214
215 def destroy(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800216 if self.chrome:
217 self.chrome.kill()
218 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800219 if self.ui_process:
Jon Salz258a40c2012-04-19 12:34:01 +0800220 utils.kill_process_tree(self.ui_process, 'ui')
Jon Salz73e0fd02012-04-04 11:46:38 +0800221 self.ui_process = None
Jon Salz258a40c2012-04-19 12:34:01 +0800222 if self.web_socket_manager:
223 logging.info('Stopping web sockets')
224 self.web_socket_manager.close()
225 self.web_socket_manager = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800226 if self.state_server_thread:
227 logging.info('Stopping state server')
228 self.state_server.shutdown()
229 self.state_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800230 self.state_server.server_close()
231 self.state_server_thread = None
Jon Salz66f65e62012-05-24 17:40:26 +0800232 if self.state_instance:
233 self.state_instance.close()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800234 if self.event_server_thread:
235 logging.info('Stopping event server')
236 self.event_server.shutdown() # pylint: disable=E1101
237 self.event_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800238 self.event_server.server_close()
239 self.event_server_thread = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800240 if self.prespawner:
241 logging.info('Stopping prespawner')
242 self.prespawner.stop()
243 self.prespawner = None
244 if self.event_client:
Jon Salz258a40c2012-04-19 12:34:01 +0800245 logging.info('Closing event client')
Jon Salz8375c2e2012-04-04 15:22:24 +0800246 self.event_client.close()
Jon Salz258a40c2012-04-19 12:34:01 +0800247 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800248 if self.event_log:
249 self.event_log.Close()
250 self.event_log = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800251 self.check_exceptions()
Jon Salz258a40c2012-04-19 12:34:01 +0800252 logging.info('Done destroying Goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800253
254 def start_state_server(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800255 self.state_instance, self.state_server = (
256 state.create_server(bind_address='0.0.0.0'))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800257 logging.info('Starting state server')
258 self.state_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800259 target=self.state_server.serve_forever,
260 name='StateServer')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800261 self.state_server_thread.start()
262
263 def start_event_server(self):
264 self.event_server = EventServer()
265 logging.info('Starting factory event server')
266 self.event_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800267 target=self.event_server.serve_forever,
268 name='EventServer') # pylint: disable=E1101
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800269 self.event_server_thread.start()
270
271 self.event_client = EventClient(
272 callback=self.handle_event, event_loop=self.run_queue)
273
Jon Salz258a40c2012-04-19 12:34:01 +0800274 self.web_socket_manager = WebSocketManager(self.uuid)
275 self.state_server.add_handler("/event",
276 self.web_socket_manager.handle_web_socket)
277
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800278 def start_ui(self):
Jon Salz6d0f8202012-07-02 14:02:25 +0800279 ui_proc_args = [
280 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
281 self.options.test_list]
Jon Salz14bcbb02012-03-17 15:11:50 +0800282 if self.options.verbose:
283 ui_proc_args.append('-v')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800284 logging.info('Starting ui %s', ui_proc_args)
285 self.ui_process = subprocess.Popen(ui_proc_args)
286 logging.info('Waiting for UI to come up...')
287 self.event_client.wait(
288 lambda event: event.type == Event.Type.UI_READY)
289 logging.info('UI has started')
290
291 def set_visible_test(self, test):
292 if self.visible_test == test:
293 return
294
295 if test:
296 test.update_state(visible=True)
297 if self.visible_test:
298 self.visible_test.update_state(visible=False)
299 self.visible_test = test
300
Jon Salz74ad3262012-03-16 14:40:55 +0800301 def handle_shutdown_complete(self, test, state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800302 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800303 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800304
Jon Salz74ad3262012-03-16 14:40:55 +0800305 @param test: The ShutdownStep.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800306 @param state: The test state.
307 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800308 state = test.update_state(increment_shutdown_count=1)
309 logging.info('Detected shutdown (%d of %d)',
310 state.shutdown_count, test.iterations)
Jon Salz4f6c7172012-06-11 20:45:36 +0800311
312 def log_and_update_state(status, error_msg, **kw):
313 self.event_log.Log('rebooted',
314 status=status, error_msg=error_msg, **kw)
315 test.update_state(status=status, error_msg=error_msg)
316
317 if not self.last_shutdown_time:
318 log_and_update_state(status=TestState.FAILED,
319 error_msg='Unable to read shutdown_time')
320 return
321
322 now = time.time()
323 logging.info('%.03f s passed since reboot',
324 now - self.last_shutdown_time)
325
326 if self.last_shutdown_time > now:
327 test.update_state(status=TestState.FAILED,
328 error_msg='Time moved backward during reboot')
329 elif (isinstance(test, factory.RebootStep) and
330 self.test_list.options.max_reboot_time_secs and
331 (now - self.last_shutdown_time >
332 self.test_list.options.max_reboot_time_secs)):
333 # A reboot took too long; fail. (We don't check this for
334 # HaltSteps, because the machine could be halted for a
335 # very long time, and even unplugged with battery backup,
336 # thus hosing the clock.)
337 log_and_update_state(
338 status=TestState.FAILED,
339 error_msg=('More than %d s elapsed during reboot '
340 '(%.03f s, from %s to %s)' % (
341 self.test_list.options.max_reboot_time_secs,
342 now - self.last_shutdown_time,
343 utils.TimeString(self.last_shutdown_time),
344 utils.TimeString(now))),
345 duration=(now-self.last_shutdown_time))
346 elif state.shutdown_count == test.iterations:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800347 # Good!
Jon Salz4f6c7172012-06-11 20:45:36 +0800348 log_and_update_state(status=TestState.PASSED,
349 duration=(now - self.last_shutdown_time),
350 error_msg='')
Jon Salz74ad3262012-03-16 14:40:55 +0800351 elif state.shutdown_count > test.iterations:
Jon Salz73e0fd02012-04-04 11:46:38 +0800352 # Shut down too many times
Jon Salz4f6c7172012-06-11 20:45:36 +0800353 log_and_update_state(status=TestState.FAILED,
354 error_msg='Too many shutdowns')
Jon Salz258a40c2012-04-19 12:34:01 +0800355 elif utils.are_shift_keys_depressed():
Jon Salz73e0fd02012-04-04 11:46:38 +0800356 logging.info('Shift keys are depressed; cancelling restarts')
357 # Abort shutdown
Jon Salz4f6c7172012-06-11 20:45:36 +0800358 log_and_update_state(
Jon Salz73e0fd02012-04-04 11:46:38 +0800359 status=TestState.FAILED,
360 error_msg='Shutdown aborted with double shift keys')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800361 else:
Jon Salz94eb56f2012-06-12 18:01:12 +0800362 def handler():
363 if self._prompt_cancel_shutdown(test, state.shutdown_count + 1):
364 log_and_update_state(
365 status=TestState.FAILED,
366 error_msg='Shutdown aborted by operator')
367 return
368
369 # Time to shutdown again
370 log_and_update_state(
371 status=TestState.ACTIVE,
372 error_msg='',
373 iteration=state.shutdown_count)
374
375 self.event_log.Log('shutdown', operation='reboot')
376 self.state_instance.set_shared_data('shutdown_time',
Jon Salz4f6c7172012-06-11 20:45:36 +0800377 time.time())
Jon Salz94eb56f2012-06-12 18:01:12 +0800378 self.env.shutdown('reboot')
379
380 self.on_ui_startup.append(handler)
381
382 def _prompt_cancel_shutdown(self, test, iteration):
383 if self.options.ui != 'chrome':
384 return False
385
386 pending_shutdown_data = {
387 'delay_secs': test.delay_secs,
388 'time': time.time() + test.delay_secs,
389 'operation': test.operation,
390 'iteration': iteration,
391 'iterations': test.iterations,
392 }
393
394 # Create a new (threaded) event client since we
395 # don't want to use the event loop for this.
396 with EventClient() as event_client:
397 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
398 **pending_shutdown_data))
399 aborted = event_client.wait(
400 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
401 timeout=test.delay_secs) is not None
402 if aborted:
403 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
404 return aborted
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800405
406 def init_states(self):
407 '''
408 Initializes all states on startup.
409 '''
410 for test in self.test_list.get_all_tests():
411 # Make sure the state server knows about all the tests,
412 # defaulting to an untested state.
413 test.update_state(update_parent=False, visible=False)
414
Jon Salzd6361c22012-06-11 22:23:57 +0800415 var_log_messages = None
416
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800417 # Any 'active' tests should be marked as failed now.
418 for test in self.test_list.walk():
419 state = test.get_state()
Hung-Te Lin96632362012-03-20 21:14:18 +0800420 if state.status != TestState.ACTIVE:
421 continue
422 if isinstance(test, factory.ShutdownStep):
423 # Shutdown while the test was active - that's good.
424 self.handle_shutdown_complete(test, state)
425 else:
Jon Salzd6361c22012-06-11 22:23:57 +0800426 # Unexpected shutdown. Grab /var/log/messages for context.
427 if var_log_messages is None:
428 try:
429 var_log_messages = (
430 utils.var_log_messages_before_reboot())
431 # Write it to the log, to make it easier to
432 # correlate with /var/log/messages.
433 logging.info(
434 'Unexpected shutdown. '
435 'Tail of /var/log/messages before last reboot:\n'
436 '%s', ('\n'.join(
437 ' ' + x for x in var_log_messages)))
438 except:
439 logging.exception('Unable to grok /var/log/messages')
440 var_log_messages = []
441
442 error_msg = 'Unexpected shutdown while test was running'
443 self.event_log.Log('end_test',
444 path=test.path,
445 status=TestState.FAILED,
446 invocation=test.get_state().invocation,
447 error_msg=error_msg,
448 var_log_messages='\n'.join(var_log_messages))
449 test.update_state(
450 status=TestState.FAILED,
451 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800452
453 def show_next_active_test(self):
454 '''
455 Rotates to the next visible active test.
456 '''
457 self.reap_completed_tests()
458 active_tests = [
459 t for t in self.test_list.walk()
460 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
461 if not active_tests:
462 return
463
464 try:
465 next_test = active_tests[
466 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
467 except ValueError: # visible_test not present in active_tests
468 next_test = active_tests[0]
469
470 self.set_visible_test(next_test)
471
472 def handle_event(self, event):
473 '''
474 Handles an event from the event server.
475 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800476 handler = self.event_handlers.get(event.type)
477 if handler:
Jon Salz968e90b2012-03-18 16:12:43 +0800478 handler(event)
Jon Salz0405ab52012-03-16 15:26:52 +0800479 else:
Jon Salz968e90b2012-03-18 16:12:43 +0800480 # We don't register handlers for all event types - just ignore
481 # this event.
482 logging.debug('Unbound event type %s', event.type)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800483
484 def run_next_test(self):
485 '''
486 Runs the next eligible test (or tests) in self.tests_to_run.
487 '''
488 self.reap_completed_tests()
489 while self.tests_to_run:
490 logging.debug('Tests to run: %s',
491 [x.path for x in self.tests_to_run])
492
493 test = self.tests_to_run[0]
494
495 if test in self.invocations:
496 logging.info('Next test %s is already running', test.path)
497 self.tests_to_run.popleft()
498 return
499
500 if self.invocations and not (test.backgroundable and all(
501 [x.backgroundable for x in self.invocations])):
502 logging.debug('Waiting for non-backgroundable tests to '
503 'complete before running %s', test.path)
504 return
505
506 self.tests_to_run.popleft()
507
Jon Salz74ad3262012-03-16 14:40:55 +0800508 if isinstance(test, factory.ShutdownStep):
Jon Salz8796e362012-05-24 11:39:09 +0800509 if os.path.exists(NO_REBOOT_FILE):
510 test.update_state(
511 status=TestState.FAILED, increment_count=1,
512 error_msg=('Skipped shutdown since %s is present' %
513 NO_REBOOT_FILE))
514 continue
515
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800516 test.update_state(status=TestState.ACTIVE, increment_count=1,
Jon Salz74ad3262012-03-16 14:40:55 +0800517 error_msg='', shutdown_count=0)
Jon Salz94eb56f2012-06-12 18:01:12 +0800518 if self._prompt_cancel_shutdown(test, 1):
519 self.event_log.Log('reboot_cancelled')
520 test.update_state(
521 status=TestState.FAILED, increment_count=1,
522 error_msg='Shutdown aborted by operator',
523 shutdown_count=0)
524 return
525
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800526 # Save pending test list in the state server
527 self.state_instance.set_shared_data(
Jon Salz74ad3262012-03-16 14:40:55 +0800528 'tests_after_shutdown',
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800529 [t.path for t in self.tests_to_run])
Jon Salz4f6c7172012-06-11 20:45:36 +0800530 # Save shutdown time
531 self.state_instance.set_shared_data('shutdown_time',
532 time.time())
Jon Salz74ad3262012-03-16 14:40:55 +0800533
Jon Salz73e0fd02012-04-04 11:46:38 +0800534 with self.env.lock:
Jon Salzb9038572012-05-24 10:34:51 +0800535 self.event_log.Log('shutdown', operation=test.operation)
Jon Salz73e0fd02012-04-04 11:46:38 +0800536 shutdown_result = self.env.shutdown(test.operation)
537 if shutdown_result:
538 # That's all, folks!
539 self.run_queue.put(None)
540 return
541 else:
542 # Just pass (e.g., in the chroot).
543 test.update_state(status=TestState.PASSED)
544 self.state_instance.set_shared_data(
545 'tests_after_shutdown', None)
Jon Salz94eb56f2012-06-12 18:01:12 +0800546 # Send event with no fields to indicate that there is no
547 # longer a pending shutdown.
548 self.event_client.post_event(Event(
549 Event.Type.PENDING_SHUTDOWN))
Jon Salz73e0fd02012-04-04 11:46:38 +0800550 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800551
552 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
553 self.invocations[test] = invoc
554 if self.visible_test is None and test.has_ui:
555 self.set_visible_test(test)
Jon Salz7c15e8b2012-06-19 17:10:37 +0800556 self.check_connection_manager()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800557 invoc.start()
558
Jon Salz7c15e8b2012-06-19 17:10:37 +0800559 def check_connection_manager(self):
560 exclusive_tests = [
561 test.path
562 for test in self.invocations
563 if test.is_exclusive(
564 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
565 if exclusive_tests:
566 # Make sure networking is disabled.
567 if self.network_enabled:
568 logging.info('Disabling network, as requested by %s',
569 exclusive_tests)
570 self.connection_manager.DisableNetworking()
571 self.network_enabled = False
572 else:
573 # Make sure networking is enabled.
574 if not self.network_enabled:
575 logging.info('Re-enabling network')
576 self.connection_manager.EnableNetworking()
577 self.network_enabled = True
578
Jon Salz0405ab52012-03-16 15:26:52 +0800579 def run_tests(self, subtrees, untested_only=False):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800580 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800581 Runs tests under subtree.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800582
583 The tests are run in order unless one fails (then stops).
584 Backgroundable tests are run simultaneously; when a foreground test is
585 encountered, we wait for all active tests to finish before continuing.
Jon Salz0405ab52012-03-16 15:26:52 +0800586
587 @param subtrees: Node or nodes containing tests to run (may either be
588 a single test or a list). Duplicates will be ignored.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800589 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800590 if type(subtrees) != list:
591 subtrees = [subtrees]
592
593 # Nodes we've seen so far, to avoid duplicates.
594 seen = set()
595
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800596 self.tests_to_run = deque()
Jon Salz0405ab52012-03-16 15:26:52 +0800597 for subtree in subtrees:
598 for test in subtree.walk():
599 if test in seen:
600 continue
601 seen.add(test)
602
603 if not test.is_leaf():
604 continue
605 if (untested_only and
606 test.get_state().status != TestState.UNTESTED):
607 continue
608 self.tests_to_run.append(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800609 self.run_next_test()
610
611 def reap_completed_tests(self):
612 '''
613 Removes completed tests from the set of active tests.
614
615 Also updates the visible test if it was reaped.
616 '''
617 for t, v in dict(self.invocations).iteritems():
618 if v.is_completed():
619 del self.invocations[t]
620
621 if (self.visible_test is None or
622 self.visible_test not in self.invocations):
623 self.set_visible_test(None)
624 # Make the first running test, if any, the visible test
625 for t in self.test_list.walk():
626 if t in self.invocations:
627 self.set_visible_test(t)
628 break
629
Hung-Te Lin96632362012-03-20 21:14:18 +0800630 def kill_active_tests(self, abort):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800631 '''
632 Kills and waits for all active tests.
Hung-Te Lin96632362012-03-20 21:14:18 +0800633
634 @param abort: True to change state of killed tests to FAILED, False for
635 UNTESTED.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800636 '''
637 self.reap_completed_tests()
638 for test, invoc in self.invocations.items():
639 factory.console.info('Killing active test %s...' % test.path)
640 invoc.abort_and_join()
641 factory.console.info('Killed %s' % test.path)
642 del self.invocations[test]
Hung-Te Lin96632362012-03-20 21:14:18 +0800643 if not abort:
644 test.update_state(status=TestState.UNTESTED)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800645 self.reap_completed_tests()
646
Jon Salzf00cdc82012-05-28 18:56:17 +0800647 def stop(self):
648 self.kill_active_tests(False)
649 self.run_tests([])
650
Hung-Te Lin96632362012-03-20 21:14:18 +0800651 def abort_active_tests(self):
652 self.kill_active_tests(True)
653
Jon Salz73e0fd02012-04-04 11:46:38 +0800654 def main(self):
Jon Salzeb8d25f2012-05-22 15:17:32 +0800655 try:
656 self.init()
Jon Salzb9038572012-05-24 10:34:51 +0800657 self.event_log.Log('goofy_init',
658 success=True)
Jon Salzeb8d25f2012-05-22 15:17:32 +0800659 except:
660 if self.event_log:
661 try:
Jon Salzb9038572012-05-24 10:34:51 +0800662 self.event_log.Log('goofy_init',
663 success=False,
664 trace=traceback.format_exc())
Jon Salzeb8d25f2012-05-22 15:17:32 +0800665 except:
666 pass
667 raise
668
Jon Salz73e0fd02012-04-04 11:46:38 +0800669 self.run()
670
Jon Salz5f2a0672012-05-22 17:14:06 +0800671 def update_system_info(self):
672 '''Updates system info.'''
Jon Salz49a7d152012-06-19 15:04:09 +0800673 system_info = system.SystemInfo()
Jon Salz5f2a0672012-05-22 17:14:06 +0800674 self.state_instance.set_shared_data('system_info', system_info.__dict__)
675 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
676 system_info=system_info.__dict__))
677 logging.info('System info: %r', system_info.__dict__)
678
Jon Salz37eccbd2012-05-25 16:06:52 +0800679 def update_factory(self):
680 self.kill_active_tests(False)
681 self.run_tests([])
682
683 try:
684 if updater.TryUpdate(pre_update_hook=self.state_instance.close):
685 self.env.shutdown('reboot')
686 except:
687 factory.console.exception('Unable to update')
688
Jon Salz73e0fd02012-04-04 11:46:38 +0800689 def init(self, args=None, env=None):
690 '''Initializes Goofy.
Jon Salz74ad3262012-03-16 14:40:55 +0800691
692 Args:
Jon Salz73e0fd02012-04-04 11:46:38 +0800693 args: A list of command-line arguments. Uses sys.argv if
694 args is None.
695 env: An Environment instance to use (or None to choose
Jon Salz258a40c2012-04-19 12:34:01 +0800696 FakeChrootEnvironment or DUTEnvironment as appropriate).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800697 '''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800698 parser = OptionParser()
699 parser.add_option('-v', '--verbose', dest='verbose',
700 action='store_true',
701 help='Enable debug logging')
702 parser.add_option('--print_test_list', dest='print_test_list',
703 metavar='FILE',
704 help='Read and print test list FILE, and exit')
Jon Salz758e6cc2012-04-03 15:47:07 +0800705 parser.add_option('--restart', dest='restart',
706 action='store_true',
707 help='Clear all test state')
Jon Salz258a40c2012-04-19 12:34:01 +0800708 parser.add_option('--ui', dest='ui', type='choice',
709 choices=['none', 'gtk', 'chrome'],
710 default='gtk',
711 help='UI to use')
Jon Salz63585ea2012-05-21 15:03:32 +0800712 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
713 type='int', default=1,
714 help=('Factor by which to scale UI '
715 '(Chrome UI only)'))
Jon Salz73e0fd02012-04-04 11:46:38 +0800716 parser.add_option('--test_list', dest='test_list',
717 metavar='FILE',
718 help='Use FILE as test list')
719 (self.options, self.args) = parser.parse_args(args)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800720
Jon Salz73e0fd02012-04-04 11:46:38 +0800721 global _inited_logging
722 if not _inited_logging:
723 factory.init_logging('goofy', verbose=self.options.verbose)
724 _inited_logging = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800725 self.event_log = EventLog('goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800726
Jon Salz73e0fd02012-04-04 11:46:38 +0800727 if (not suppress_chroot_warning and
728 factory.in_chroot() and
Jon Salz258a40c2012-04-19 12:34:01 +0800729 self.options.ui == 'gtk' and
Jon Salz758e6cc2012-04-03 15:47:07 +0800730 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
731 # That's not going to work! Tell the user how to run
732 # this way.
733 logging.warn(GOOFY_IN_CHROOT_WARNING)
734 time.sleep(1)
735
Jon Salz73e0fd02012-04-04 11:46:38 +0800736 if env:
737 self.env = env
738 elif factory.in_chroot():
Jon Salz5f2a0672012-05-22 17:14:06 +0800739 self.env = test_environment.FakeChrootEnvironment()
Jon Salz73e0fd02012-04-04 11:46:38 +0800740 logging.warn(
741 'Using chroot environment: will not actually run autotests')
742 else:
Jon Salz5f2a0672012-05-22 17:14:06 +0800743 self.env = test_environment.DUTEnvironment()
Jon Salz323dd3d2012-04-09 18:40:43 +0800744 self.env.goofy = self
Jon Salz73e0fd02012-04-04 11:46:38 +0800745
Jon Salz758e6cc2012-04-03 15:47:07 +0800746 if self.options.restart:
747 state.clear_state()
748
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800749 if self.options.print_test_list:
750 print (factory.read_test_list(self.options.print_test_list).
751 __repr__(recursive=True))
752 return
753
Jon Salz7c15e8b2012-06-19 17:10:37 +0800754 if self.options.ui_scale_factor != 1 and utils.in_qemu():
Jon Salz9b312912012-06-04 11:27:00 +0800755 logging.warn(
756 'In QEMU; ignoring ui_scale_factor argument')
757 self.options.ui_scale_factor = 1
758
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800759 logging.info('Started')
760
761 self.start_state_server()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800762 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz63585ea2012-05-21 15:03:32 +0800763 self.state_instance.set_shared_data('ui_scale_factor',
764 self.options.ui_scale_factor)
Jon Salz4f6c7172012-06-11 20:45:36 +0800765 self.last_shutdown_time = (
766 self.state_instance.get_shared_data('shutdown_time', optional=True))
767 self.state_instance.del_shared_data('shutdown_time', optional=True)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800768
Jon Salz2f757d42012-06-27 17:06:42 +0800769 if not self.options.test_list:
770 self.options.test_list = find_test_list()
771 if not self.options.test_list:
772 logging.error('No test list. Aborting.')
773 sys.exit(1)
774 logging.info('Using test list %s', self.options.test_list)
775
Jon Salz73e0fd02012-04-04 11:46:38 +0800776 self.test_list = factory.read_test_list(self.options.test_list,
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800777 self.state_instance)
Jon Salz06fbeff2012-05-21 17:06:05 +0800778 if not self.state_instance.has_shared_data('ui_lang'):
779 self.state_instance.set_shared_data('ui_lang',
780 self.test_list.options.ui_lang)
Jon Salzdbf398f2012-06-14 17:30:01 +0800781 self.state_instance.set_shared_data(
782 'test_list_options',
783 self.test_list.options.__dict__)
Jon Salz258a40c2012-04-19 12:34:01 +0800784 self.state_instance.test_list = self.test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800785
786 self.init_states()
787 self.start_event_server()
Jon Salz7c15e8b2012-06-19 17:10:37 +0800788 self.connection_manager = self.env.create_connection_manager(
789 self.test_list.options.wlans)
Jon Salz258a40c2012-04-19 12:34:01 +0800790
Jon Salz5f2a0672012-05-22 17:14:06 +0800791 self.update_system_info()
792
Jon Salz5da61e62012-05-31 13:06:22 +0800793 os.environ['CROS_FACTORY'] = '1'
Jon Salzeebd12e2012-06-08 15:34:56 +0800794 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
Jon Salz5da61e62012-05-31 13:06:22 +0800795
Jon Salzb1b39092012-05-03 02:05:09 +0800796 # Set CROS_UI since some behaviors in ui.py depend on the
797 # particular UI in use. TODO(jsalz): Remove this (and all
798 # places it is used) when the GTK UI is removed.
799 os.environ['CROS_UI'] = self.options.ui
Jon Salz258a40c2012-04-19 12:34:01 +0800800
Jon Salzb1b39092012-05-03 02:05:09 +0800801 if self.options.ui == 'chrome':
Jon Salz258a40c2012-04-19 12:34:01 +0800802 self.env.launch_chrome()
803 logging.info('Waiting for a web socket connection')
804 self.web_socket_manager.wait()
Jon Salzb1b39092012-05-03 02:05:09 +0800805
806 # Wait for the test widget size to be set; this is done in
807 # an asynchronous RPC so there is a small chance that the
808 # web socket might be opened first.
809 for i in range(100): # 10 s
Jon Salz63585ea2012-05-21 15:03:32 +0800810 try:
811 if self.state_instance.get_shared_data('test_widget_size'):
812 break
813 except KeyError:
814 pass # Retry
Jon Salzb1b39092012-05-03 02:05:09 +0800815 time.sleep(0.1) # 100 ms
816 else:
817 logging.warn('Never received test_widget_size from UI')
Jon Salz258a40c2012-04-19 12:34:01 +0800818 elif self.options.ui == 'gtk':
Jon Salz73e0fd02012-04-04 11:46:38 +0800819 self.start_ui()
Jon Salz258a40c2012-04-19 12:34:01 +0800820
Jon Salz94eb56f2012-06-12 18:01:12 +0800821 for handler in self.on_ui_startup:
822 handler()
823
Jon Salz8375c2e2012-04-04 15:22:24 +0800824 self.prespawner = Prespawner()
Jon Salz323dd3d2012-04-09 18:40:43 +0800825 self.prespawner.start()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800826
827 def state_change_callback(test, state):
828 self.event_client.post_event(
829 Event(Event.Type.STATE_CHANGE,
830 path=test.path, state=state))
831 self.test_list.state_change_callback = state_change_callback
832
833 try:
Jon Salz758e6cc2012-04-03 15:47:07 +0800834 tests_after_shutdown = self.state_instance.get_shared_data(
835 'tests_after_shutdown')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800836 except KeyError:
Jon Salz758e6cc2012-04-03 15:47:07 +0800837 tests_after_shutdown = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800838
Jon Salz758e6cc2012-04-03 15:47:07 +0800839 if tests_after_shutdown is not None:
840 logging.info('Resuming tests after shutdown: %s',
841 tests_after_shutdown)
842 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800843 self.tests_to_run.extend(
Jon Salz758e6cc2012-04-03 15:47:07 +0800844 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Jon Salz73e0fd02012-04-04 11:46:38 +0800845 self.run_queue.put(self.run_next_test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800846 else:
Jon Salz57717ca2012-04-04 16:47:25 +0800847 if self.test_list.options.auto_run_on_start:
848 self.run_queue.put(
849 lambda: self.run_tests(self.test_list, untested_only=True))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800850
Jon Salz73e0fd02012-04-04 11:46:38 +0800851 def run(self):
852 '''Runs Goofy.'''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800853 # Process events forever.
Jon Salz57717ca2012-04-04 16:47:25 +0800854 while self.run_once(True):
Jon Salz73e0fd02012-04-04 11:46:38 +0800855 pass
856
Jon Salz57717ca2012-04-04 16:47:25 +0800857 def run_once(self, block=False):
Jon Salz73e0fd02012-04-04 11:46:38 +0800858 '''Runs all items pending in the event loop.
859
Jon Salz57717ca2012-04-04 16:47:25 +0800860 Args:
861 block: If true, block until at least one event is processed.
862
Jon Salz73e0fd02012-04-04 11:46:38 +0800863 Returns:
864 True to keep going or False to shut down.
865 '''
Jon Salz7c15e8b2012-06-19 17:10:37 +0800866 events = utils.DrainQueue(self.run_queue)
867 if not events:
868 # Nothing on the run queue.
869 self._run_queue_idle()
870 if block:
871 # Block for at least one event...
872 events.append(self.run_queue.get())
873 # ...and grab anything else that showed up at the same
874 # time.
875 events.extend(utils.DrainQueue(self.run_queue))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800876
Jon Salz73e0fd02012-04-04 11:46:38 +0800877 for event in events:
878 if not event:
879 # Shutdown request.
880 self.run_queue.task_done()
881 return False
882
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800883 try:
884 event()
885 except Exception as e: # pylint: disable=W0703
886 logging.error('Error in event loop: %s', e)
887 traceback.print_exc(sys.stderr)
Jon Salz8375c2e2012-04-04 15:22:24 +0800888 self.record_exception(traceback.format_exception_only(
889 *sys.exc_info()[:2]))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800890 # But keep going
891 finally:
892 self.run_queue.task_done()
Jon Salz73e0fd02012-04-04 11:46:38 +0800893 return True
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800894
Jon Salz7c15e8b2012-06-19 17:10:37 +0800895 def _run_queue_idle(self):
896 '''Invoked when the run queue has no events.'''
897 self.check_connection_manager()
898
Jon Salz258a40c2012-04-19 12:34:01 +0800899 def run_tests_with_status(self, statuses_to_run, starting_at=None,
900 root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800901 '''Runs all top-level tests with a particular status.
902
903 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +0800904
905 Args:
906 starting_at: If provided, only auto-runs tests beginning with
907 this test.
Jon Salz0405ab52012-03-16 15:26:52 +0800908 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800909 root = root or self.test_list
910
Jon Salz57717ca2012-04-04 16:47:25 +0800911 if starting_at:
912 # Make sure they passed a test, not a string.
913 assert isinstance(starting_at, factory.FactoryTest)
914
Jon Salz0405ab52012-03-16 15:26:52 +0800915 tests_to_reset = []
916 tests_to_run = []
917
Jon Salz57717ca2012-04-04 16:47:25 +0800918 found_starting_at = False
919
Jon Salz258a40c2012-04-19 12:34:01 +0800920 for test in root.get_top_level_tests():
Jon Salz57717ca2012-04-04 16:47:25 +0800921 if starting_at:
922 if test == starting_at:
923 # We've found starting_at; do auto-run on all
924 # subsequent tests.
925 found_starting_at = True
926 if not found_starting_at:
927 # Don't start this guy yet
928 continue
929
Jon Salz0405ab52012-03-16 15:26:52 +0800930 status = test.get_state().status
931 if status == TestState.ACTIVE or status in statuses_to_run:
932 # Reset the test (later; we will need to abort
933 # all active tests first).
934 tests_to_reset.append(test)
935 if status in statuses_to_run:
936 tests_to_run.append(test)
937
938 self.abort_active_tests()
939
940 # Reset all statuses of the tests to run (in case any tests were active;
941 # we want them to be run again).
942 for test_to_reset in tests_to_reset:
943 for test in test_to_reset.walk():
944 test.update_state(status=TestState.UNTESTED)
945
946 self.run_tests(tests_to_run, untested_only=True)
947
Jon Salz258a40c2012-04-19 12:34:01 +0800948 def restart_tests(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800949 '''Restarts all tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800950 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +0800951
Jon Salz258a40c2012-04-19 12:34:01 +0800952 self.abort_active_tests()
953 for test in root.walk():
954 test.update_state(status=TestState.UNTESTED)
955 self.run_tests(root)
956
957 def auto_run(self, starting_at=None, root=None):
Jon Salz57717ca2012-04-04 16:47:25 +0800958 '''"Auto-runs" tests that have not been run yet.
959
960 Args:
961 starting_at: If provide, only auto-runs tests beginning with
962 this test.
963 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800964 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +0800965 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Jon Salz258a40c2012-04-19 12:34:01 +0800966 starting_at=starting_at,
967 root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800968
Jon Salz258a40c2012-04-19 12:34:01 +0800969 def re_run_failed(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800970 '''Re-runs failed tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800971 root = root or self.test_list
972 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800973
Jon Salz968e90b2012-03-18 16:12:43 +0800974 def show_review_information(self):
Hung-Te Lin96632362012-03-20 21:14:18 +0800975 '''Event handler for showing review information screen.
976
977 The information screene is rendered by main UI program (ui.py), so in
978 goofy we only need to kill all active tests, set them as untested, and
979 clear remaining tests.
980 '''
981 self.kill_active_tests(False)
982 self.run_tests([])
983
Jon Salz0405ab52012-03-16 15:26:52 +0800984 def handle_switch_test(self, event):
Jon Salz968e90b2012-03-18 16:12:43 +0800985 '''Switches to a particular test.
986
987 @param event: The SWITCH_TEST event.
988 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800989 test = self.test_list.lookup_path(event.path)
Jon Salz57717ca2012-04-04 16:47:25 +0800990 if not test:
Jon Salz968e90b2012-03-18 16:12:43 +0800991 logging.error('Unknown test %r', event.key)
Jon Salz57717ca2012-04-04 16:47:25 +0800992 return
993
994 invoc = self.invocations.get(test)
995 if invoc and test.backgroundable:
996 # Already running: just bring to the front if it
997 # has a UI.
998 logging.info('Setting visible test to %s', test.path)
999 self.event_client.post_event(
1000 Event(Event.Type.SET_VISIBLE_TEST, path=test.path))
1001 return
1002
1003 self.abort_active_tests()
1004 for t in test.walk():
1005 t.update_state(status=TestState.UNTESTED)
1006
1007 if self.test_list.options.auto_run_on_keypress:
1008 self.auto_run(starting_at=test)
1009 else:
1010 self.run_tests(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001011
Jon Salz73e0fd02012-04-04 11:46:38 +08001012 def wait(self):
1013 '''Waits for all pending invocations.
1014
1015 Useful for testing.
1016 '''
1017 for k, v in self.invocations.iteritems():
1018 logging.info('Waiting for %s to complete...', k)
1019 v.thread.join()
1020
1021 def check_exceptions(self):
1022 '''Raises an error if any exceptions have occurred in
1023 invocation threads.'''
1024 if self.exceptions:
1025 raise RuntimeError('Exception in invocation thread: %r' %
1026 self.exceptions)
1027
1028 def record_exception(self, msg):
1029 '''Records an exception in an invocation thread.
1030
1031 An exception with the given message will be rethrown when
1032 Goofy is destroyed.'''
1033 self.exceptions.append(msg)
1034
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001035
1036if __name__ == '__main__':
1037 Goofy().main()