blob: 983fd2d944cd9fe19d56ca7cfe683605b471fe39 [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
Hung-Te Linf2f78f72012-02-08 19:27:11 +080052DEFAULT_TEST_LIST_PATH = os.path.join(
Jon Salzf3b9d462012-06-26 12:35:44 +080053 factory.FACTORY_PATH , 'test_lists', 'test_list')
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
95 # Try in order: test_list, test_list.$hwid_cfg, test_list.all
96 if hwid_cfg:
97 test_list = '%s_%s' % (DEFAULT_TEST_LIST_PATH, hwid_cfg)
98 if os.path.exists(test_list):
99 logging.info('Using special test list: %s', test_list)
100 return test_list
101 logging.info('WARNING: no specific test list for config: %s', hwid_cfg)
102
103 test_list = DEFAULT_TEST_LIST_PATH
104 if os.path.exists(test_list):
105 return test_list
106
107 test_list = ('%s.all' % DEFAULT_TEST_LIST_PATH)
108 if os.path.exists(test_list):
109 logging.info('Using default test list: ' + test_list)
110 return test_list
111 logging.info('ERROR: Cannot find any test list.')
112
Jon Salz73e0fd02012-04-04 11:46:38 +0800113
Jon Salz73e0fd02012-04-04 11:46:38 +0800114_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800115
116class Goofy(object):
117 '''
118 The main factory flow.
119
120 Note that all methods in this class must be invoked from the main
121 (event) thread. Other threads, such as callbacks and TestInvocation
122 methods, should instead post events on the run queue.
123
124 TODO: Unit tests. (chrome-os-partner:7409)
125
126 Properties:
Jon Salz258a40c2012-04-19 12:34:01 +0800127 uuid: A unique UUID for this invocation of Goofy.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800128 state_instance: An instance of FactoryState.
129 state_server: The FactoryState XML/RPC server.
130 state_server_thread: A thread running state_server.
131 event_server: The EventServer socket server.
132 event_server_thread: A thread running event_server.
133 event_client: A client to the event server.
Jon Salz7c15e8b2012-06-19 17:10:37 +0800134 connection_manager: The connection_manager object.
135 network_enabled: Whether the connection_manager is currently
136 enabling connections.
Hung-Te Lin6bb48552012-02-09 14:37:43 +0800137 ui_process: The factory ui process object.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800138 run_queue: A queue of callbacks to invoke from the main thread.
139 invocations: A map from FactoryTest objects to the corresponding
140 TestInvocations objects representing active tests.
141 tests_to_run: A deque of tests that should be run when the current
142 test(s) complete.
143 options: Command-line options.
144 args: Command-line args.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800145 test_list: The test list.
Jon Salz0405ab52012-03-16 15:26:52 +0800146 event_handlers: Map of Event.Type to the method used to handle that
147 event. If the method has an 'event' argument, the event is passed
148 to the handler.
Jon Salz73e0fd02012-04-04 11:46:38 +0800149 exceptions: Exceptions encountered in invocation threads.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800150 '''
151 def __init__(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800152 self.uuid = str(uuid.uuid4())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800153 self.state_instance = None
154 self.state_server = None
155 self.state_server_thread = None
156 self.event_server = None
157 self.event_server_thread = None
158 self.event_client = None
Jon Salz7c15e8b2012-06-19 17:10:37 +0800159 self.connection_manager = None
160 self.network_enabled = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800161 self.event_log = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800162 self.prespawner = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800163 self.ui_process = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800164 self.run_queue = Queue.Queue()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800165 self.invocations = {}
166 self.tests_to_run = deque()
167 self.visible_test = None
Jon Salz258a40c2012-04-19 12:34:01 +0800168 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800169
170 self.options = None
171 self.args = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800172 self.test_list = None
Jon Salz94eb56f2012-06-12 18:01:12 +0800173 self.on_ui_startup = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800174
Jon Salz258a40c2012-04-19 12:34:01 +0800175 def test_or_root(event):
176 '''Returns the top-level parent for a test (the root node of the
177 tests that need to be run together if the given test path is to
178 be run).'''
179 try:
180 path = event.path
Jon Salzd2ed6cb2012-05-02 09:35:14 +0800181 except AttributeError:
Jon Salz258a40c2012-04-19 12:34:01 +0800182 path = None
183
184 if path:
Jon Salzf617b282012-05-24 14:14:04 +0800185 return (self.test_list.lookup_path(path).
186 get_top_level_parent_or_group())
Jon Salz258a40c2012-04-19 12:34:01 +0800187 else:
188 return self.test_list
189
Jon Salz0405ab52012-03-16 15:26:52 +0800190 self.event_handlers = {
191 Event.Type.SWITCH_TEST: self.handle_switch_test,
Jon Salz968e90b2012-03-18 16:12:43 +0800192 Event.Type.SHOW_NEXT_ACTIVE_TEST:
193 lambda event: self.show_next_active_test(),
194 Event.Type.RESTART_TESTS:
Jon Salz258a40c2012-04-19 12:34:01 +0800195 lambda event: self.restart_tests(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800196 Event.Type.AUTO_RUN:
Jon Salz258a40c2012-04-19 12:34:01 +0800197 lambda event: self.auto_run(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800198 Event.Type.RE_RUN_FAILED:
Jon Salz258a40c2012-04-19 12:34:01 +0800199 lambda event: self.re_run_failed(root=test_or_root(event)),
Jon Salz2eaae372012-06-14 18:11:27 +0800200 Event.Type.RUN_TESTS_WITH_STATUS:
201 lambda event: self.run_tests_with_status(
202 event.status,
203 root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800204 Event.Type.REVIEW:
205 lambda event: self.show_review_information(),
Jon Salz5f2a0672012-05-22 17:14:06 +0800206 Event.Type.UPDATE_SYSTEM_INFO:
207 lambda event: self.update_system_info(),
Jon Salz37eccbd2012-05-25 16:06:52 +0800208 Event.Type.UPDATE_FACTORY:
209 lambda event: self.update_factory(),
Jon Salzf00cdc82012-05-28 18:56:17 +0800210 Event.Type.STOP:
211 lambda event: self.stop(),
Jon Salz0405ab52012-03-16 15:26:52 +0800212 }
213
Jon Salz73e0fd02012-04-04 11:46:38 +0800214 self.exceptions = []
Jon Salz258a40c2012-04-19 12:34:01 +0800215 self.web_socket_manager = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800216
217 def destroy(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800218 if self.chrome:
219 self.chrome.kill()
220 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800221 if self.ui_process:
Jon Salz258a40c2012-04-19 12:34:01 +0800222 utils.kill_process_tree(self.ui_process, 'ui')
Jon Salz73e0fd02012-04-04 11:46:38 +0800223 self.ui_process = None
Jon Salz258a40c2012-04-19 12:34:01 +0800224 if self.web_socket_manager:
225 logging.info('Stopping web sockets')
226 self.web_socket_manager.close()
227 self.web_socket_manager = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800228 if self.state_server_thread:
229 logging.info('Stopping state server')
230 self.state_server.shutdown()
231 self.state_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800232 self.state_server.server_close()
233 self.state_server_thread = None
Jon Salz66f65e62012-05-24 17:40:26 +0800234 if self.state_instance:
235 self.state_instance.close()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800236 if self.event_server_thread:
237 logging.info('Stopping event server')
238 self.event_server.shutdown() # pylint: disable=E1101
239 self.event_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800240 self.event_server.server_close()
241 self.event_server_thread = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800242 if self.prespawner:
243 logging.info('Stopping prespawner')
244 self.prespawner.stop()
245 self.prespawner = None
246 if self.event_client:
Jon Salz258a40c2012-04-19 12:34:01 +0800247 logging.info('Closing event client')
Jon Salz8375c2e2012-04-04 15:22:24 +0800248 self.event_client.close()
Jon Salz258a40c2012-04-19 12:34:01 +0800249 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800250 if self.event_log:
251 self.event_log.Close()
252 self.event_log = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800253 self.check_exceptions()
Jon Salz258a40c2012-04-19 12:34:01 +0800254 logging.info('Done destroying Goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800255
256 def start_state_server(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800257 self.state_instance, self.state_server = (
258 state.create_server(bind_address='0.0.0.0'))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800259 logging.info('Starting state server')
260 self.state_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800261 target=self.state_server.serve_forever,
262 name='StateServer')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800263 self.state_server_thread.start()
264
265 def start_event_server(self):
266 self.event_server = EventServer()
267 logging.info('Starting factory event server')
268 self.event_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800269 target=self.event_server.serve_forever,
270 name='EventServer') # pylint: disable=E1101
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800271 self.event_server_thread.start()
272
273 self.event_client = EventClient(
274 callback=self.handle_event, event_loop=self.run_queue)
275
Jon Salz258a40c2012-04-19 12:34:01 +0800276 self.web_socket_manager = WebSocketManager(self.uuid)
277 self.state_server.add_handler("/event",
278 self.web_socket_manager.handle_web_socket)
279
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800280 def start_ui(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800281 ui_proc_args = [os.path.join(factory.CROS_FACTORY_LIB_PATH, 'ui'),
282 self.options.test_list]
Jon Salz14bcbb02012-03-17 15:11:50 +0800283 if self.options.verbose:
284 ui_proc_args.append('-v')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800285 logging.info('Starting ui %s', ui_proc_args)
286 self.ui_process = subprocess.Popen(ui_proc_args)
287 logging.info('Waiting for UI to come up...')
288 self.event_client.wait(
289 lambda event: event.type == Event.Type.UI_READY)
290 logging.info('UI has started')
291
292 def set_visible_test(self, test):
293 if self.visible_test == test:
294 return
295
296 if test:
297 test.update_state(visible=True)
298 if self.visible_test:
299 self.visible_test.update_state(visible=False)
300 self.visible_test = test
301
Jon Salz74ad3262012-03-16 14:40:55 +0800302 def handle_shutdown_complete(self, test, state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800303 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800304 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800305
Jon Salz74ad3262012-03-16 14:40:55 +0800306 @param test: The ShutdownStep.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800307 @param state: The test state.
308 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800309 state = test.update_state(increment_shutdown_count=1)
310 logging.info('Detected shutdown (%d of %d)',
311 state.shutdown_count, test.iterations)
Jon Salz4f6c7172012-06-11 20:45:36 +0800312
313 def log_and_update_state(status, error_msg, **kw):
314 self.event_log.Log('rebooted',
315 status=status, error_msg=error_msg, **kw)
316 test.update_state(status=status, error_msg=error_msg)
317
318 if not self.last_shutdown_time:
319 log_and_update_state(status=TestState.FAILED,
320 error_msg='Unable to read shutdown_time')
321 return
322
323 now = time.time()
324 logging.info('%.03f s passed since reboot',
325 now - self.last_shutdown_time)
326
327 if self.last_shutdown_time > now:
328 test.update_state(status=TestState.FAILED,
329 error_msg='Time moved backward during reboot')
330 elif (isinstance(test, factory.RebootStep) and
331 self.test_list.options.max_reboot_time_secs and
332 (now - self.last_shutdown_time >
333 self.test_list.options.max_reboot_time_secs)):
334 # A reboot took too long; fail. (We don't check this for
335 # HaltSteps, because the machine could be halted for a
336 # very long time, and even unplugged with battery backup,
337 # thus hosing the clock.)
338 log_and_update_state(
339 status=TestState.FAILED,
340 error_msg=('More than %d s elapsed during reboot '
341 '(%.03f s, from %s to %s)' % (
342 self.test_list.options.max_reboot_time_secs,
343 now - self.last_shutdown_time,
344 utils.TimeString(self.last_shutdown_time),
345 utils.TimeString(now))),
346 duration=(now-self.last_shutdown_time))
347 elif state.shutdown_count == test.iterations:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800348 # Good!
Jon Salz4f6c7172012-06-11 20:45:36 +0800349 log_and_update_state(status=TestState.PASSED,
350 duration=(now - self.last_shutdown_time),
351 error_msg='')
Jon Salz74ad3262012-03-16 14:40:55 +0800352 elif state.shutdown_count > test.iterations:
Jon Salz73e0fd02012-04-04 11:46:38 +0800353 # Shut down too many times
Jon Salz4f6c7172012-06-11 20:45:36 +0800354 log_and_update_state(status=TestState.FAILED,
355 error_msg='Too many shutdowns')
Jon Salz258a40c2012-04-19 12:34:01 +0800356 elif utils.are_shift_keys_depressed():
Jon Salz73e0fd02012-04-04 11:46:38 +0800357 logging.info('Shift keys are depressed; cancelling restarts')
358 # Abort shutdown
Jon Salz4f6c7172012-06-11 20:45:36 +0800359 log_and_update_state(
Jon Salz73e0fd02012-04-04 11:46:38 +0800360 status=TestState.FAILED,
361 error_msg='Shutdown aborted with double shift keys')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800362 else:
Jon Salz94eb56f2012-06-12 18:01:12 +0800363 def handler():
364 if self._prompt_cancel_shutdown(test, state.shutdown_count + 1):
365 log_and_update_state(
366 status=TestState.FAILED,
367 error_msg='Shutdown aborted by operator')
368 return
369
370 # Time to shutdown again
371 log_and_update_state(
372 status=TestState.ACTIVE,
373 error_msg='',
374 iteration=state.shutdown_count)
375
376 self.event_log.Log('shutdown', operation='reboot')
377 self.state_instance.set_shared_data('shutdown_time',
Jon Salz4f6c7172012-06-11 20:45:36 +0800378 time.time())
Jon Salz94eb56f2012-06-12 18:01:12 +0800379 self.env.shutdown('reboot')
380
381 self.on_ui_startup.append(handler)
382
383 def _prompt_cancel_shutdown(self, test, iteration):
384 if self.options.ui != 'chrome':
385 return False
386
387 pending_shutdown_data = {
388 'delay_secs': test.delay_secs,
389 'time': time.time() + test.delay_secs,
390 'operation': test.operation,
391 'iteration': iteration,
392 'iterations': test.iterations,
393 }
394
395 # Create a new (threaded) event client since we
396 # don't want to use the event loop for this.
397 with EventClient() as event_client:
398 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
399 **pending_shutdown_data))
400 aborted = event_client.wait(
401 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
402 timeout=test.delay_secs) is not None
403 if aborted:
404 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
405 return aborted
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800406
407 def init_states(self):
408 '''
409 Initializes all states on startup.
410 '''
411 for test in self.test_list.get_all_tests():
412 # Make sure the state server knows about all the tests,
413 # defaulting to an untested state.
414 test.update_state(update_parent=False, visible=False)
415
Jon Salzd6361c22012-06-11 22:23:57 +0800416 var_log_messages = None
417
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800418 # Any 'active' tests should be marked as failed now.
419 for test in self.test_list.walk():
420 state = test.get_state()
Hung-Te Lin96632362012-03-20 21:14:18 +0800421 if state.status != TestState.ACTIVE:
422 continue
423 if isinstance(test, factory.ShutdownStep):
424 # Shutdown while the test was active - that's good.
425 self.handle_shutdown_complete(test, state)
426 else:
Jon Salzd6361c22012-06-11 22:23:57 +0800427 # Unexpected shutdown. Grab /var/log/messages for context.
428 if var_log_messages is None:
429 try:
430 var_log_messages = (
431 utils.var_log_messages_before_reboot())
432 # Write it to the log, to make it easier to
433 # correlate with /var/log/messages.
434 logging.info(
435 'Unexpected shutdown. '
436 'Tail of /var/log/messages before last reboot:\n'
437 '%s', ('\n'.join(
438 ' ' + x for x in var_log_messages)))
439 except:
440 logging.exception('Unable to grok /var/log/messages')
441 var_log_messages = []
442
443 error_msg = 'Unexpected shutdown while test was running'
444 self.event_log.Log('end_test',
445 path=test.path,
446 status=TestState.FAILED,
447 invocation=test.get_state().invocation,
448 error_msg=error_msg,
449 var_log_messages='\n'.join(var_log_messages))
450 test.update_state(
451 status=TestState.FAILED,
452 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800453
454 def show_next_active_test(self):
455 '''
456 Rotates to the next visible active test.
457 '''
458 self.reap_completed_tests()
459 active_tests = [
460 t for t in self.test_list.walk()
461 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
462 if not active_tests:
463 return
464
465 try:
466 next_test = active_tests[
467 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
468 except ValueError: # visible_test not present in active_tests
469 next_test = active_tests[0]
470
471 self.set_visible_test(next_test)
472
473 def handle_event(self, event):
474 '''
475 Handles an event from the event server.
476 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800477 handler = self.event_handlers.get(event.type)
478 if handler:
Jon Salz968e90b2012-03-18 16:12:43 +0800479 handler(event)
Jon Salz0405ab52012-03-16 15:26:52 +0800480 else:
Jon Salz968e90b2012-03-18 16:12:43 +0800481 # We don't register handlers for all event types - just ignore
482 # this event.
483 logging.debug('Unbound event type %s', event.type)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800484
485 def run_next_test(self):
486 '''
487 Runs the next eligible test (or tests) in self.tests_to_run.
488 '''
489 self.reap_completed_tests()
490 while self.tests_to_run:
491 logging.debug('Tests to run: %s',
492 [x.path for x in self.tests_to_run])
493
494 test = self.tests_to_run[0]
495
496 if test in self.invocations:
497 logging.info('Next test %s is already running', test.path)
498 self.tests_to_run.popleft()
499 return
500
501 if self.invocations and not (test.backgroundable and all(
502 [x.backgroundable for x in self.invocations])):
503 logging.debug('Waiting for non-backgroundable tests to '
504 'complete before running %s', test.path)
505 return
506
507 self.tests_to_run.popleft()
508
Jon Salz74ad3262012-03-16 14:40:55 +0800509 if isinstance(test, factory.ShutdownStep):
Jon Salz8796e362012-05-24 11:39:09 +0800510 if os.path.exists(NO_REBOOT_FILE):
511 test.update_state(
512 status=TestState.FAILED, increment_count=1,
513 error_msg=('Skipped shutdown since %s is present' %
514 NO_REBOOT_FILE))
515 continue
516
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800517 test.update_state(status=TestState.ACTIVE, increment_count=1,
Jon Salz74ad3262012-03-16 14:40:55 +0800518 error_msg='', shutdown_count=0)
Jon Salz94eb56f2012-06-12 18:01:12 +0800519 if self._prompt_cancel_shutdown(test, 1):
520 self.event_log.Log('reboot_cancelled')
521 test.update_state(
522 status=TestState.FAILED, increment_count=1,
523 error_msg='Shutdown aborted by operator',
524 shutdown_count=0)
525 return
526
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800527 # Save pending test list in the state server
528 self.state_instance.set_shared_data(
Jon Salz74ad3262012-03-16 14:40:55 +0800529 'tests_after_shutdown',
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800530 [t.path for t in self.tests_to_run])
Jon Salz4f6c7172012-06-11 20:45:36 +0800531 # Save shutdown time
532 self.state_instance.set_shared_data('shutdown_time',
533 time.time())
Jon Salz74ad3262012-03-16 14:40:55 +0800534
Jon Salz73e0fd02012-04-04 11:46:38 +0800535 with self.env.lock:
Jon Salzb9038572012-05-24 10:34:51 +0800536 self.event_log.Log('shutdown', operation=test.operation)
Jon Salz73e0fd02012-04-04 11:46:38 +0800537 shutdown_result = self.env.shutdown(test.operation)
538 if shutdown_result:
539 # That's all, folks!
540 self.run_queue.put(None)
541 return
542 else:
543 # Just pass (e.g., in the chroot).
544 test.update_state(status=TestState.PASSED)
545 self.state_instance.set_shared_data(
546 'tests_after_shutdown', None)
Jon Salz94eb56f2012-06-12 18:01:12 +0800547 # Send event with no fields to indicate that there is no
548 # longer a pending shutdown.
549 self.event_client.post_event(Event(
550 Event.Type.PENDING_SHUTDOWN))
Jon Salz73e0fd02012-04-04 11:46:38 +0800551 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800552
553 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
554 self.invocations[test] = invoc
555 if self.visible_test is None and test.has_ui:
556 self.set_visible_test(test)
Jon Salz7c15e8b2012-06-19 17:10:37 +0800557 self.check_connection_manager()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800558 invoc.start()
559
Jon Salz7c15e8b2012-06-19 17:10:37 +0800560 def check_connection_manager(self):
561 exclusive_tests = [
562 test.path
563 for test in self.invocations
564 if test.is_exclusive(
565 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
566 if exclusive_tests:
567 # Make sure networking is disabled.
568 if self.network_enabled:
569 logging.info('Disabling network, as requested by %s',
570 exclusive_tests)
571 self.connection_manager.DisableNetworking()
572 self.network_enabled = False
573 else:
574 # Make sure networking is enabled.
575 if not self.network_enabled:
576 logging.info('Re-enabling network')
577 self.connection_manager.EnableNetworking()
578 self.network_enabled = True
579
Jon Salz0405ab52012-03-16 15:26:52 +0800580 def run_tests(self, subtrees, untested_only=False):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800581 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800582 Runs tests under subtree.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800583
584 The tests are run in order unless one fails (then stops).
585 Backgroundable tests are run simultaneously; when a foreground test is
586 encountered, we wait for all active tests to finish before continuing.
Jon Salz0405ab52012-03-16 15:26:52 +0800587
588 @param subtrees: Node or nodes containing tests to run (may either be
589 a single test or a list). Duplicates will be ignored.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800590 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800591 if type(subtrees) != list:
592 subtrees = [subtrees]
593
594 # Nodes we've seen so far, to avoid duplicates.
595 seen = set()
596
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800597 self.tests_to_run = deque()
Jon Salz0405ab52012-03-16 15:26:52 +0800598 for subtree in subtrees:
599 for test in subtree.walk():
600 if test in seen:
601 continue
602 seen.add(test)
603
604 if not test.is_leaf():
605 continue
606 if (untested_only and
607 test.get_state().status != TestState.UNTESTED):
608 continue
609 self.tests_to_run.append(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800610 self.run_next_test()
611
612 def reap_completed_tests(self):
613 '''
614 Removes completed tests from the set of active tests.
615
616 Also updates the visible test if it was reaped.
617 '''
618 for t, v in dict(self.invocations).iteritems():
619 if v.is_completed():
620 del self.invocations[t]
621
622 if (self.visible_test is None or
623 self.visible_test not in self.invocations):
624 self.set_visible_test(None)
625 # Make the first running test, if any, the visible test
626 for t in self.test_list.walk():
627 if t in self.invocations:
628 self.set_visible_test(t)
629 break
630
Hung-Te Lin96632362012-03-20 21:14:18 +0800631 def kill_active_tests(self, abort):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800632 '''
633 Kills and waits for all active tests.
Hung-Te Lin96632362012-03-20 21:14:18 +0800634
635 @param abort: True to change state of killed tests to FAILED, False for
636 UNTESTED.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800637 '''
638 self.reap_completed_tests()
639 for test, invoc in self.invocations.items():
640 factory.console.info('Killing active test %s...' % test.path)
641 invoc.abort_and_join()
642 factory.console.info('Killed %s' % test.path)
643 del self.invocations[test]
Hung-Te Lin96632362012-03-20 21:14:18 +0800644 if not abort:
645 test.update_state(status=TestState.UNTESTED)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800646 self.reap_completed_tests()
647
Jon Salzf00cdc82012-05-28 18:56:17 +0800648 def stop(self):
649 self.kill_active_tests(False)
650 self.run_tests([])
651
Hung-Te Lin96632362012-03-20 21:14:18 +0800652 def abort_active_tests(self):
653 self.kill_active_tests(True)
654
Jon Salz73e0fd02012-04-04 11:46:38 +0800655 def main(self):
Jon Salzeb8d25f2012-05-22 15:17:32 +0800656 try:
657 self.init()
Jon Salzb9038572012-05-24 10:34:51 +0800658 self.event_log.Log('goofy_init',
659 success=True)
Jon Salzeb8d25f2012-05-22 15:17:32 +0800660 except:
661 if self.event_log:
662 try:
Jon Salzb9038572012-05-24 10:34:51 +0800663 self.event_log.Log('goofy_init',
664 success=False,
665 trace=traceback.format_exc())
Jon Salzeb8d25f2012-05-22 15:17:32 +0800666 except:
667 pass
668 raise
669
Jon Salz73e0fd02012-04-04 11:46:38 +0800670 self.run()
671
Jon Salz5f2a0672012-05-22 17:14:06 +0800672 def update_system_info(self):
673 '''Updates system info.'''
Jon Salz49a7d152012-06-19 15:04:09 +0800674 system_info = system.SystemInfo()
Jon Salz5f2a0672012-05-22 17:14:06 +0800675 self.state_instance.set_shared_data('system_info', system_info.__dict__)
676 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
677 system_info=system_info.__dict__))
678 logging.info('System info: %r', system_info.__dict__)
679
Jon Salz37eccbd2012-05-25 16:06:52 +0800680 def update_factory(self):
681 self.kill_active_tests(False)
682 self.run_tests([])
683
684 try:
685 if updater.TryUpdate(pre_update_hook=self.state_instance.close):
686 self.env.shutdown('reboot')
687 except:
688 factory.console.exception('Unable to update')
689
Jon Salz73e0fd02012-04-04 11:46:38 +0800690 def init(self, args=None, env=None):
691 '''Initializes Goofy.
Jon Salz74ad3262012-03-16 14:40:55 +0800692
693 Args:
Jon Salz73e0fd02012-04-04 11:46:38 +0800694 args: A list of command-line arguments. Uses sys.argv if
695 args is None.
696 env: An Environment instance to use (or None to choose
Jon Salz258a40c2012-04-19 12:34:01 +0800697 FakeChrootEnvironment or DUTEnvironment as appropriate).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800698 '''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800699 parser = OptionParser()
700 parser.add_option('-v', '--verbose', dest='verbose',
701 action='store_true',
702 help='Enable debug logging')
703 parser.add_option('--print_test_list', dest='print_test_list',
704 metavar='FILE',
705 help='Read and print test list FILE, and exit')
Jon Salz758e6cc2012-04-03 15:47:07 +0800706 parser.add_option('--restart', dest='restart',
707 action='store_true',
708 help='Clear all test state')
Jon Salz258a40c2012-04-19 12:34:01 +0800709 parser.add_option('--ui', dest='ui', type='choice',
710 choices=['none', 'gtk', 'chrome'],
711 default='gtk',
712 help='UI to use')
Jon Salz63585ea2012-05-21 15:03:32 +0800713 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
714 type='int', default=1,
715 help=('Factor by which to scale UI '
716 '(Chrome UI only)'))
Jon Salz73e0fd02012-04-04 11:46:38 +0800717 parser.add_option('--test_list', dest='test_list',
718 metavar='FILE',
719 help='Use FILE as test list')
720 (self.options, self.args) = parser.parse_args(args)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800721
Jon Salz73e0fd02012-04-04 11:46:38 +0800722 global _inited_logging
723 if not _inited_logging:
724 factory.init_logging('goofy', verbose=self.options.verbose)
725 _inited_logging = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800726 self.event_log = EventLog('goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800727
Jon Salz73e0fd02012-04-04 11:46:38 +0800728 if (not suppress_chroot_warning and
729 factory.in_chroot() and
Jon Salz258a40c2012-04-19 12:34:01 +0800730 self.options.ui == 'gtk' and
Jon Salz758e6cc2012-04-03 15:47:07 +0800731 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
732 # That's not going to work! Tell the user how to run
733 # this way.
734 logging.warn(GOOFY_IN_CHROOT_WARNING)
735 time.sleep(1)
736
Jon Salz73e0fd02012-04-04 11:46:38 +0800737 if env:
738 self.env = env
739 elif factory.in_chroot():
Jon Salz5f2a0672012-05-22 17:14:06 +0800740 self.env = test_environment.FakeChrootEnvironment()
Jon Salz73e0fd02012-04-04 11:46:38 +0800741 logging.warn(
742 'Using chroot environment: will not actually run autotests')
743 else:
Jon Salz5f2a0672012-05-22 17:14:06 +0800744 self.env = test_environment.DUTEnvironment()
Jon Salz323dd3d2012-04-09 18:40:43 +0800745 self.env.goofy = self
Jon Salz73e0fd02012-04-04 11:46:38 +0800746
Jon Salz758e6cc2012-04-03 15:47:07 +0800747 if self.options.restart:
748 state.clear_state()
749
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800750 if self.options.print_test_list:
751 print (factory.read_test_list(self.options.print_test_list).
752 __repr__(recursive=True))
753 return
754
Jon Salz7c15e8b2012-06-19 17:10:37 +0800755 if self.options.ui_scale_factor != 1 and utils.in_qemu():
Jon Salz9b312912012-06-04 11:27:00 +0800756 logging.warn(
757 'In QEMU; ignoring ui_scale_factor argument')
758 self.options.ui_scale_factor = 1
759
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800760 logging.info('Started')
761
762 self.start_state_server()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800763 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz63585ea2012-05-21 15:03:32 +0800764 self.state_instance.set_shared_data('ui_scale_factor',
765 self.options.ui_scale_factor)
Jon Salz4f6c7172012-06-11 20:45:36 +0800766 self.last_shutdown_time = (
767 self.state_instance.get_shared_data('shutdown_time', optional=True))
768 self.state_instance.del_shared_data('shutdown_time', optional=True)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800769
Jon Salz73e0fd02012-04-04 11:46:38 +0800770 self.options.test_list = (self.options.test_list or find_test_list())
771 self.test_list = factory.read_test_list(self.options.test_list,
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800772 self.state_instance)
Jon Salz06fbeff2012-05-21 17:06:05 +0800773 if not self.state_instance.has_shared_data('ui_lang'):
774 self.state_instance.set_shared_data('ui_lang',
775 self.test_list.options.ui_lang)
Jon Salzdbf398f2012-06-14 17:30:01 +0800776 self.state_instance.set_shared_data(
777 'test_list_options',
778 self.test_list.options.__dict__)
Jon Salz258a40c2012-04-19 12:34:01 +0800779 self.state_instance.test_list = self.test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800780
781 self.init_states()
782 self.start_event_server()
Jon Salz7c15e8b2012-06-19 17:10:37 +0800783 self.connection_manager = self.env.create_connection_manager(
784 self.test_list.options.wlans)
Jon Salz258a40c2012-04-19 12:34:01 +0800785
Jon Salz5f2a0672012-05-22 17:14:06 +0800786 self.update_system_info()
787
Jon Salz5da61e62012-05-31 13:06:22 +0800788 os.environ['CROS_FACTORY'] = '1'
Jon Salzeebd12e2012-06-08 15:34:56 +0800789 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
Jon Salz5da61e62012-05-31 13:06:22 +0800790
Jon Salzb1b39092012-05-03 02:05:09 +0800791 # Set CROS_UI since some behaviors in ui.py depend on the
792 # particular UI in use. TODO(jsalz): Remove this (and all
793 # places it is used) when the GTK UI is removed.
794 os.environ['CROS_UI'] = self.options.ui
Jon Salz258a40c2012-04-19 12:34:01 +0800795
Jon Salzb1b39092012-05-03 02:05:09 +0800796 if self.options.ui == 'chrome':
Jon Salz258a40c2012-04-19 12:34:01 +0800797 self.env.launch_chrome()
798 logging.info('Waiting for a web socket connection')
799 self.web_socket_manager.wait()
Jon Salzb1b39092012-05-03 02:05:09 +0800800
801 # Wait for the test widget size to be set; this is done in
802 # an asynchronous RPC so there is a small chance that the
803 # web socket might be opened first.
804 for i in range(100): # 10 s
Jon Salz63585ea2012-05-21 15:03:32 +0800805 try:
806 if self.state_instance.get_shared_data('test_widget_size'):
807 break
808 except KeyError:
809 pass # Retry
Jon Salzb1b39092012-05-03 02:05:09 +0800810 time.sleep(0.1) # 100 ms
811 else:
812 logging.warn('Never received test_widget_size from UI')
Jon Salz258a40c2012-04-19 12:34:01 +0800813 elif self.options.ui == 'gtk':
Jon Salz73e0fd02012-04-04 11:46:38 +0800814 self.start_ui()
Jon Salz258a40c2012-04-19 12:34:01 +0800815
Jon Salz94eb56f2012-06-12 18:01:12 +0800816 for handler in self.on_ui_startup:
817 handler()
818
Jon Salz8375c2e2012-04-04 15:22:24 +0800819 self.prespawner = Prespawner()
Jon Salz323dd3d2012-04-09 18:40:43 +0800820 self.prespawner.start()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800821
822 def state_change_callback(test, state):
823 self.event_client.post_event(
824 Event(Event.Type.STATE_CHANGE,
825 path=test.path, state=state))
826 self.test_list.state_change_callback = state_change_callback
827
828 try:
Jon Salz758e6cc2012-04-03 15:47:07 +0800829 tests_after_shutdown = self.state_instance.get_shared_data(
830 'tests_after_shutdown')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800831 except KeyError:
Jon Salz758e6cc2012-04-03 15:47:07 +0800832 tests_after_shutdown = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800833
Jon Salz758e6cc2012-04-03 15:47:07 +0800834 if tests_after_shutdown is not None:
835 logging.info('Resuming tests after shutdown: %s',
836 tests_after_shutdown)
837 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800838 self.tests_to_run.extend(
Jon Salz758e6cc2012-04-03 15:47:07 +0800839 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Jon Salz73e0fd02012-04-04 11:46:38 +0800840 self.run_queue.put(self.run_next_test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800841 else:
Jon Salz57717ca2012-04-04 16:47:25 +0800842 if self.test_list.options.auto_run_on_start:
843 self.run_queue.put(
844 lambda: self.run_tests(self.test_list, untested_only=True))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800845
Jon Salz73e0fd02012-04-04 11:46:38 +0800846 def run(self):
847 '''Runs Goofy.'''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800848 # Process events forever.
Jon Salz57717ca2012-04-04 16:47:25 +0800849 while self.run_once(True):
Jon Salz73e0fd02012-04-04 11:46:38 +0800850 pass
851
Jon Salz57717ca2012-04-04 16:47:25 +0800852 def run_once(self, block=False):
Jon Salz73e0fd02012-04-04 11:46:38 +0800853 '''Runs all items pending in the event loop.
854
Jon Salz57717ca2012-04-04 16:47:25 +0800855 Args:
856 block: If true, block until at least one event is processed.
857
Jon Salz73e0fd02012-04-04 11:46:38 +0800858 Returns:
859 True to keep going or False to shut down.
860 '''
Jon Salz7c15e8b2012-06-19 17:10:37 +0800861 events = utils.DrainQueue(self.run_queue)
862 if not events:
863 # Nothing on the run queue.
864 self._run_queue_idle()
865 if block:
866 # Block for at least one event...
867 events.append(self.run_queue.get())
868 # ...and grab anything else that showed up at the same
869 # time.
870 events.extend(utils.DrainQueue(self.run_queue))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800871
Jon Salz73e0fd02012-04-04 11:46:38 +0800872 for event in events:
873 if not event:
874 # Shutdown request.
875 self.run_queue.task_done()
876 return False
877
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800878 try:
879 event()
880 except Exception as e: # pylint: disable=W0703
881 logging.error('Error in event loop: %s', e)
882 traceback.print_exc(sys.stderr)
Jon Salz8375c2e2012-04-04 15:22:24 +0800883 self.record_exception(traceback.format_exception_only(
884 *sys.exc_info()[:2]))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800885 # But keep going
886 finally:
887 self.run_queue.task_done()
Jon Salz73e0fd02012-04-04 11:46:38 +0800888 return True
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800889
Jon Salz7c15e8b2012-06-19 17:10:37 +0800890 def _run_queue_idle(self):
891 '''Invoked when the run queue has no events.'''
892 self.check_connection_manager()
893
Jon Salz258a40c2012-04-19 12:34:01 +0800894 def run_tests_with_status(self, statuses_to_run, starting_at=None,
895 root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800896 '''Runs all top-level tests with a particular status.
897
898 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +0800899
900 Args:
901 starting_at: If provided, only auto-runs tests beginning with
902 this test.
Jon Salz0405ab52012-03-16 15:26:52 +0800903 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800904 root = root or self.test_list
905
Jon Salz57717ca2012-04-04 16:47:25 +0800906 if starting_at:
907 # Make sure they passed a test, not a string.
908 assert isinstance(starting_at, factory.FactoryTest)
909
Jon Salz0405ab52012-03-16 15:26:52 +0800910 tests_to_reset = []
911 tests_to_run = []
912
Jon Salz57717ca2012-04-04 16:47:25 +0800913 found_starting_at = False
914
Jon Salz258a40c2012-04-19 12:34:01 +0800915 for test in root.get_top_level_tests():
Jon Salz57717ca2012-04-04 16:47:25 +0800916 if starting_at:
917 if test == starting_at:
918 # We've found starting_at; do auto-run on all
919 # subsequent tests.
920 found_starting_at = True
921 if not found_starting_at:
922 # Don't start this guy yet
923 continue
924
Jon Salz0405ab52012-03-16 15:26:52 +0800925 status = test.get_state().status
926 if status == TestState.ACTIVE or status in statuses_to_run:
927 # Reset the test (later; we will need to abort
928 # all active tests first).
929 tests_to_reset.append(test)
930 if status in statuses_to_run:
931 tests_to_run.append(test)
932
933 self.abort_active_tests()
934
935 # Reset all statuses of the tests to run (in case any tests were active;
936 # we want them to be run again).
937 for test_to_reset in tests_to_reset:
938 for test in test_to_reset.walk():
939 test.update_state(status=TestState.UNTESTED)
940
941 self.run_tests(tests_to_run, untested_only=True)
942
Jon Salz258a40c2012-04-19 12:34:01 +0800943 def restart_tests(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800944 '''Restarts all tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800945 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +0800946
Jon Salz258a40c2012-04-19 12:34:01 +0800947 self.abort_active_tests()
948 for test in root.walk():
949 test.update_state(status=TestState.UNTESTED)
950 self.run_tests(root)
951
952 def auto_run(self, starting_at=None, root=None):
Jon Salz57717ca2012-04-04 16:47:25 +0800953 '''"Auto-runs" tests that have not been run yet.
954
955 Args:
956 starting_at: If provide, only auto-runs tests beginning with
957 this test.
958 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800959 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +0800960 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Jon Salz258a40c2012-04-19 12:34:01 +0800961 starting_at=starting_at,
962 root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800963
Jon Salz258a40c2012-04-19 12:34:01 +0800964 def re_run_failed(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800965 '''Re-runs failed tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800966 root = root or self.test_list
967 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800968
Jon Salz968e90b2012-03-18 16:12:43 +0800969 def show_review_information(self):
Hung-Te Lin96632362012-03-20 21:14:18 +0800970 '''Event handler for showing review information screen.
971
972 The information screene is rendered by main UI program (ui.py), so in
973 goofy we only need to kill all active tests, set them as untested, and
974 clear remaining tests.
975 '''
976 self.kill_active_tests(False)
977 self.run_tests([])
978
Jon Salz0405ab52012-03-16 15:26:52 +0800979 def handle_switch_test(self, event):
Jon Salz968e90b2012-03-18 16:12:43 +0800980 '''Switches to a particular test.
981
982 @param event: The SWITCH_TEST event.
983 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800984 test = self.test_list.lookup_path(event.path)
Jon Salz57717ca2012-04-04 16:47:25 +0800985 if not test:
Jon Salz968e90b2012-03-18 16:12:43 +0800986 logging.error('Unknown test %r', event.key)
Jon Salz57717ca2012-04-04 16:47:25 +0800987 return
988
989 invoc = self.invocations.get(test)
990 if invoc and test.backgroundable:
991 # Already running: just bring to the front if it
992 # has a UI.
993 logging.info('Setting visible test to %s', test.path)
994 self.event_client.post_event(
995 Event(Event.Type.SET_VISIBLE_TEST, path=test.path))
996 return
997
998 self.abort_active_tests()
999 for t in test.walk():
1000 t.update_state(status=TestState.UNTESTED)
1001
1002 if self.test_list.options.auto_run_on_keypress:
1003 self.auto_run(starting_at=test)
1004 else:
1005 self.run_tests(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001006
Jon Salz73e0fd02012-04-04 11:46:38 +08001007 def wait(self):
1008 '''Waits for all pending invocations.
1009
1010 Useful for testing.
1011 '''
1012 for k, v in self.invocations.iteritems():
1013 logging.info('Waiting for %s to complete...', k)
1014 v.thread.join()
1015
1016 def check_exceptions(self):
1017 '''Raises an error if any exceptions have occurred in
1018 invocation threads.'''
1019 if self.exceptions:
1020 raise RuntimeError('Exception in invocation thread: %r' %
1021 self.exceptions)
1022
1023 def record_exception(self, msg):
1024 '''Records an exception in an invocation thread.
1025
1026 An exception with the given message will be rethrown when
1027 Goofy is destroyed.'''
1028 self.exceptions.append(msg)
1029
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001030
1031if __name__ == '__main__':
1032 Goofy().main()