blob: de594ca7bf2369232d53e9a3c07eb74c5813d869 [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 Salz258a40c2012-04-19 12:34:01 +0800279 ui_proc_args = [os.path.join(factory.CROS_FACTORY_LIB_PATH, 'ui'),
280 self.options.test_list]
Jon Salz14bcbb02012-03-17 15:11:50 +0800281 if self.options.verbose:
282 ui_proc_args.append('-v')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800283 logging.info('Starting ui %s', ui_proc_args)
284 self.ui_process = subprocess.Popen(ui_proc_args)
285 logging.info('Waiting for UI to come up...')
286 self.event_client.wait(
287 lambda event: event.type == Event.Type.UI_READY)
288 logging.info('UI has started')
289
290 def set_visible_test(self, test):
291 if self.visible_test == test:
292 return
293
294 if test:
295 test.update_state(visible=True)
296 if self.visible_test:
297 self.visible_test.update_state(visible=False)
298 self.visible_test = test
299
Jon Salz74ad3262012-03-16 14:40:55 +0800300 def handle_shutdown_complete(self, test, state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800301 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800302 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800303
Jon Salz74ad3262012-03-16 14:40:55 +0800304 @param test: The ShutdownStep.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800305 @param state: The test state.
306 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800307 state = test.update_state(increment_shutdown_count=1)
308 logging.info('Detected shutdown (%d of %d)',
309 state.shutdown_count, test.iterations)
Jon Salz4f6c7172012-06-11 20:45:36 +0800310
311 def log_and_update_state(status, error_msg, **kw):
312 self.event_log.Log('rebooted',
313 status=status, error_msg=error_msg, **kw)
314 test.update_state(status=status, error_msg=error_msg)
315
316 if not self.last_shutdown_time:
317 log_and_update_state(status=TestState.FAILED,
318 error_msg='Unable to read shutdown_time')
319 return
320
321 now = time.time()
322 logging.info('%.03f s passed since reboot',
323 now - self.last_shutdown_time)
324
325 if self.last_shutdown_time > now:
326 test.update_state(status=TestState.FAILED,
327 error_msg='Time moved backward during reboot')
328 elif (isinstance(test, factory.RebootStep) and
329 self.test_list.options.max_reboot_time_secs and
330 (now - self.last_shutdown_time >
331 self.test_list.options.max_reboot_time_secs)):
332 # A reboot took too long; fail. (We don't check this for
333 # HaltSteps, because the machine could be halted for a
334 # very long time, and even unplugged with battery backup,
335 # thus hosing the clock.)
336 log_and_update_state(
337 status=TestState.FAILED,
338 error_msg=('More than %d s elapsed during reboot '
339 '(%.03f s, from %s to %s)' % (
340 self.test_list.options.max_reboot_time_secs,
341 now - self.last_shutdown_time,
342 utils.TimeString(self.last_shutdown_time),
343 utils.TimeString(now))),
344 duration=(now-self.last_shutdown_time))
345 elif state.shutdown_count == test.iterations:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800346 # Good!
Jon Salz4f6c7172012-06-11 20:45:36 +0800347 log_and_update_state(status=TestState.PASSED,
348 duration=(now - self.last_shutdown_time),
349 error_msg='')
Jon Salz74ad3262012-03-16 14:40:55 +0800350 elif state.shutdown_count > test.iterations:
Jon Salz73e0fd02012-04-04 11:46:38 +0800351 # Shut down too many times
Jon Salz4f6c7172012-06-11 20:45:36 +0800352 log_and_update_state(status=TestState.FAILED,
353 error_msg='Too many shutdowns')
Jon Salz258a40c2012-04-19 12:34:01 +0800354 elif utils.are_shift_keys_depressed():
Jon Salz73e0fd02012-04-04 11:46:38 +0800355 logging.info('Shift keys are depressed; cancelling restarts')
356 # Abort shutdown
Jon Salz4f6c7172012-06-11 20:45:36 +0800357 log_and_update_state(
Jon Salz73e0fd02012-04-04 11:46:38 +0800358 status=TestState.FAILED,
359 error_msg='Shutdown aborted with double shift keys')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800360 else:
Jon Salz94eb56f2012-06-12 18:01:12 +0800361 def handler():
362 if self._prompt_cancel_shutdown(test, state.shutdown_count + 1):
363 log_and_update_state(
364 status=TestState.FAILED,
365 error_msg='Shutdown aborted by operator')
366 return
367
368 # Time to shutdown again
369 log_and_update_state(
370 status=TestState.ACTIVE,
371 error_msg='',
372 iteration=state.shutdown_count)
373
374 self.event_log.Log('shutdown', operation='reboot')
375 self.state_instance.set_shared_data('shutdown_time',
Jon Salz4f6c7172012-06-11 20:45:36 +0800376 time.time())
Jon Salz94eb56f2012-06-12 18:01:12 +0800377 self.env.shutdown('reboot')
378
379 self.on_ui_startup.append(handler)
380
381 def _prompt_cancel_shutdown(self, test, iteration):
382 if self.options.ui != 'chrome':
383 return False
384
385 pending_shutdown_data = {
386 'delay_secs': test.delay_secs,
387 'time': time.time() + test.delay_secs,
388 'operation': test.operation,
389 'iteration': iteration,
390 'iterations': test.iterations,
391 }
392
393 # Create a new (threaded) event client since we
394 # don't want to use the event loop for this.
395 with EventClient() as event_client:
396 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
397 **pending_shutdown_data))
398 aborted = event_client.wait(
399 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
400 timeout=test.delay_secs) is not None
401 if aborted:
402 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
403 return aborted
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800404
405 def init_states(self):
406 '''
407 Initializes all states on startup.
408 '''
409 for test in self.test_list.get_all_tests():
410 # Make sure the state server knows about all the tests,
411 # defaulting to an untested state.
412 test.update_state(update_parent=False, visible=False)
413
Jon Salzd6361c22012-06-11 22:23:57 +0800414 var_log_messages = None
415
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800416 # Any 'active' tests should be marked as failed now.
417 for test in self.test_list.walk():
418 state = test.get_state()
Hung-Te Lin96632362012-03-20 21:14:18 +0800419 if state.status != TestState.ACTIVE:
420 continue
421 if isinstance(test, factory.ShutdownStep):
422 # Shutdown while the test was active - that's good.
423 self.handle_shutdown_complete(test, state)
424 else:
Jon Salzd6361c22012-06-11 22:23:57 +0800425 # Unexpected shutdown. Grab /var/log/messages for context.
426 if var_log_messages is None:
427 try:
428 var_log_messages = (
429 utils.var_log_messages_before_reboot())
430 # Write it to the log, to make it easier to
431 # correlate with /var/log/messages.
432 logging.info(
433 'Unexpected shutdown. '
434 'Tail of /var/log/messages before last reboot:\n'
435 '%s', ('\n'.join(
436 ' ' + x for x in var_log_messages)))
437 except:
438 logging.exception('Unable to grok /var/log/messages')
439 var_log_messages = []
440
441 error_msg = 'Unexpected shutdown while test was running'
442 self.event_log.Log('end_test',
443 path=test.path,
444 status=TestState.FAILED,
445 invocation=test.get_state().invocation,
446 error_msg=error_msg,
447 var_log_messages='\n'.join(var_log_messages))
448 test.update_state(
449 status=TestState.FAILED,
450 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800451
452 def show_next_active_test(self):
453 '''
454 Rotates to the next visible active test.
455 '''
456 self.reap_completed_tests()
457 active_tests = [
458 t for t in self.test_list.walk()
459 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
460 if not active_tests:
461 return
462
463 try:
464 next_test = active_tests[
465 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
466 except ValueError: # visible_test not present in active_tests
467 next_test = active_tests[0]
468
469 self.set_visible_test(next_test)
470
471 def handle_event(self, event):
472 '''
473 Handles an event from the event server.
474 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800475 handler = self.event_handlers.get(event.type)
476 if handler:
Jon Salz968e90b2012-03-18 16:12:43 +0800477 handler(event)
Jon Salz0405ab52012-03-16 15:26:52 +0800478 else:
Jon Salz968e90b2012-03-18 16:12:43 +0800479 # We don't register handlers for all event types - just ignore
480 # this event.
481 logging.debug('Unbound event type %s', event.type)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800482
483 def run_next_test(self):
484 '''
485 Runs the next eligible test (or tests) in self.tests_to_run.
486 '''
487 self.reap_completed_tests()
488 while self.tests_to_run:
489 logging.debug('Tests to run: %s',
490 [x.path for x in self.tests_to_run])
491
492 test = self.tests_to_run[0]
493
494 if test in self.invocations:
495 logging.info('Next test %s is already running', test.path)
496 self.tests_to_run.popleft()
497 return
498
499 if self.invocations and not (test.backgroundable and all(
500 [x.backgroundable for x in self.invocations])):
501 logging.debug('Waiting for non-backgroundable tests to '
502 'complete before running %s', test.path)
503 return
504
505 self.tests_to_run.popleft()
506
Jon Salz74ad3262012-03-16 14:40:55 +0800507 if isinstance(test, factory.ShutdownStep):
Jon Salz8796e362012-05-24 11:39:09 +0800508 if os.path.exists(NO_REBOOT_FILE):
509 test.update_state(
510 status=TestState.FAILED, increment_count=1,
511 error_msg=('Skipped shutdown since %s is present' %
512 NO_REBOOT_FILE))
513 continue
514
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800515 test.update_state(status=TestState.ACTIVE, increment_count=1,
Jon Salz74ad3262012-03-16 14:40:55 +0800516 error_msg='', shutdown_count=0)
Jon Salz94eb56f2012-06-12 18:01:12 +0800517 if self._prompt_cancel_shutdown(test, 1):
518 self.event_log.Log('reboot_cancelled')
519 test.update_state(
520 status=TestState.FAILED, increment_count=1,
521 error_msg='Shutdown aborted by operator',
522 shutdown_count=0)
523 return
524
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800525 # Save pending test list in the state server
526 self.state_instance.set_shared_data(
Jon Salz74ad3262012-03-16 14:40:55 +0800527 'tests_after_shutdown',
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800528 [t.path for t in self.tests_to_run])
Jon Salz4f6c7172012-06-11 20:45:36 +0800529 # Save shutdown time
530 self.state_instance.set_shared_data('shutdown_time',
531 time.time())
Jon Salz74ad3262012-03-16 14:40:55 +0800532
Jon Salz73e0fd02012-04-04 11:46:38 +0800533 with self.env.lock:
Jon Salzb9038572012-05-24 10:34:51 +0800534 self.event_log.Log('shutdown', operation=test.operation)
Jon Salz73e0fd02012-04-04 11:46:38 +0800535 shutdown_result = self.env.shutdown(test.operation)
536 if shutdown_result:
537 # That's all, folks!
538 self.run_queue.put(None)
539 return
540 else:
541 # Just pass (e.g., in the chroot).
542 test.update_state(status=TestState.PASSED)
543 self.state_instance.set_shared_data(
544 'tests_after_shutdown', None)
Jon Salz94eb56f2012-06-12 18:01:12 +0800545 # Send event with no fields to indicate that there is no
546 # longer a pending shutdown.
547 self.event_client.post_event(Event(
548 Event.Type.PENDING_SHUTDOWN))
Jon Salz73e0fd02012-04-04 11:46:38 +0800549 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800550
551 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
552 self.invocations[test] = invoc
553 if self.visible_test is None and test.has_ui:
554 self.set_visible_test(test)
Jon Salz7c15e8b2012-06-19 17:10:37 +0800555 self.check_connection_manager()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800556 invoc.start()
557
Jon Salz7c15e8b2012-06-19 17:10:37 +0800558 def check_connection_manager(self):
559 exclusive_tests = [
560 test.path
561 for test in self.invocations
562 if test.is_exclusive(
563 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
564 if exclusive_tests:
565 # Make sure networking is disabled.
566 if self.network_enabled:
567 logging.info('Disabling network, as requested by %s',
568 exclusive_tests)
569 self.connection_manager.DisableNetworking()
570 self.network_enabled = False
571 else:
572 # Make sure networking is enabled.
573 if not self.network_enabled:
574 logging.info('Re-enabling network')
575 self.connection_manager.EnableNetworking()
576 self.network_enabled = True
577
Jon Salz0405ab52012-03-16 15:26:52 +0800578 def run_tests(self, subtrees, untested_only=False):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800579 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800580 Runs tests under subtree.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800581
582 The tests are run in order unless one fails (then stops).
583 Backgroundable tests are run simultaneously; when a foreground test is
584 encountered, we wait for all active tests to finish before continuing.
Jon Salz0405ab52012-03-16 15:26:52 +0800585
586 @param subtrees: Node or nodes containing tests to run (may either be
587 a single test or a list). Duplicates will be ignored.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800588 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800589 if type(subtrees) != list:
590 subtrees = [subtrees]
591
592 # Nodes we've seen so far, to avoid duplicates.
593 seen = set()
594
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800595 self.tests_to_run = deque()
Jon Salz0405ab52012-03-16 15:26:52 +0800596 for subtree in subtrees:
597 for test in subtree.walk():
598 if test in seen:
599 continue
600 seen.add(test)
601
602 if not test.is_leaf():
603 continue
604 if (untested_only and
605 test.get_state().status != TestState.UNTESTED):
606 continue
607 self.tests_to_run.append(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800608 self.run_next_test()
609
610 def reap_completed_tests(self):
611 '''
612 Removes completed tests from the set of active tests.
613
614 Also updates the visible test if it was reaped.
615 '''
616 for t, v in dict(self.invocations).iteritems():
617 if v.is_completed():
618 del self.invocations[t]
619
620 if (self.visible_test is None or
621 self.visible_test not in self.invocations):
622 self.set_visible_test(None)
623 # Make the first running test, if any, the visible test
624 for t in self.test_list.walk():
625 if t in self.invocations:
626 self.set_visible_test(t)
627 break
628
Hung-Te Lin96632362012-03-20 21:14:18 +0800629 def kill_active_tests(self, abort):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800630 '''
631 Kills and waits for all active tests.
Hung-Te Lin96632362012-03-20 21:14:18 +0800632
633 @param abort: True to change state of killed tests to FAILED, False for
634 UNTESTED.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800635 '''
636 self.reap_completed_tests()
637 for test, invoc in self.invocations.items():
638 factory.console.info('Killing active test %s...' % test.path)
639 invoc.abort_and_join()
640 factory.console.info('Killed %s' % test.path)
641 del self.invocations[test]
Hung-Te Lin96632362012-03-20 21:14:18 +0800642 if not abort:
643 test.update_state(status=TestState.UNTESTED)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800644 self.reap_completed_tests()
645
Jon Salzf00cdc82012-05-28 18:56:17 +0800646 def stop(self):
647 self.kill_active_tests(False)
648 self.run_tests([])
649
Hung-Te Lin96632362012-03-20 21:14:18 +0800650 def abort_active_tests(self):
651 self.kill_active_tests(True)
652
Jon Salz73e0fd02012-04-04 11:46:38 +0800653 def main(self):
Jon Salzeb8d25f2012-05-22 15:17:32 +0800654 try:
655 self.init()
Jon Salzb9038572012-05-24 10:34:51 +0800656 self.event_log.Log('goofy_init',
657 success=True)
Jon Salzeb8d25f2012-05-22 15:17:32 +0800658 except:
659 if self.event_log:
660 try:
Jon Salzb9038572012-05-24 10:34:51 +0800661 self.event_log.Log('goofy_init',
662 success=False,
663 trace=traceback.format_exc())
Jon Salzeb8d25f2012-05-22 15:17:32 +0800664 except:
665 pass
666 raise
667
Jon Salz73e0fd02012-04-04 11:46:38 +0800668 self.run()
669
Jon Salz5f2a0672012-05-22 17:14:06 +0800670 def update_system_info(self):
671 '''Updates system info.'''
Jon Salz49a7d152012-06-19 15:04:09 +0800672 system_info = system.SystemInfo()
Jon Salz5f2a0672012-05-22 17:14:06 +0800673 self.state_instance.set_shared_data('system_info', system_info.__dict__)
674 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
675 system_info=system_info.__dict__))
676 logging.info('System info: %r', system_info.__dict__)
677
Jon Salz37eccbd2012-05-25 16:06:52 +0800678 def update_factory(self):
679 self.kill_active_tests(False)
680 self.run_tests([])
681
682 try:
683 if updater.TryUpdate(pre_update_hook=self.state_instance.close):
684 self.env.shutdown('reboot')
685 except:
686 factory.console.exception('Unable to update')
687
Jon Salz73e0fd02012-04-04 11:46:38 +0800688 def init(self, args=None, env=None):
689 '''Initializes Goofy.
Jon Salz74ad3262012-03-16 14:40:55 +0800690
691 Args:
Jon Salz73e0fd02012-04-04 11:46:38 +0800692 args: A list of command-line arguments. Uses sys.argv if
693 args is None.
694 env: An Environment instance to use (or None to choose
Jon Salz258a40c2012-04-19 12:34:01 +0800695 FakeChrootEnvironment or DUTEnvironment as appropriate).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800696 '''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800697 parser = OptionParser()
698 parser.add_option('-v', '--verbose', dest='verbose',
699 action='store_true',
700 help='Enable debug logging')
701 parser.add_option('--print_test_list', dest='print_test_list',
702 metavar='FILE',
703 help='Read and print test list FILE, and exit')
Jon Salz758e6cc2012-04-03 15:47:07 +0800704 parser.add_option('--restart', dest='restart',
705 action='store_true',
706 help='Clear all test state')
Jon Salz258a40c2012-04-19 12:34:01 +0800707 parser.add_option('--ui', dest='ui', type='choice',
708 choices=['none', 'gtk', 'chrome'],
709 default='gtk',
710 help='UI to use')
Jon Salz63585ea2012-05-21 15:03:32 +0800711 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
712 type='int', default=1,
713 help=('Factor by which to scale UI '
714 '(Chrome UI only)'))
Jon Salz73e0fd02012-04-04 11:46:38 +0800715 parser.add_option('--test_list', dest='test_list',
716 metavar='FILE',
717 help='Use FILE as test list')
718 (self.options, self.args) = parser.parse_args(args)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800719
Jon Salz73e0fd02012-04-04 11:46:38 +0800720 global _inited_logging
721 if not _inited_logging:
722 factory.init_logging('goofy', verbose=self.options.verbose)
723 _inited_logging = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800724 self.event_log = EventLog('goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800725
Jon Salz73e0fd02012-04-04 11:46:38 +0800726 if (not suppress_chroot_warning and
727 factory.in_chroot() and
Jon Salz258a40c2012-04-19 12:34:01 +0800728 self.options.ui == 'gtk' and
Jon Salz758e6cc2012-04-03 15:47:07 +0800729 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
730 # That's not going to work! Tell the user how to run
731 # this way.
732 logging.warn(GOOFY_IN_CHROOT_WARNING)
733 time.sleep(1)
734
Jon Salz73e0fd02012-04-04 11:46:38 +0800735 if env:
736 self.env = env
737 elif factory.in_chroot():
Jon Salz5f2a0672012-05-22 17:14:06 +0800738 self.env = test_environment.FakeChrootEnvironment()
Jon Salz73e0fd02012-04-04 11:46:38 +0800739 logging.warn(
740 'Using chroot environment: will not actually run autotests')
741 else:
Jon Salz5f2a0672012-05-22 17:14:06 +0800742 self.env = test_environment.DUTEnvironment()
Jon Salz323dd3d2012-04-09 18:40:43 +0800743 self.env.goofy = self
Jon Salz73e0fd02012-04-04 11:46:38 +0800744
Jon Salz758e6cc2012-04-03 15:47:07 +0800745 if self.options.restart:
746 state.clear_state()
747
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800748 if self.options.print_test_list:
749 print (factory.read_test_list(self.options.print_test_list).
750 __repr__(recursive=True))
751 return
752
Jon Salz7c15e8b2012-06-19 17:10:37 +0800753 if self.options.ui_scale_factor != 1 and utils.in_qemu():
Jon Salz9b312912012-06-04 11:27:00 +0800754 logging.warn(
755 'In QEMU; ignoring ui_scale_factor argument')
756 self.options.ui_scale_factor = 1
757
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800758 logging.info('Started')
759
760 self.start_state_server()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800761 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz63585ea2012-05-21 15:03:32 +0800762 self.state_instance.set_shared_data('ui_scale_factor',
763 self.options.ui_scale_factor)
Jon Salz4f6c7172012-06-11 20:45:36 +0800764 self.last_shutdown_time = (
765 self.state_instance.get_shared_data('shutdown_time', optional=True))
766 self.state_instance.del_shared_data('shutdown_time', optional=True)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800767
Jon Salz2f757d42012-06-27 17:06:42 +0800768 if not self.options.test_list:
769 self.options.test_list = find_test_list()
770 if not self.options.test_list:
771 logging.error('No test list. Aborting.')
772 sys.exit(1)
773 logging.info('Using test list %s', self.options.test_list)
774
Jon Salz73e0fd02012-04-04 11:46:38 +0800775 self.test_list = factory.read_test_list(self.options.test_list,
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800776 self.state_instance)
Jon Salz06fbeff2012-05-21 17:06:05 +0800777 if not self.state_instance.has_shared_data('ui_lang'):
778 self.state_instance.set_shared_data('ui_lang',
779 self.test_list.options.ui_lang)
Jon Salzdbf398f2012-06-14 17:30:01 +0800780 self.state_instance.set_shared_data(
781 'test_list_options',
782 self.test_list.options.__dict__)
Jon Salz258a40c2012-04-19 12:34:01 +0800783 self.state_instance.test_list = self.test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800784
785 self.init_states()
786 self.start_event_server()
Jon Salz7c15e8b2012-06-19 17:10:37 +0800787 self.connection_manager = self.env.create_connection_manager(
788 self.test_list.options.wlans)
Jon Salz258a40c2012-04-19 12:34:01 +0800789
Jon Salz5f2a0672012-05-22 17:14:06 +0800790 self.update_system_info()
791
Jon Salz5da61e62012-05-31 13:06:22 +0800792 os.environ['CROS_FACTORY'] = '1'
Jon Salzeebd12e2012-06-08 15:34:56 +0800793 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
Jon Salz5da61e62012-05-31 13:06:22 +0800794
Jon Salzb1b39092012-05-03 02:05:09 +0800795 # Set CROS_UI since some behaviors in ui.py depend on the
796 # particular UI in use. TODO(jsalz): Remove this (and all
797 # places it is used) when the GTK UI is removed.
798 os.environ['CROS_UI'] = self.options.ui
Jon Salz258a40c2012-04-19 12:34:01 +0800799
Jon Salzb1b39092012-05-03 02:05:09 +0800800 if self.options.ui == 'chrome':
Jon Salz258a40c2012-04-19 12:34:01 +0800801 self.env.launch_chrome()
802 logging.info('Waiting for a web socket connection')
803 self.web_socket_manager.wait()
Jon Salzb1b39092012-05-03 02:05:09 +0800804
805 # Wait for the test widget size to be set; this is done in
806 # an asynchronous RPC so there is a small chance that the
807 # web socket might be opened first.
808 for i in range(100): # 10 s
Jon Salz63585ea2012-05-21 15:03:32 +0800809 try:
810 if self.state_instance.get_shared_data('test_widget_size'):
811 break
812 except KeyError:
813 pass # Retry
Jon Salzb1b39092012-05-03 02:05:09 +0800814 time.sleep(0.1) # 100 ms
815 else:
816 logging.warn('Never received test_widget_size from UI')
Jon Salz258a40c2012-04-19 12:34:01 +0800817 elif self.options.ui == 'gtk':
Jon Salz73e0fd02012-04-04 11:46:38 +0800818 self.start_ui()
Jon Salz258a40c2012-04-19 12:34:01 +0800819
Jon Salz94eb56f2012-06-12 18:01:12 +0800820 for handler in self.on_ui_startup:
821 handler()
822
Jon Salz8375c2e2012-04-04 15:22:24 +0800823 self.prespawner = Prespawner()
Jon Salz323dd3d2012-04-09 18:40:43 +0800824 self.prespawner.start()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800825
826 def state_change_callback(test, state):
827 self.event_client.post_event(
828 Event(Event.Type.STATE_CHANGE,
829 path=test.path, state=state))
830 self.test_list.state_change_callback = state_change_callback
831
832 try:
Jon Salz758e6cc2012-04-03 15:47:07 +0800833 tests_after_shutdown = self.state_instance.get_shared_data(
834 'tests_after_shutdown')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800835 except KeyError:
Jon Salz758e6cc2012-04-03 15:47:07 +0800836 tests_after_shutdown = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800837
Jon Salz758e6cc2012-04-03 15:47:07 +0800838 if tests_after_shutdown is not None:
839 logging.info('Resuming tests after shutdown: %s',
840 tests_after_shutdown)
841 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800842 self.tests_to_run.extend(
Jon Salz758e6cc2012-04-03 15:47:07 +0800843 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Jon Salz73e0fd02012-04-04 11:46:38 +0800844 self.run_queue.put(self.run_next_test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800845 else:
Jon Salz57717ca2012-04-04 16:47:25 +0800846 if self.test_list.options.auto_run_on_start:
847 self.run_queue.put(
848 lambda: self.run_tests(self.test_list, untested_only=True))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800849
Jon Salz73e0fd02012-04-04 11:46:38 +0800850 def run(self):
851 '''Runs Goofy.'''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800852 # Process events forever.
Jon Salz57717ca2012-04-04 16:47:25 +0800853 while self.run_once(True):
Jon Salz73e0fd02012-04-04 11:46:38 +0800854 pass
855
Jon Salz57717ca2012-04-04 16:47:25 +0800856 def run_once(self, block=False):
Jon Salz73e0fd02012-04-04 11:46:38 +0800857 '''Runs all items pending in the event loop.
858
Jon Salz57717ca2012-04-04 16:47:25 +0800859 Args:
860 block: If true, block until at least one event is processed.
861
Jon Salz73e0fd02012-04-04 11:46:38 +0800862 Returns:
863 True to keep going or False to shut down.
864 '''
Jon Salz7c15e8b2012-06-19 17:10:37 +0800865 events = utils.DrainQueue(self.run_queue)
866 if not events:
867 # Nothing on the run queue.
868 self._run_queue_idle()
869 if block:
870 # Block for at least one event...
871 events.append(self.run_queue.get())
872 # ...and grab anything else that showed up at the same
873 # time.
874 events.extend(utils.DrainQueue(self.run_queue))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800875
Jon Salz73e0fd02012-04-04 11:46:38 +0800876 for event in events:
877 if not event:
878 # Shutdown request.
879 self.run_queue.task_done()
880 return False
881
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800882 try:
883 event()
884 except Exception as e: # pylint: disable=W0703
885 logging.error('Error in event loop: %s', e)
886 traceback.print_exc(sys.stderr)
Jon Salz8375c2e2012-04-04 15:22:24 +0800887 self.record_exception(traceback.format_exception_only(
888 *sys.exc_info()[:2]))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800889 # But keep going
890 finally:
891 self.run_queue.task_done()
Jon Salz73e0fd02012-04-04 11:46:38 +0800892 return True
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800893
Jon Salz7c15e8b2012-06-19 17:10:37 +0800894 def _run_queue_idle(self):
895 '''Invoked when the run queue has no events.'''
896 self.check_connection_manager()
897
Jon Salz258a40c2012-04-19 12:34:01 +0800898 def run_tests_with_status(self, statuses_to_run, starting_at=None,
899 root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800900 '''Runs all top-level tests with a particular status.
901
902 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +0800903
904 Args:
905 starting_at: If provided, only auto-runs tests beginning with
906 this test.
Jon Salz0405ab52012-03-16 15:26:52 +0800907 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800908 root = root or self.test_list
909
Jon Salz57717ca2012-04-04 16:47:25 +0800910 if starting_at:
911 # Make sure they passed a test, not a string.
912 assert isinstance(starting_at, factory.FactoryTest)
913
Jon Salz0405ab52012-03-16 15:26:52 +0800914 tests_to_reset = []
915 tests_to_run = []
916
Jon Salz57717ca2012-04-04 16:47:25 +0800917 found_starting_at = False
918
Jon Salz258a40c2012-04-19 12:34:01 +0800919 for test in root.get_top_level_tests():
Jon Salz57717ca2012-04-04 16:47:25 +0800920 if starting_at:
921 if test == starting_at:
922 # We've found starting_at; do auto-run on all
923 # subsequent tests.
924 found_starting_at = True
925 if not found_starting_at:
926 # Don't start this guy yet
927 continue
928
Jon Salz0405ab52012-03-16 15:26:52 +0800929 status = test.get_state().status
930 if status == TestState.ACTIVE or status in statuses_to_run:
931 # Reset the test (later; we will need to abort
932 # all active tests first).
933 tests_to_reset.append(test)
934 if status in statuses_to_run:
935 tests_to_run.append(test)
936
937 self.abort_active_tests()
938
939 # Reset all statuses of the tests to run (in case any tests were active;
940 # we want them to be run again).
941 for test_to_reset in tests_to_reset:
942 for test in test_to_reset.walk():
943 test.update_state(status=TestState.UNTESTED)
944
945 self.run_tests(tests_to_run, untested_only=True)
946
Jon Salz258a40c2012-04-19 12:34:01 +0800947 def restart_tests(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800948 '''Restarts all tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800949 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +0800950
Jon Salz258a40c2012-04-19 12:34:01 +0800951 self.abort_active_tests()
952 for test in root.walk():
953 test.update_state(status=TestState.UNTESTED)
954 self.run_tests(root)
955
956 def auto_run(self, starting_at=None, root=None):
Jon Salz57717ca2012-04-04 16:47:25 +0800957 '''"Auto-runs" tests that have not been run yet.
958
959 Args:
960 starting_at: If provide, only auto-runs tests beginning with
961 this test.
962 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800963 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +0800964 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Jon Salz258a40c2012-04-19 12:34:01 +0800965 starting_at=starting_at,
966 root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800967
Jon Salz258a40c2012-04-19 12:34:01 +0800968 def re_run_failed(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800969 '''Re-runs failed tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800970 root = root or self.test_list
971 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800972
Jon Salz968e90b2012-03-18 16:12:43 +0800973 def show_review_information(self):
Hung-Te Lin96632362012-03-20 21:14:18 +0800974 '''Event handler for showing review information screen.
975
976 The information screene is rendered by main UI program (ui.py), so in
977 goofy we only need to kill all active tests, set them as untested, and
978 clear remaining tests.
979 '''
980 self.kill_active_tests(False)
981 self.run_tests([])
982
Jon Salz0405ab52012-03-16 15:26:52 +0800983 def handle_switch_test(self, event):
Jon Salz968e90b2012-03-18 16:12:43 +0800984 '''Switches to a particular test.
985
986 @param event: The SWITCH_TEST event.
987 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800988 test = self.test_list.lookup_path(event.path)
Jon Salz57717ca2012-04-04 16:47:25 +0800989 if not test:
Jon Salz968e90b2012-03-18 16:12:43 +0800990 logging.error('Unknown test %r', event.key)
Jon Salz57717ca2012-04-04 16:47:25 +0800991 return
992
993 invoc = self.invocations.get(test)
994 if invoc and test.backgroundable:
995 # Already running: just bring to the front if it
996 # has a UI.
997 logging.info('Setting visible test to %s', test.path)
998 self.event_client.post_event(
999 Event(Event.Type.SET_VISIBLE_TEST, path=test.path))
1000 return
1001
1002 self.abort_active_tests()
1003 for t in test.walk():
1004 t.update_state(status=TestState.UNTESTED)
1005
1006 if self.test_list.options.auto_run_on_keypress:
1007 self.auto_run(starting_at=test)
1008 else:
1009 self.run_tests(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001010
Jon Salz73e0fd02012-04-04 11:46:38 +08001011 def wait(self):
1012 '''Waits for all pending invocations.
1013
1014 Useful for testing.
1015 '''
1016 for k, v in self.invocations.iteritems():
1017 logging.info('Waiting for %s to complete...', k)
1018 v.thread.join()
1019
1020 def check_exceptions(self):
1021 '''Raises an error if any exceptions have occurred in
1022 invocation threads.'''
1023 if self.exceptions:
1024 raise RuntimeError('Exception in invocation thread: %r' %
1025 self.exceptions)
1026
1027 def record_exception(self, msg):
1028 '''Records an exception in an invocation thread.
1029
1030 An exception with the given message will be rethrown when
1031 Goofy is destroyed.'''
1032 self.exceptions.append(msg)
1033
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001034
1035if __name__ == '__main__':
1036 Goofy().main()