blob: b69f1b6bec86dfdbad43f38b9900a2cef5c0098d [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
Jon Salz51528e12012-07-02 18:54:45 +080041from cros.factory.goofy import test_steps
42from cros.factory.goofy.event_log_watcher import EventLogWatcher
43from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080044from cros.factory.test import utils
45from cros.factory.test.event import Event
46from cros.factory.test.event import EventClient
47from cros.factory.test.event import EventServer
48from cros.factory.event_log import EventLog
49from cros.factory.goofy.invocation import TestInvocation
50from cros.factory.goofy import system
51from cros.factory.goofy import test_environment
52from cros.factory.goofy.web_socket_manager import WebSocketManager
Hung-Te Linf2f78f72012-02-08 19:27:11 +080053
54
Jon Salz2f757d42012-06-27 17:06:42 +080055DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
56CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080057HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
58
Jon Salz8796e362012-05-24 11:39:09 +080059# File that suppresses reboot if present (e.g., for development).
60NO_REBOOT_FILE = '/var/log/factory.noreboot'
61
Jon Salz758e6cc2012-04-03 15:47:07 +080062GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
63You are running Goofy inside the chroot. Autotests are not supported.
64
65To use Goofy in the chroot, first install an Xvnc server:
66
67 sudo apt-get install tightvncserver
68
69...and then start a VNC X server outside the chroot:
70
71 vncserver :10 &
72 vncviewer :10
73
74...and run Goofy as follows:
75
76 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
77''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080078suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080079
80def get_hwid_cfg():
81 '''
82 Returns the HWID config tag, or an empty string if none can be found.
83 '''
84 if 'CROS_HWID' in os.environ:
85 return os.environ['CROS_HWID']
86 if os.path.exists(HWID_CFG_PATH):
87 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
88 return hwid_cfg_handle.read().strip()
89 return ''
90
91
92def find_test_list():
93 '''
94 Returns the path to the active test list, based on the HWID config tag.
95 '''
96 hwid_cfg = get_hwid_cfg()
97
Jon Salz2f757d42012-06-27 17:06:42 +080098 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
99
100 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
101 search_files = ['test_list', 'test_list.all']
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800102 if hwid_cfg:
Jon Salz2f757d42012-06-27 17:06:42 +0800103 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800104
Jon Salz2f757d42012-06-27 17:06:42 +0800105 for d in search_dirs:
106 for f in search_files:
107 test_list = os.path.join(d, f)
108 if os.path.exists(test_list):
109 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800110
Jon Salz2f757d42012-06-27 17:06:42 +0800111 logging.warn('Cannot find test lists named any of %s in any of %s',
112 search_files, search_dirs)
113 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800114
Jon Salz73e0fd02012-04-04 11:46:38 +0800115_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800116
117class Goofy(object):
118 '''
119 The main factory flow.
120
121 Note that all methods in this class must be invoked from the main
122 (event) thread. Other threads, such as callbacks and TestInvocation
123 methods, should instead post events on the run queue.
124
125 TODO: Unit tests. (chrome-os-partner:7409)
126
127 Properties:
Jon Salz258a40c2012-04-19 12:34:01 +0800128 uuid: A unique UUID for this invocation of Goofy.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800129 state_instance: An instance of FactoryState.
130 state_server: The FactoryState XML/RPC server.
131 state_server_thread: A thread running state_server.
132 event_server: The EventServer socket server.
133 event_server_thread: A thread running event_server.
134 event_client: A client to the event server.
Jon Salz7c15e8b2012-06-19 17:10:37 +0800135 connection_manager: The connection_manager object.
136 network_enabled: Whether the connection_manager is currently
137 enabling connections.
Hung-Te Lin6bb48552012-02-09 14:37:43 +0800138 ui_process: The factory ui process object.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800139 run_queue: A queue of callbacks to invoke from the main thread.
140 invocations: A map from FactoryTest objects to the corresponding
141 TestInvocations objects representing active tests.
142 tests_to_run: A deque of tests that should be run when the current
143 test(s) complete.
144 options: Command-line options.
145 args: Command-line args.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800146 test_list: The test list.
Jon Salz0405ab52012-03-16 15:26:52 +0800147 event_handlers: Map of Event.Type to the method used to handle that
148 event. If the method has an 'event' argument, the event is passed
149 to the handler.
Jon Salz73e0fd02012-04-04 11:46:38 +0800150 exceptions: Exceptions encountered in invocation threads.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800151 '''
152 def __init__(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800153 self.uuid = str(uuid.uuid4())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800154 self.state_instance = None
155 self.state_server = None
156 self.state_server_thread = None
157 self.event_server = None
158 self.event_server_thread = None
159 self.event_client = None
Jon Salz7c15e8b2012-06-19 17:10:37 +0800160 self.connection_manager = None
Jon Salz51528e12012-07-02 18:54:45 +0800161 self.log_watcher = None
Jon Salz7c15e8b2012-06-19 17:10:37 +0800162 self.network_enabled = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800163 self.event_log = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800164 self.prespawner = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800165 self.ui_process = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800166 self.run_queue = Queue.Queue()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800167 self.invocations = {}
168 self.tests_to_run = deque()
169 self.visible_test = None
Jon Salz258a40c2012-04-19 12:34:01 +0800170 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800171
172 self.options = None
173 self.args = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800174 self.test_list = None
Jon Salz94eb56f2012-06-12 18:01:12 +0800175 self.on_ui_startup = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800176
Jon Salz258a40c2012-04-19 12:34:01 +0800177 def test_or_root(event):
178 '''Returns the top-level parent for a test (the root node of the
179 tests that need to be run together if the given test path is to
180 be run).'''
181 try:
182 path = event.path
Jon Salzd2ed6cb2012-05-02 09:35:14 +0800183 except AttributeError:
Jon Salz258a40c2012-04-19 12:34:01 +0800184 path = None
185
186 if path:
Jon Salzf617b282012-05-24 14:14:04 +0800187 return (self.test_list.lookup_path(path).
188 get_top_level_parent_or_group())
Jon Salz258a40c2012-04-19 12:34:01 +0800189 else:
190 return self.test_list
191
Jon Salz0405ab52012-03-16 15:26:52 +0800192 self.event_handlers = {
193 Event.Type.SWITCH_TEST: self.handle_switch_test,
Jon Salz968e90b2012-03-18 16:12:43 +0800194 Event.Type.SHOW_NEXT_ACTIVE_TEST:
195 lambda event: self.show_next_active_test(),
196 Event.Type.RESTART_TESTS:
Jon Salz258a40c2012-04-19 12:34:01 +0800197 lambda event: self.restart_tests(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800198 Event.Type.AUTO_RUN:
Jon Salz258a40c2012-04-19 12:34:01 +0800199 lambda event: self.auto_run(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800200 Event.Type.RE_RUN_FAILED:
Jon Salz258a40c2012-04-19 12:34:01 +0800201 lambda event: self.re_run_failed(root=test_or_root(event)),
Jon Salz2eaae372012-06-14 18:11:27 +0800202 Event.Type.RUN_TESTS_WITH_STATUS:
203 lambda event: self.run_tests_with_status(
204 event.status,
205 root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800206 Event.Type.REVIEW:
207 lambda event: self.show_review_information(),
Jon Salz5f2a0672012-05-22 17:14:06 +0800208 Event.Type.UPDATE_SYSTEM_INFO:
209 lambda event: self.update_system_info(),
Jon Salz37eccbd2012-05-25 16:06:52 +0800210 Event.Type.UPDATE_FACTORY:
211 lambda event: self.update_factory(),
Jon Salzf00cdc82012-05-28 18:56:17 +0800212 Event.Type.STOP:
213 lambda event: self.stop(),
Jon Salz0405ab52012-03-16 15:26:52 +0800214 }
215
Jon Salz73e0fd02012-04-04 11:46:38 +0800216 self.exceptions = []
Jon Salz258a40c2012-04-19 12:34:01 +0800217 self.web_socket_manager = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800218
219 def destroy(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800220 if self.chrome:
221 self.chrome.kill()
222 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800223 if self.ui_process:
Jon Salz258a40c2012-04-19 12:34:01 +0800224 utils.kill_process_tree(self.ui_process, 'ui')
Jon Salz73e0fd02012-04-04 11:46:38 +0800225 self.ui_process = None
Jon Salz258a40c2012-04-19 12:34:01 +0800226 if self.web_socket_manager:
227 logging.info('Stopping web sockets')
228 self.web_socket_manager.close()
229 self.web_socket_manager = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800230 if self.state_server_thread:
231 logging.info('Stopping state server')
232 self.state_server.shutdown()
233 self.state_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800234 self.state_server.server_close()
235 self.state_server_thread = None
Jon Salz66f65e62012-05-24 17:40:26 +0800236 if self.state_instance:
237 self.state_instance.close()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800238 if self.event_server_thread:
239 logging.info('Stopping event server')
240 self.event_server.shutdown() # pylint: disable=E1101
241 self.event_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800242 self.event_server.server_close()
243 self.event_server_thread = None
Jon Salz51528e12012-07-02 18:54:45 +0800244 if self.log_watcher:
245 if self.log_watcher.IsThreadStarted():
246 self.log_watcher.StopWatchThread()
247 self.log_watcher = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800248 if self.prespawner:
249 logging.info('Stopping prespawner')
250 self.prespawner.stop()
251 self.prespawner = None
252 if self.event_client:
Jon Salz258a40c2012-04-19 12:34:01 +0800253 logging.info('Closing event client')
Jon Salz8375c2e2012-04-04 15:22:24 +0800254 self.event_client.close()
Jon Salz258a40c2012-04-19 12:34:01 +0800255 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800256 if self.event_log:
257 self.event_log.Close()
258 self.event_log = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800259 self.check_exceptions()
Jon Salz258a40c2012-04-19 12:34:01 +0800260 logging.info('Done destroying Goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800261
262 def start_state_server(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800263 self.state_instance, self.state_server = (
264 state.create_server(bind_address='0.0.0.0'))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800265 logging.info('Starting state server')
266 self.state_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800267 target=self.state_server.serve_forever,
268 name='StateServer')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800269 self.state_server_thread.start()
270
271 def start_event_server(self):
272 self.event_server = EventServer()
273 logging.info('Starting factory event server')
274 self.event_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800275 target=self.event_server.serve_forever,
276 name='EventServer') # pylint: disable=E1101
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800277 self.event_server_thread.start()
278
279 self.event_client = EventClient(
280 callback=self.handle_event, event_loop=self.run_queue)
281
Jon Salz258a40c2012-04-19 12:34:01 +0800282 self.web_socket_manager = WebSocketManager(self.uuid)
283 self.state_server.add_handler("/event",
284 self.web_socket_manager.handle_web_socket)
285
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800286 def start_ui(self):
Jon Salz6d0f8202012-07-02 14:02:25 +0800287 ui_proc_args = [
288 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
289 self.options.test_list]
Jon Salz14bcbb02012-03-17 15:11:50 +0800290 if self.options.verbose:
291 ui_proc_args.append('-v')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800292 logging.info('Starting ui %s', ui_proc_args)
293 self.ui_process = subprocess.Popen(ui_proc_args)
294 logging.info('Waiting for UI to come up...')
295 self.event_client.wait(
296 lambda event: event.type == Event.Type.UI_READY)
297 logging.info('UI has started')
298
299 def set_visible_test(self, test):
300 if self.visible_test == test:
301 return
302
303 if test:
304 test.update_state(visible=True)
305 if self.visible_test:
306 self.visible_test.update_state(visible=False)
307 self.visible_test = test
308
Jon Salz74ad3262012-03-16 14:40:55 +0800309 def handle_shutdown_complete(self, test, state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800310 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800311 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800312
Jon Salz74ad3262012-03-16 14:40:55 +0800313 @param test: The ShutdownStep.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800314 @param state: The test state.
315 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800316 state = test.update_state(increment_shutdown_count=1)
317 logging.info('Detected shutdown (%d of %d)',
318 state.shutdown_count, test.iterations)
Jon Salz4f6c7172012-06-11 20:45:36 +0800319
320 def log_and_update_state(status, error_msg, **kw):
321 self.event_log.Log('rebooted',
322 status=status, error_msg=error_msg, **kw)
323 test.update_state(status=status, error_msg=error_msg)
324
325 if not self.last_shutdown_time:
326 log_and_update_state(status=TestState.FAILED,
327 error_msg='Unable to read shutdown_time')
328 return
329
330 now = time.time()
331 logging.info('%.03f s passed since reboot',
332 now - self.last_shutdown_time)
333
334 if self.last_shutdown_time > now:
335 test.update_state(status=TestState.FAILED,
336 error_msg='Time moved backward during reboot')
337 elif (isinstance(test, factory.RebootStep) and
338 self.test_list.options.max_reboot_time_secs and
339 (now - self.last_shutdown_time >
340 self.test_list.options.max_reboot_time_secs)):
341 # A reboot took too long; fail. (We don't check this for
342 # HaltSteps, because the machine could be halted for a
343 # very long time, and even unplugged with battery backup,
344 # thus hosing the clock.)
345 log_and_update_state(
346 status=TestState.FAILED,
347 error_msg=('More than %d s elapsed during reboot '
348 '(%.03f s, from %s to %s)' % (
349 self.test_list.options.max_reboot_time_secs,
350 now - self.last_shutdown_time,
351 utils.TimeString(self.last_shutdown_time),
352 utils.TimeString(now))),
353 duration=(now-self.last_shutdown_time))
354 elif state.shutdown_count == test.iterations:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800355 # Good!
Jon Salz4f6c7172012-06-11 20:45:36 +0800356 log_and_update_state(status=TestState.PASSED,
357 duration=(now - self.last_shutdown_time),
358 error_msg='')
Jon Salz74ad3262012-03-16 14:40:55 +0800359 elif state.shutdown_count > test.iterations:
Jon Salz73e0fd02012-04-04 11:46:38 +0800360 # Shut down too many times
Jon Salz4f6c7172012-06-11 20:45:36 +0800361 log_and_update_state(status=TestState.FAILED,
362 error_msg='Too many shutdowns')
Jon Salz258a40c2012-04-19 12:34:01 +0800363 elif utils.are_shift_keys_depressed():
Jon Salz73e0fd02012-04-04 11:46:38 +0800364 logging.info('Shift keys are depressed; cancelling restarts')
365 # Abort shutdown
Jon Salz4f6c7172012-06-11 20:45:36 +0800366 log_and_update_state(
Jon Salz73e0fd02012-04-04 11:46:38 +0800367 status=TestState.FAILED,
368 error_msg='Shutdown aborted with double shift keys')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800369 else:
Jon Salz94eb56f2012-06-12 18:01:12 +0800370 def handler():
371 if self._prompt_cancel_shutdown(test, state.shutdown_count + 1):
372 log_and_update_state(
373 status=TestState.FAILED,
374 error_msg='Shutdown aborted by operator')
375 return
376
377 # Time to shutdown again
378 log_and_update_state(
379 status=TestState.ACTIVE,
380 error_msg='',
381 iteration=state.shutdown_count)
382
383 self.event_log.Log('shutdown', operation='reboot')
384 self.state_instance.set_shared_data('shutdown_time',
Jon Salz4f6c7172012-06-11 20:45:36 +0800385 time.time())
Jon Salz94eb56f2012-06-12 18:01:12 +0800386 self.env.shutdown('reboot')
387
388 self.on_ui_startup.append(handler)
389
390 def _prompt_cancel_shutdown(self, test, iteration):
391 if self.options.ui != 'chrome':
392 return False
393
394 pending_shutdown_data = {
395 'delay_secs': test.delay_secs,
396 'time': time.time() + test.delay_secs,
397 'operation': test.operation,
398 'iteration': iteration,
399 'iterations': test.iterations,
400 }
401
402 # Create a new (threaded) event client since we
403 # don't want to use the event loop for this.
404 with EventClient() as event_client:
405 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
406 **pending_shutdown_data))
407 aborted = event_client.wait(
408 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
409 timeout=test.delay_secs) is not None
410 if aborted:
411 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
412 return aborted
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800413
414 def init_states(self):
415 '''
416 Initializes all states on startup.
417 '''
418 for test in self.test_list.get_all_tests():
419 # Make sure the state server knows about all the tests,
420 # defaulting to an untested state.
421 test.update_state(update_parent=False, visible=False)
422
Jon Salzd6361c22012-06-11 22:23:57 +0800423 var_log_messages = None
424
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800425 # Any 'active' tests should be marked as failed now.
426 for test in self.test_list.walk():
427 state = test.get_state()
Hung-Te Lin96632362012-03-20 21:14:18 +0800428 if state.status != TestState.ACTIVE:
429 continue
430 if isinstance(test, factory.ShutdownStep):
431 # Shutdown while the test was active - that's good.
432 self.handle_shutdown_complete(test, state)
433 else:
Jon Salzd6361c22012-06-11 22:23:57 +0800434 # Unexpected shutdown. Grab /var/log/messages for context.
435 if var_log_messages is None:
436 try:
437 var_log_messages = (
438 utils.var_log_messages_before_reboot())
439 # Write it to the log, to make it easier to
440 # correlate with /var/log/messages.
441 logging.info(
442 'Unexpected shutdown. '
443 'Tail of /var/log/messages before last reboot:\n'
444 '%s', ('\n'.join(
445 ' ' + x for x in var_log_messages)))
446 except:
447 logging.exception('Unable to grok /var/log/messages')
448 var_log_messages = []
449
450 error_msg = 'Unexpected shutdown while test was running'
451 self.event_log.Log('end_test',
452 path=test.path,
453 status=TestState.FAILED,
454 invocation=test.get_state().invocation,
455 error_msg=error_msg,
456 var_log_messages='\n'.join(var_log_messages))
457 test.update_state(
458 status=TestState.FAILED,
459 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800460
461 def show_next_active_test(self):
462 '''
463 Rotates to the next visible active test.
464 '''
465 self.reap_completed_tests()
466 active_tests = [
467 t for t in self.test_list.walk()
468 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
469 if not active_tests:
470 return
471
472 try:
473 next_test = active_tests[
474 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
475 except ValueError: # visible_test not present in active_tests
476 next_test = active_tests[0]
477
478 self.set_visible_test(next_test)
479
480 def handle_event(self, event):
481 '''
482 Handles an event from the event server.
483 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800484 handler = self.event_handlers.get(event.type)
485 if handler:
Jon Salz968e90b2012-03-18 16:12:43 +0800486 handler(event)
Jon Salz0405ab52012-03-16 15:26:52 +0800487 else:
Jon Salz968e90b2012-03-18 16:12:43 +0800488 # We don't register handlers for all event types - just ignore
489 # this event.
490 logging.debug('Unbound event type %s', event.type)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800491
492 def run_next_test(self):
493 '''
494 Runs the next eligible test (or tests) in self.tests_to_run.
495 '''
496 self.reap_completed_tests()
497 while self.tests_to_run:
498 logging.debug('Tests to run: %s',
499 [x.path for x in self.tests_to_run])
500
501 test = self.tests_to_run[0]
502
503 if test in self.invocations:
504 logging.info('Next test %s is already running', test.path)
505 self.tests_to_run.popleft()
506 return
507
508 if self.invocations and not (test.backgroundable and all(
509 [x.backgroundable for x in self.invocations])):
510 logging.debug('Waiting for non-backgroundable tests to '
511 'complete before running %s', test.path)
512 return
513
514 self.tests_to_run.popleft()
515
Jon Salz74ad3262012-03-16 14:40:55 +0800516 if isinstance(test, factory.ShutdownStep):
Jon Salz8796e362012-05-24 11:39:09 +0800517 if os.path.exists(NO_REBOOT_FILE):
518 test.update_state(
519 status=TestState.FAILED, increment_count=1,
520 error_msg=('Skipped shutdown since %s is present' %
521 NO_REBOOT_FILE))
522 continue
523
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800524 test.update_state(status=TestState.ACTIVE, increment_count=1,
Jon Salz74ad3262012-03-16 14:40:55 +0800525 error_msg='', shutdown_count=0)
Jon Salz94eb56f2012-06-12 18:01:12 +0800526 if self._prompt_cancel_shutdown(test, 1):
527 self.event_log.Log('reboot_cancelled')
528 test.update_state(
529 status=TestState.FAILED, increment_count=1,
530 error_msg='Shutdown aborted by operator',
531 shutdown_count=0)
532 return
533
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800534 # Save pending test list in the state server
535 self.state_instance.set_shared_data(
Jon Salz74ad3262012-03-16 14:40:55 +0800536 'tests_after_shutdown',
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800537 [t.path for t in self.tests_to_run])
Jon Salz4f6c7172012-06-11 20:45:36 +0800538 # Save shutdown time
539 self.state_instance.set_shared_data('shutdown_time',
540 time.time())
Jon Salz74ad3262012-03-16 14:40:55 +0800541
Jon Salz73e0fd02012-04-04 11:46:38 +0800542 with self.env.lock:
Jon Salzb9038572012-05-24 10:34:51 +0800543 self.event_log.Log('shutdown', operation=test.operation)
Jon Salz73e0fd02012-04-04 11:46:38 +0800544 shutdown_result = self.env.shutdown(test.operation)
545 if shutdown_result:
546 # That's all, folks!
547 self.run_queue.put(None)
548 return
549 else:
550 # Just pass (e.g., in the chroot).
551 test.update_state(status=TestState.PASSED)
552 self.state_instance.set_shared_data(
553 'tests_after_shutdown', None)
Jon Salz94eb56f2012-06-12 18:01:12 +0800554 # Send event with no fields to indicate that there is no
555 # longer a pending shutdown.
556 self.event_client.post_event(Event(
557 Event.Type.PENDING_SHUTDOWN))
Jon Salz73e0fd02012-04-04 11:46:38 +0800558 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800559
560 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
561 self.invocations[test] = invoc
562 if self.visible_test is None and test.has_ui:
563 self.set_visible_test(test)
Jon Salz7c15e8b2012-06-19 17:10:37 +0800564 self.check_connection_manager()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800565 invoc.start()
566
Jon Salz7c15e8b2012-06-19 17:10:37 +0800567 def check_connection_manager(self):
568 exclusive_tests = [
569 test.path
570 for test in self.invocations
571 if test.is_exclusive(
572 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
573 if exclusive_tests:
574 # Make sure networking is disabled.
575 if self.network_enabled:
576 logging.info('Disabling network, as requested by %s',
577 exclusive_tests)
578 self.connection_manager.DisableNetworking()
579 self.network_enabled = False
580 else:
581 # Make sure networking is enabled.
582 if not self.network_enabled:
583 logging.info('Re-enabling network')
584 self.connection_manager.EnableNetworking()
585 self.network_enabled = True
586
Jon Salz0405ab52012-03-16 15:26:52 +0800587 def run_tests(self, subtrees, untested_only=False):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800588 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800589 Runs tests under subtree.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800590
591 The tests are run in order unless one fails (then stops).
592 Backgroundable tests are run simultaneously; when a foreground test is
593 encountered, we wait for all active tests to finish before continuing.
Jon Salz0405ab52012-03-16 15:26:52 +0800594
595 @param subtrees: Node or nodes containing tests to run (may either be
596 a single test or a list). Duplicates will be ignored.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800597 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800598 if type(subtrees) != list:
599 subtrees = [subtrees]
600
601 # Nodes we've seen so far, to avoid duplicates.
602 seen = set()
603
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800604 self.tests_to_run = deque()
Jon Salz0405ab52012-03-16 15:26:52 +0800605 for subtree in subtrees:
606 for test in subtree.walk():
607 if test in seen:
608 continue
609 seen.add(test)
610
611 if not test.is_leaf():
612 continue
613 if (untested_only and
614 test.get_state().status != TestState.UNTESTED):
615 continue
616 self.tests_to_run.append(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800617 self.run_next_test()
618
619 def reap_completed_tests(self):
620 '''
621 Removes completed tests from the set of active tests.
622
623 Also updates the visible test if it was reaped.
624 '''
625 for t, v in dict(self.invocations).iteritems():
626 if v.is_completed():
627 del self.invocations[t]
628
629 if (self.visible_test is None or
630 self.visible_test not in self.invocations):
631 self.set_visible_test(None)
632 # Make the first running test, if any, the visible test
633 for t in self.test_list.walk():
634 if t in self.invocations:
635 self.set_visible_test(t)
636 break
637
Hung-Te Lin96632362012-03-20 21:14:18 +0800638 def kill_active_tests(self, abort):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800639 '''
640 Kills and waits for all active tests.
Hung-Te Lin96632362012-03-20 21:14:18 +0800641
642 @param abort: True to change state of killed tests to FAILED, False for
643 UNTESTED.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800644 '''
645 self.reap_completed_tests()
646 for test, invoc in self.invocations.items():
647 factory.console.info('Killing active test %s...' % test.path)
648 invoc.abort_and_join()
649 factory.console.info('Killed %s' % test.path)
650 del self.invocations[test]
Hung-Te Lin96632362012-03-20 21:14:18 +0800651 if not abort:
652 test.update_state(status=TestState.UNTESTED)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800653 self.reap_completed_tests()
654
Jon Salzf00cdc82012-05-28 18:56:17 +0800655 def stop(self):
656 self.kill_active_tests(False)
657 self.run_tests([])
658
Hung-Te Lin96632362012-03-20 21:14:18 +0800659 def abort_active_tests(self):
660 self.kill_active_tests(True)
661
Jon Salz73e0fd02012-04-04 11:46:38 +0800662 def main(self):
Jon Salzeb8d25f2012-05-22 15:17:32 +0800663 try:
664 self.init()
Jon Salzb9038572012-05-24 10:34:51 +0800665 self.event_log.Log('goofy_init',
666 success=True)
Jon Salzeb8d25f2012-05-22 15:17:32 +0800667 except:
668 if self.event_log:
669 try:
Jon Salzb9038572012-05-24 10:34:51 +0800670 self.event_log.Log('goofy_init',
671 success=False,
672 trace=traceback.format_exc())
Jon Salzeb8d25f2012-05-22 15:17:32 +0800673 except:
674 pass
675 raise
676
Jon Salz73e0fd02012-04-04 11:46:38 +0800677 self.run()
678
Jon Salz5f2a0672012-05-22 17:14:06 +0800679 def update_system_info(self):
680 '''Updates system info.'''
Jon Salz49a7d152012-06-19 15:04:09 +0800681 system_info = system.SystemInfo()
Jon Salz5f2a0672012-05-22 17:14:06 +0800682 self.state_instance.set_shared_data('system_info', system_info.__dict__)
683 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
684 system_info=system_info.__dict__))
685 logging.info('System info: %r', system_info.__dict__)
686
Jon Salz37eccbd2012-05-25 16:06:52 +0800687 def update_factory(self):
688 self.kill_active_tests(False)
689 self.run_tests([])
690
691 try:
692 if updater.TryUpdate(pre_update_hook=self.state_instance.close):
693 self.env.shutdown('reboot')
694 except:
695 factory.console.exception('Unable to update')
696
Jon Salz73e0fd02012-04-04 11:46:38 +0800697 def init(self, args=None, env=None):
698 '''Initializes Goofy.
Jon Salz74ad3262012-03-16 14:40:55 +0800699
700 Args:
Jon Salz73e0fd02012-04-04 11:46:38 +0800701 args: A list of command-line arguments. Uses sys.argv if
702 args is None.
703 env: An Environment instance to use (or None to choose
Jon Salz258a40c2012-04-19 12:34:01 +0800704 FakeChrootEnvironment or DUTEnvironment as appropriate).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800705 '''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800706 parser = OptionParser()
707 parser.add_option('-v', '--verbose', dest='verbose',
708 action='store_true',
709 help='Enable debug logging')
710 parser.add_option('--print_test_list', dest='print_test_list',
711 metavar='FILE',
712 help='Read and print test list FILE, and exit')
Jon Salz758e6cc2012-04-03 15:47:07 +0800713 parser.add_option('--restart', dest='restart',
714 action='store_true',
715 help='Clear all test state')
Jon Salz258a40c2012-04-19 12:34:01 +0800716 parser.add_option('--ui', dest='ui', type='choice',
717 choices=['none', 'gtk', 'chrome'],
718 default='gtk',
719 help='UI to use')
Jon Salz63585ea2012-05-21 15:03:32 +0800720 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
721 type='int', default=1,
722 help=('Factor by which to scale UI '
723 '(Chrome UI only)'))
Jon Salz73e0fd02012-04-04 11:46:38 +0800724 parser.add_option('--test_list', dest='test_list',
725 metavar='FILE',
726 help='Use FILE as test list')
727 (self.options, self.args) = parser.parse_args(args)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800728
Jon Salz73e0fd02012-04-04 11:46:38 +0800729 global _inited_logging
730 if not _inited_logging:
731 factory.init_logging('goofy', verbose=self.options.verbose)
732 _inited_logging = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800733 self.event_log = EventLog('goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800734
Jon Salz73e0fd02012-04-04 11:46:38 +0800735 if (not suppress_chroot_warning and
736 factory.in_chroot() and
Jon Salz258a40c2012-04-19 12:34:01 +0800737 self.options.ui == 'gtk' and
Jon Salz758e6cc2012-04-03 15:47:07 +0800738 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
739 # That's not going to work! Tell the user how to run
740 # this way.
741 logging.warn(GOOFY_IN_CHROOT_WARNING)
742 time.sleep(1)
743
Jon Salz73e0fd02012-04-04 11:46:38 +0800744 if env:
745 self.env = env
746 elif factory.in_chroot():
Jon Salz5f2a0672012-05-22 17:14:06 +0800747 self.env = test_environment.FakeChrootEnvironment()
Jon Salz73e0fd02012-04-04 11:46:38 +0800748 logging.warn(
749 'Using chroot environment: will not actually run autotests')
750 else:
Jon Salz5f2a0672012-05-22 17:14:06 +0800751 self.env = test_environment.DUTEnvironment()
Jon Salz323dd3d2012-04-09 18:40:43 +0800752 self.env.goofy = self
Jon Salz73e0fd02012-04-04 11:46:38 +0800753
Jon Salz758e6cc2012-04-03 15:47:07 +0800754 if self.options.restart:
755 state.clear_state()
756
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800757 if self.options.print_test_list:
Jon Salz51528e12012-07-02 18:54:45 +0800758 print (factory.read_test_list(
759 self.options.print_test_list,
760 test_classes=dict(test_steps.__dict__)).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800761 __repr__(recursive=True))
762 return
763
Jon Salz7c15e8b2012-06-19 17:10:37 +0800764 if self.options.ui_scale_factor != 1 and utils.in_qemu():
Jon Salz9b312912012-06-04 11:27:00 +0800765 logging.warn(
766 'In QEMU; ignoring ui_scale_factor argument')
767 self.options.ui_scale_factor = 1
768
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800769 logging.info('Started')
770
771 self.start_state_server()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800772 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz63585ea2012-05-21 15:03:32 +0800773 self.state_instance.set_shared_data('ui_scale_factor',
774 self.options.ui_scale_factor)
Jon Salz4f6c7172012-06-11 20:45:36 +0800775 self.last_shutdown_time = (
776 self.state_instance.get_shared_data('shutdown_time', optional=True))
777 self.state_instance.del_shared_data('shutdown_time', optional=True)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800778
Jon Salz2f757d42012-06-27 17:06:42 +0800779 if not self.options.test_list:
780 self.options.test_list = find_test_list()
781 if not self.options.test_list:
782 logging.error('No test list. Aborting.')
783 sys.exit(1)
784 logging.info('Using test list %s', self.options.test_list)
785
Jon Salz51528e12012-07-02 18:54:45 +0800786 self.test_list = factory.read_test_list(
787 self.options.test_list,
788 self.state_instance,
789 test_classes=dict(test_steps.__dict__))
Jon Salz06fbeff2012-05-21 17:06:05 +0800790 if not self.state_instance.has_shared_data('ui_lang'):
791 self.state_instance.set_shared_data('ui_lang',
792 self.test_list.options.ui_lang)
Jon Salzdbf398f2012-06-14 17:30:01 +0800793 self.state_instance.set_shared_data(
794 'test_list_options',
795 self.test_list.options.__dict__)
Jon Salz258a40c2012-04-19 12:34:01 +0800796 self.state_instance.test_list = self.test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800797
798 self.init_states()
799 self.start_event_server()
Jon Salz7c15e8b2012-06-19 17:10:37 +0800800 self.connection_manager = self.env.create_connection_manager(
801 self.test_list.options.wlans)
Jon Salz51528e12012-07-02 18:54:45 +0800802 # Note that we create a log watcher even if
803 # sync_event_log_period_secs isn't set (no background
804 # syncing), since we may use it to flush event logs as well.
805 self.log_watcher = EventLogWatcher(
806 self.test_list.options.sync_event_log_period_secs,
807 handle_event_logs_callback=self._handle_event_logs)
808 if self.test_list.options.sync_event_log_period_secs:
809 self.log_watcher.StartWatchThread()
Jon Salz258a40c2012-04-19 12:34:01 +0800810
Jon Salz5f2a0672012-05-22 17:14:06 +0800811 self.update_system_info()
812
Jon Salz5da61e62012-05-31 13:06:22 +0800813 os.environ['CROS_FACTORY'] = '1'
Jon Salzeebd12e2012-06-08 15:34:56 +0800814 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
Jon Salz5da61e62012-05-31 13:06:22 +0800815
Jon Salzb1b39092012-05-03 02:05:09 +0800816 # Set CROS_UI since some behaviors in ui.py depend on the
817 # particular UI in use. TODO(jsalz): Remove this (and all
818 # places it is used) when the GTK UI is removed.
819 os.environ['CROS_UI'] = self.options.ui
Jon Salz258a40c2012-04-19 12:34:01 +0800820
Jon Salzb1b39092012-05-03 02:05:09 +0800821 if self.options.ui == 'chrome':
Jon Salz258a40c2012-04-19 12:34:01 +0800822 self.env.launch_chrome()
823 logging.info('Waiting for a web socket connection')
824 self.web_socket_manager.wait()
Jon Salzb1b39092012-05-03 02:05:09 +0800825
826 # Wait for the test widget size to be set; this is done in
827 # an asynchronous RPC so there is a small chance that the
828 # web socket might be opened first.
829 for i in range(100): # 10 s
Jon Salz63585ea2012-05-21 15:03:32 +0800830 try:
831 if self.state_instance.get_shared_data('test_widget_size'):
832 break
833 except KeyError:
834 pass # Retry
Jon Salzb1b39092012-05-03 02:05:09 +0800835 time.sleep(0.1) # 100 ms
836 else:
837 logging.warn('Never received test_widget_size from UI')
Jon Salz258a40c2012-04-19 12:34:01 +0800838 elif self.options.ui == 'gtk':
Jon Salz73e0fd02012-04-04 11:46:38 +0800839 self.start_ui()
Jon Salz258a40c2012-04-19 12:34:01 +0800840
Jon Salz94eb56f2012-06-12 18:01:12 +0800841 for handler in self.on_ui_startup:
842 handler()
843
Jon Salz8375c2e2012-04-04 15:22:24 +0800844 self.prespawner = Prespawner()
Jon Salz323dd3d2012-04-09 18:40:43 +0800845 self.prespawner.start()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800846
847 def state_change_callback(test, state):
848 self.event_client.post_event(
849 Event(Event.Type.STATE_CHANGE,
850 path=test.path, state=state))
851 self.test_list.state_change_callback = state_change_callback
852
853 try:
Jon Salz758e6cc2012-04-03 15:47:07 +0800854 tests_after_shutdown = self.state_instance.get_shared_data(
855 'tests_after_shutdown')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800856 except KeyError:
Jon Salz758e6cc2012-04-03 15:47:07 +0800857 tests_after_shutdown = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800858
Jon Salz758e6cc2012-04-03 15:47:07 +0800859 if tests_after_shutdown is not None:
860 logging.info('Resuming tests after shutdown: %s',
861 tests_after_shutdown)
862 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800863 self.tests_to_run.extend(
Jon Salz758e6cc2012-04-03 15:47:07 +0800864 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Jon Salz73e0fd02012-04-04 11:46:38 +0800865 self.run_queue.put(self.run_next_test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800866 else:
Jon Salz57717ca2012-04-04 16:47:25 +0800867 if self.test_list.options.auto_run_on_start:
868 self.run_queue.put(
869 lambda: self.run_tests(self.test_list, untested_only=True))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800870
Jon Salz73e0fd02012-04-04 11:46:38 +0800871 def run(self):
872 '''Runs Goofy.'''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800873 # Process events forever.
Jon Salz57717ca2012-04-04 16:47:25 +0800874 while self.run_once(True):
Jon Salz73e0fd02012-04-04 11:46:38 +0800875 pass
876
Jon Salz57717ca2012-04-04 16:47:25 +0800877 def run_once(self, block=False):
Jon Salz73e0fd02012-04-04 11:46:38 +0800878 '''Runs all items pending in the event loop.
879
Jon Salz57717ca2012-04-04 16:47:25 +0800880 Args:
881 block: If true, block until at least one event is processed.
882
Jon Salz73e0fd02012-04-04 11:46:38 +0800883 Returns:
884 True to keep going or False to shut down.
885 '''
Jon Salz7c15e8b2012-06-19 17:10:37 +0800886 events = utils.DrainQueue(self.run_queue)
887 if not events:
888 # Nothing on the run queue.
889 self._run_queue_idle()
890 if block:
891 # Block for at least one event...
892 events.append(self.run_queue.get())
893 # ...and grab anything else that showed up at the same
894 # time.
895 events.extend(utils.DrainQueue(self.run_queue))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800896
Jon Salz73e0fd02012-04-04 11:46:38 +0800897 for event in events:
898 if not event:
899 # Shutdown request.
900 self.run_queue.task_done()
901 return False
902
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800903 try:
904 event()
905 except Exception as e: # pylint: disable=W0703
906 logging.error('Error in event loop: %s', e)
907 traceback.print_exc(sys.stderr)
Jon Salz8375c2e2012-04-04 15:22:24 +0800908 self.record_exception(traceback.format_exception_only(
909 *sys.exc_info()[:2]))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800910 # But keep going
911 finally:
912 self.run_queue.task_done()
Jon Salz73e0fd02012-04-04 11:46:38 +0800913 return True
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800914
Jon Salz7c15e8b2012-06-19 17:10:37 +0800915 def _run_queue_idle(self):
916 '''Invoked when the run queue has no events.'''
917 self.check_connection_manager()
918
Jon Salz51528e12012-07-02 18:54:45 +0800919 def _handle_event_logs(self, log_name, chunk):
920 '''Callback for event watcher.
921
922 Attempts to upload the event logs to the shopfloor server.
923 '''
924 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
925 start_time = time.time()
926 logging.info('Syncing %s', description)
927 shopfloor_client = shopfloor.get_instance(
928 detect=True,
929 timeout=self.test_list.options.shopfloor_timeout_secs)
930 shopfloor_client.UploadEvent(log_name, chunk)
931 logging.info(
932 'Successfully synced %s in %.03f s',
933 description, time.time() - start_time)
934
Jon Salz258a40c2012-04-19 12:34:01 +0800935 def run_tests_with_status(self, statuses_to_run, starting_at=None,
936 root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800937 '''Runs all top-level tests with a particular status.
938
939 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +0800940
941 Args:
942 starting_at: If provided, only auto-runs tests beginning with
943 this test.
Jon Salz0405ab52012-03-16 15:26:52 +0800944 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800945 root = root or self.test_list
946
Jon Salz57717ca2012-04-04 16:47:25 +0800947 if starting_at:
948 # Make sure they passed a test, not a string.
949 assert isinstance(starting_at, factory.FactoryTest)
950
Jon Salz0405ab52012-03-16 15:26:52 +0800951 tests_to_reset = []
952 tests_to_run = []
953
Jon Salz57717ca2012-04-04 16:47:25 +0800954 found_starting_at = False
955
Jon Salz258a40c2012-04-19 12:34:01 +0800956 for test in root.get_top_level_tests():
Jon Salz57717ca2012-04-04 16:47:25 +0800957 if starting_at:
958 if test == starting_at:
959 # We've found starting_at; do auto-run on all
960 # subsequent tests.
961 found_starting_at = True
962 if not found_starting_at:
963 # Don't start this guy yet
964 continue
965
Jon Salz0405ab52012-03-16 15:26:52 +0800966 status = test.get_state().status
967 if status == TestState.ACTIVE or status in statuses_to_run:
968 # Reset the test (later; we will need to abort
969 # all active tests first).
970 tests_to_reset.append(test)
971 if status in statuses_to_run:
972 tests_to_run.append(test)
973
974 self.abort_active_tests()
975
976 # Reset all statuses of the tests to run (in case any tests were active;
977 # we want them to be run again).
978 for test_to_reset in tests_to_reset:
979 for test in test_to_reset.walk():
980 test.update_state(status=TestState.UNTESTED)
981
982 self.run_tests(tests_to_run, untested_only=True)
983
Jon Salz258a40c2012-04-19 12:34:01 +0800984 def restart_tests(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800985 '''Restarts all tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800986 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +0800987
Jon Salz258a40c2012-04-19 12:34:01 +0800988 self.abort_active_tests()
989 for test in root.walk():
990 test.update_state(status=TestState.UNTESTED)
991 self.run_tests(root)
992
993 def auto_run(self, starting_at=None, root=None):
Jon Salz57717ca2012-04-04 16:47:25 +0800994 '''"Auto-runs" tests that have not been run yet.
995
996 Args:
997 starting_at: If provide, only auto-runs tests beginning with
998 this test.
999 '''
Jon Salz258a40c2012-04-19 12:34:01 +08001000 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001001 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Jon Salz258a40c2012-04-19 12:34:01 +08001002 starting_at=starting_at,
1003 root=root)
Jon Salz0405ab52012-03-16 15:26:52 +08001004
Jon Salz258a40c2012-04-19 12:34:01 +08001005 def re_run_failed(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +08001006 '''Re-runs failed tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +08001007 root = root or self.test_list
1008 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz0405ab52012-03-16 15:26:52 +08001009
Jon Salz968e90b2012-03-18 16:12:43 +08001010 def show_review_information(self):
Hung-Te Lin96632362012-03-20 21:14:18 +08001011 '''Event handler for showing review information screen.
1012
1013 The information screene is rendered by main UI program (ui.py), so in
1014 goofy we only need to kill all active tests, set them as untested, and
1015 clear remaining tests.
1016 '''
1017 self.kill_active_tests(False)
1018 self.run_tests([])
1019
Jon Salz0405ab52012-03-16 15:26:52 +08001020 def handle_switch_test(self, event):
Jon Salz968e90b2012-03-18 16:12:43 +08001021 '''Switches to a particular test.
1022
1023 @param event: The SWITCH_TEST event.
1024 '''
Jon Salz0405ab52012-03-16 15:26:52 +08001025 test = self.test_list.lookup_path(event.path)
Jon Salz57717ca2012-04-04 16:47:25 +08001026 if not test:
Jon Salz968e90b2012-03-18 16:12:43 +08001027 logging.error('Unknown test %r', event.key)
Jon Salz57717ca2012-04-04 16:47:25 +08001028 return
1029
1030 invoc = self.invocations.get(test)
1031 if invoc and test.backgroundable:
1032 # Already running: just bring to the front if it
1033 # has a UI.
1034 logging.info('Setting visible test to %s', test.path)
1035 self.event_client.post_event(
1036 Event(Event.Type.SET_VISIBLE_TEST, path=test.path))
1037 return
1038
1039 self.abort_active_tests()
1040 for t in test.walk():
1041 t.update_state(status=TestState.UNTESTED)
1042
1043 if self.test_list.options.auto_run_on_keypress:
1044 self.auto_run(starting_at=test)
1045 else:
1046 self.run_tests(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001047
Jon Salz73e0fd02012-04-04 11:46:38 +08001048 def wait(self):
1049 '''Waits for all pending invocations.
1050
1051 Useful for testing.
1052 '''
1053 for k, v in self.invocations.iteritems():
1054 logging.info('Waiting for %s to complete...', k)
1055 v.thread.join()
1056
1057 def check_exceptions(self):
1058 '''Raises an error if any exceptions have occurred in
1059 invocation threads.'''
1060 if self.exceptions:
1061 raise RuntimeError('Exception in invocation thread: %r' %
1062 self.exceptions)
1063
1064 def record_exception(self, msg):
1065 '''Records an exception in an invocation thread.
1066
1067 An exception with the given message will be rethrown when
1068 Goofy is destroyed.'''
1069 self.exceptions.append(msg)
1070
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001071
1072if __name__ == '__main__':
1073 Goofy().main()