blob: 502bfacba9d78ab8533b2e9c96857909f0bc300d [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
Hung-Te Linf2f78f72012-02-08 19:27:11 +08002# -*- coding: utf-8 -*-
3#
Jon Salz37eccbd2012-05-25 16:06:52 +08004# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08005# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8'''
9The main factory flow that runs the factory test and finalizes a device.
10'''
11
Jon Salz0405ab52012-03-16 15:26:52 +080012import logging
13import os
Jon Salz73e0fd02012-04-04 11:46:38 +080014import Queue
Jon Salz77c151e2012-08-28 07:20:37 +080015import signal
Jon Salz0405ab52012-03-16 15:26:52 +080016import sys
Jon Salz0405ab52012-03-16 15:26:52 +080017import threading
18import time
19import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080020import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080021from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080022from collections import deque
23from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080024
Jon Salz0697cbf2012-07-04 15:14:04 +080025import factory_common # pylint: disable=W0611
jcliangcd688182012-08-20 21:01:26 +080026from cros.factory import event_log
27from cros.factory import system
28from cros.factory.event_log import EventLog
29from cros.factory.goofy import test_environment
30from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080031from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080032from cros.factory.goofy.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080033from cros.factory.goofy.goofy_rpc import GoofyRPC
34from cros.factory.goofy.invocation import TestInvocation
35from cros.factory.goofy.prespawner import Prespawner
36from cros.factory.goofy.web_socket_manager import WebSocketManager
37from cros.factory.system.charge_manager import ChargeManager
Jon Salzb92c5112012-09-21 15:40:11 +080038from cros.factory.system import disk_space
jcliangcd688182012-08-20 21:01:26 +080039from cros.factory.test import factory
40from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080041from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080042from cros.factory.test import utils
43from cros.factory.test.event import Event
44from cros.factory.test.event import EventClient
45from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080046from cros.factory.test.factory import TestState
Dean Liao592e4d52013-01-10 20:06:39 +080047from cros.factory.tools.key_filter import KeyFilter
Jon Salz78c32392012-07-25 14:18:29 +080048from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080049
50
Jon Salz2f757d42012-06-27 17:06:42 +080051DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
52CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080053HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Chun-ta Lin279e7e92013-02-19 17:40:39 +080054CACHES_DIR = os.path.join(factory.get_state_root(), "caches")
Hung-Te Linf2f78f72012-02-08 19:27:11 +080055
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 Salz5c344f62012-07-13 14:31:16 +080059# Value for tests_after_shutdown that forces auto-run (e.g., after
60# a factory update, when the available set of tests might change).
61FORCE_AUTO_RUN = 'force_auto_run'
62
cychiang21886742012-07-05 15:16:32 +080063RUN_QUEUE_TIMEOUT_SECS = 10
64
Jon Salz758e6cc2012-04-03 15:47:07 +080065GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
66You are running Goofy inside the chroot. Autotests are not supported.
67
68To use Goofy in the chroot, first install an Xvnc server:
69
Jon Salz0697cbf2012-07-04 15:14:04 +080070 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080071
72...and then start a VNC X server outside the chroot:
73
Jon Salz0697cbf2012-07-04 15:14:04 +080074 vncserver :10 &
75 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080076
77...and run Goofy as follows:
78
Jon Salz0697cbf2012-07-04 15:14:04 +080079 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080080''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080081suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080082
83def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080084 '''
85 Returns the HWID config tag, or an empty string if none can be found.
86 '''
87 if 'CROS_HWID' in os.environ:
88 return os.environ['CROS_HWID']
89 if os.path.exists(HWID_CFG_PATH):
90 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
91 return hwid_cfg_handle.read().strip()
92 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080093
94
95def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080096 '''
97 Returns the path to the active test list, based on the HWID config tag.
98 '''
99 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800100
Jon Salz4be56b02012-12-22 07:30:46 +0800101 search_dirs = [DEFAULT_TEST_LISTS_DIR]
102 if not utils.in_chroot():
103 # Also look in suite_Factory. For backward compatibility only;
104 # new boards should just put the test list in the "test_lists"
105 # directory.
106 search_dirs.insert(0, os.path.join(
107 os.path.dirname(factory.FACTORY_PATH),
108 'autotest', 'site_tests', 'suite_Factory'))
Jon Salz2f757d42012-06-27 17:06:42 +0800109
Jon Salz0697cbf2012-07-04 15:14:04 +0800110 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
111 search_files = ['test_list', 'test_list.all']
112 if hwid_cfg:
113 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800114
Jon Salz0697cbf2012-07-04 15:14:04 +0800115 for d in search_dirs:
116 for f in search_files:
117 test_list = os.path.join(d, f)
118 if os.path.exists(test_list):
119 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800120
Jon Salz0697cbf2012-07-04 15:14:04 +0800121 logging.warn('Cannot find test lists named any of %s in any of %s',
122 search_files, search_dirs)
123 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800124
Jon Salz73e0fd02012-04-04 11:46:38 +0800125_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800126
127class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800128 '''
129 The main factory flow.
130
131 Note that all methods in this class must be invoked from the main
132 (event) thread. Other threads, such as callbacks and TestInvocation
133 methods, should instead post events on the run queue.
134
135 TODO: Unit tests. (chrome-os-partner:7409)
136
137 Properties:
138 uuid: A unique UUID for this invocation of Goofy.
139 state_instance: An instance of FactoryState.
140 state_server: The FactoryState XML/RPC server.
141 state_server_thread: A thread running state_server.
142 event_server: The EventServer socket server.
143 event_server_thread: A thread running event_server.
144 event_client: A client to the event server.
145 connection_manager: The connection_manager object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800146 ui_process: The factory ui process object.
147 run_queue: A queue of callbacks to invoke from the main thread.
148 invocations: A map from FactoryTest objects to the corresponding
149 TestInvocations objects representing active tests.
150 tests_to_run: A deque of tests that should be run when the current
151 test(s) complete.
152 options: Command-line options.
153 args: Command-line args.
154 test_list: The test list.
155 event_handlers: Map of Event.Type to the method used to handle that
156 event. If the method has an 'event' argument, the event is passed
157 to the handler.
158 exceptions: Exceptions encountered in invocation threads.
159 '''
160 def __init__(self):
161 self.uuid = str(uuid.uuid4())
162 self.state_instance = None
163 self.state_server = None
164 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800165 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800166 self.event_server = None
167 self.event_server_thread = None
168 self.event_client = None
169 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800170 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800171 self.time_sanitizer = None
172 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800173 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800174 self.event_log = None
175 self.prespawner = None
176 self.ui_process = None
Jon Salzc79a9982012-08-30 04:42:01 +0800177 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800178 self.run_queue = Queue.Queue()
179 self.invocations = {}
180 self.tests_to_run = deque()
181 self.visible_test = None
182 self.chrome = None
183
184 self.options = None
185 self.args = None
186 self.test_list = None
187 self.on_ui_startup = []
188 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800189 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800190 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800191 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800192 self.last_sync_time = None
Jon Salzb92c5112012-09-21 15:40:11 +0800193 self.last_log_disk_space_time = None
Vic Yang311ddb82012-09-26 12:08:28 +0800194 self.exclusive_items = set()
Jon Salz0f996602012-10-03 15:26:48 +0800195 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800196 self.key_filter = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800197
Jon Salz85a39882012-07-05 16:45:04 +0800198 def test_or_root(event, parent_or_group=True):
199 '''Returns the test affected by a particular event.
200
201 Args:
202 event: The event containing an optional 'path' attribute.
203 parent_on_group: If True, returns the top-level parent for a test (the
204 root node of the tests that need to be run together if the given test
205 path is to be run).
206 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800207 try:
208 path = event.path
209 except AttributeError:
210 path = None
211
212 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800213 test = self.test_list.lookup_path(path)
214 if parent_or_group:
215 test = test.get_top_level_parent_or_group()
216 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800217 else:
218 return self.test_list
219
220 self.event_handlers = {
221 Event.Type.SWITCH_TEST: self.handle_switch_test,
222 Event.Type.SHOW_NEXT_ACTIVE_TEST:
223 lambda event: self.show_next_active_test(),
224 Event.Type.RESTART_TESTS:
225 lambda event: self.restart_tests(root=test_or_root(event)),
226 Event.Type.AUTO_RUN:
227 lambda event: self.auto_run(root=test_or_root(event)),
228 Event.Type.RE_RUN_FAILED:
229 lambda event: self.re_run_failed(root=test_or_root(event)),
230 Event.Type.RUN_TESTS_WITH_STATUS:
231 lambda event: self.run_tests_with_status(
232 event.status,
233 root=test_or_root(event)),
234 Event.Type.REVIEW:
235 lambda event: self.show_review_information(),
236 Event.Type.UPDATE_SYSTEM_INFO:
237 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800238 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800239 lambda event: self.stop(root=test_or_root(event, False),
240 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800241 Event.Type.SET_VISIBLE_TEST:
242 lambda event: self.set_visible_test(
243 self.test_list.lookup_path(event.path)),
Jon Salz4712ac72013-02-07 17:12:05 +0800244 Event.Type.CLEAR_STATE:
245 lambda event: self.clear_state(self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800246 }
247
248 self.exceptions = []
249 self.web_socket_manager = None
250
251 def destroy(self):
252 if self.chrome:
253 self.chrome.kill()
254 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800255 if self.dummy_shopfloor:
256 self.dummy_shopfloor.kill()
257 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800258 if self.ui_process:
259 utils.kill_process_tree(self.ui_process, 'ui')
260 self.ui_process = None
261 if self.web_socket_manager:
262 logging.info('Stopping web sockets')
263 self.web_socket_manager.close()
264 self.web_socket_manager = None
265 if self.state_server_thread:
266 logging.info('Stopping state server')
267 self.state_server.shutdown()
268 self.state_server_thread.join()
269 self.state_server.server_close()
270 self.state_server_thread = None
271 if self.state_instance:
272 self.state_instance.close()
273 if self.event_server_thread:
274 logging.info('Stopping event server')
275 self.event_server.shutdown() # pylint: disable=E1101
276 self.event_server_thread.join()
277 self.event_server.server_close()
278 self.event_server_thread = None
279 if self.log_watcher:
280 if self.log_watcher.IsThreadStarted():
281 self.log_watcher.StopWatchThread()
282 self.log_watcher = None
283 if self.prespawner:
284 logging.info('Stopping prespawner')
285 self.prespawner.stop()
286 self.prespawner = None
287 if self.event_client:
288 logging.info('Closing event client')
289 self.event_client.close()
290 self.event_client = None
291 if self.event_log:
292 self.event_log.Close()
293 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800294 if self.key_filter:
295 self.key_filter.Stop()
296
Jon Salz0697cbf2012-07-04 15:14:04 +0800297 self.check_exceptions()
298 logging.info('Done destroying Goofy')
299
300 def start_state_server(self):
301 self.state_instance, self.state_server = (
302 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800303 self.goofy_rpc = GoofyRPC(self)
304 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800305 logging.info('Starting state server')
306 self.state_server_thread = threading.Thread(
307 target=self.state_server.serve_forever,
308 name='StateServer')
309 self.state_server_thread.start()
310
311 def start_event_server(self):
312 self.event_server = EventServer()
313 logging.info('Starting factory event server')
314 self.event_server_thread = threading.Thread(
315 target=self.event_server.serve_forever,
316 name='EventServer') # pylint: disable=E1101
317 self.event_server_thread.start()
318
319 self.event_client = EventClient(
320 callback=self.handle_event, event_loop=self.run_queue)
321
322 self.web_socket_manager = WebSocketManager(self.uuid)
323 self.state_server.add_handler("/event",
324 self.web_socket_manager.handle_web_socket)
325
326 def start_ui(self):
327 ui_proc_args = [
328 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
329 self.options.test_list]
330 if self.options.verbose:
331 ui_proc_args.append('-v')
332 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800333 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800334 logging.info('Waiting for UI to come up...')
335 self.event_client.wait(
336 lambda event: event.type == Event.Type.UI_READY)
337 logging.info('UI has started')
338
339 def set_visible_test(self, test):
340 if self.visible_test == test:
341 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800342 if test and not test.has_ui:
343 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800344
345 if test:
346 test.update_state(visible=True)
347 if self.visible_test:
348 self.visible_test.update_state(visible=False)
349 self.visible_test = test
350
Jon Salzd4306c82012-11-30 15:16:36 +0800351 def _log_startup_messages(self):
352 '''Logs the tail of var/log/messages and mosys and EC console logs.'''
353 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
354 # for factory-3004.B only. Consolidate and merge back to ToT.
355 if utils.in_chroot():
356 return
357
358 try:
359 var_log_messages = (
360 utils.var_log_messages_before_reboot())
361 logging.info(
362 'Tail of /var/log/messages before last reboot:\n'
363 '%s', ('\n'.join(
364 ' ' + x for x in var_log_messages)))
365 except: # pylint: disable=W0702
366 logging.exception('Unable to grok /var/log/messages')
367
368 try:
369 mosys_log = utils.Spawn(
370 ['mosys', 'eventlog', 'list'],
371 read_stdout=True, log_stderr_on_error=True).stdout_data
372 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
373 except: # pylint: disable=W0702
374 logging.exception('Unable to read mosys eventlog')
375
376 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800377 board = system.GetBoard()
378 ec_console_log = board.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800379 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
380 except: # pylint: disable=W0702
381 logging.exception('Error retrieving EC console log')
382
Jon Salz0697cbf2012-07-04 15:14:04 +0800383 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800384 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800385 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800386
Jon Salz0697cbf2012-07-04 15:14:04 +0800387 @param test: The ShutdownStep.
388 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800389 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800390 test_state = test.update_state(increment_shutdown_count=1)
391 logging.info('Detected shutdown (%d of %d)',
392 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800393
Jon Salz0697cbf2012-07-04 15:14:04 +0800394 def log_and_update_state(status, error_msg, **kw):
395 self.event_log.Log('rebooted',
396 status=status, error_msg=error_msg, **kw)
Jon Salzd4306c82012-11-30 15:16:36 +0800397 logging.info('Rebooted: status=%s, %s', status,
398 (('error_msg=%s' % error_msg) if error_msg else None))
Jon Salz0697cbf2012-07-04 15:14:04 +0800399 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800400
Jon Salz0697cbf2012-07-04 15:14:04 +0800401 if not self.last_shutdown_time:
402 log_and_update_state(status=TestState.FAILED,
403 error_msg='Unable to read shutdown_time')
404 return
Jon Salz258a40c2012-04-19 12:34:01 +0800405
Jon Salz0697cbf2012-07-04 15:14:04 +0800406 now = time.time()
407 logging.info('%.03f s passed since reboot',
408 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800409
Jon Salz0697cbf2012-07-04 15:14:04 +0800410 if self.last_shutdown_time > now:
411 test.update_state(status=TestState.FAILED,
412 error_msg='Time moved backward during reboot')
413 elif (isinstance(test, factory.RebootStep) and
414 self.test_list.options.max_reboot_time_secs and
415 (now - self.last_shutdown_time >
416 self.test_list.options.max_reboot_time_secs)):
417 # A reboot took too long; fail. (We don't check this for
418 # HaltSteps, because the machine could be halted for a
419 # very long time, and even unplugged with battery backup,
420 # thus hosing the clock.)
421 log_and_update_state(
422 status=TestState.FAILED,
423 error_msg=('More than %d s elapsed during reboot '
424 '(%.03f s, from %s to %s)' % (
425 self.test_list.options.max_reboot_time_secs,
426 now - self.last_shutdown_time,
427 utils.TimeString(self.last_shutdown_time),
428 utils.TimeString(now))),
429 duration=(now-self.last_shutdown_time))
Jon Salzd4306c82012-11-30 15:16:36 +0800430 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800431 elif test_state.shutdown_count == test.iterations:
432 # Good!
433 log_and_update_state(status=TestState.PASSED,
434 duration=(now - self.last_shutdown_time),
435 error_msg='')
436 elif test_state.shutdown_count > test.iterations:
437 # Shut down too many times
438 log_and_update_state(status=TestState.FAILED,
439 error_msg='Too many shutdowns')
Jon Salzd4306c82012-11-30 15:16:36 +0800440 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800441 elif utils.are_shift_keys_depressed():
442 logging.info('Shift keys are depressed; cancelling restarts')
443 # Abort shutdown
444 log_and_update_state(
445 status=TestState.FAILED,
446 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800447 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800448 else:
449 def handler():
450 if self._prompt_cancel_shutdown(
451 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800452 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800453 log_and_update_state(
454 status=TestState.FAILED,
455 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800456 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800457 return
Jon Salz0405ab52012-03-16 15:26:52 +0800458
Jon Salz0697cbf2012-07-04 15:14:04 +0800459 # Time to shutdown again
460 log_and_update_state(
461 status=TestState.ACTIVE,
462 error_msg='',
463 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800464
Jon Salz0697cbf2012-07-04 15:14:04 +0800465 self.event_log.Log('shutdown', operation='reboot')
466 self.state_instance.set_shared_data('shutdown_time',
467 time.time())
468 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800469
Jon Salz0697cbf2012-07-04 15:14:04 +0800470 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800471
Jon Salz0697cbf2012-07-04 15:14:04 +0800472 def _prompt_cancel_shutdown(self, test, iteration):
473 if self.options.ui != 'chrome':
474 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800475
Jon Salz0697cbf2012-07-04 15:14:04 +0800476 pending_shutdown_data = {
477 'delay_secs': test.delay_secs,
478 'time': time.time() + test.delay_secs,
479 'operation': test.operation,
480 'iteration': iteration,
481 'iterations': test.iterations,
482 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800483
Jon Salz0697cbf2012-07-04 15:14:04 +0800484 # Create a new (threaded) event client since we
485 # don't want to use the event loop for this.
486 with EventClient() as event_client:
487 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
488 **pending_shutdown_data))
489 aborted = event_client.wait(
490 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
491 timeout=test.delay_secs) is not None
492 if aborted:
493 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
494 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800495
Jon Salz0697cbf2012-07-04 15:14:04 +0800496 def init_states(self):
497 '''
498 Initializes all states on startup.
499 '''
500 for test in self.test_list.get_all_tests():
501 # Make sure the state server knows about all the tests,
502 # defaulting to an untested state.
503 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800504
Jon Salz0697cbf2012-07-04 15:14:04 +0800505 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800506 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800507 ec_console_log = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800508
Jon Salz0697cbf2012-07-04 15:14:04 +0800509 # Any 'active' tests should be marked as failed now.
510 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800511 if not test.is_leaf():
512 # Don't bother with parents; they will be updated when their
513 # children are updated.
514 continue
515
Jon Salz0697cbf2012-07-04 15:14:04 +0800516 test_state = test.get_state()
517 if test_state.status != TestState.ACTIVE:
518 continue
519 if isinstance(test, factory.ShutdownStep):
520 # Shutdown while the test was active - that's good.
521 self.handle_shutdown_complete(test, test_state)
522 else:
523 # Unexpected shutdown. Grab /var/log/messages for context.
524 if var_log_messages is None:
525 try:
526 var_log_messages = (
527 utils.var_log_messages_before_reboot())
528 # Write it to the log, to make it easier to
529 # correlate with /var/log/messages.
530 logging.info(
531 'Unexpected shutdown. '
532 'Tail of /var/log/messages before last reboot:\n'
533 '%s', ('\n'.join(
534 ' ' + x for x in var_log_messages)))
535 except: # pylint: disable=W0702
536 logging.exception('Unable to grok /var/log/messages')
537 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800538
Jon Salz008f4ea2012-08-28 05:39:45 +0800539 if mosys_log is None and not utils.in_chroot():
540 try:
541 mosys_log = utils.Spawn(
542 ['mosys', 'eventlog', 'list'],
543 read_stdout=True, log_stderr_on_error=True).stdout_data
544 # Write it to the log also.
545 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
546 except: # pylint: disable=W0702
547 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800548
Vic Yange4c275d2012-08-28 01:50:20 +0800549 if ec_console_log is None:
550 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800551 board = system.GetBoard()
552 ec_console_log = board.GetECConsoleLog()
Vic Yange4c275d2012-08-28 01:50:20 +0800553 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Jon Salzfe1f6652012-09-07 05:40:14 +0800554 except: # pylint: disable=W0702
Vic Yange4c275d2012-08-28 01:50:20 +0800555 logging.exception('Error retrieving EC console log')
556
Jon Salz0697cbf2012-07-04 15:14:04 +0800557 error_msg = 'Unexpected shutdown while test was running'
558 self.event_log.Log('end_test',
559 path=test.path,
560 status=TestState.FAILED,
561 invocation=test.get_state().invocation,
562 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800563 var_log_messages='\n'.join(var_log_messages),
564 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800565 test.update_state(
566 status=TestState.FAILED,
567 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800568
Jon Salz50efe942012-07-26 11:54:10 +0800569 if not test.never_fails:
570 # For "never_fails" tests (such as "Start"), don't cancel
571 # pending tests, since reboot is expected.
572 factory.console.info('Unexpected shutdown while test %s '
573 'running; cancelling any pending tests',
574 test.path)
575 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800576
Jon Salz008f4ea2012-08-28 05:39:45 +0800577 self.update_skipped_tests()
578
579 def update_skipped_tests(self):
580 '''
581 Updates skipped states based on run_if.
582 '''
583 for t in self.test_list.walk():
584 if t.is_leaf() and t.run_if_table_name:
585 skip = False
586 try:
587 aux = shopfloor.get_selected_aux_data(t.run_if_table_name)
588 value = aux.get(t.run_if_col)
589 if value is not None:
590 skip = (not value) ^ t.run_if_not
591 except ValueError:
592 # Not available; assume it shouldn't be skipped
593 pass
594
595 test_state = t.get_state()
596 if ((not skip) and
597 (test_state.status == TestState.PASSED) and
598 (test_state.error_msg == TestState.SKIPPED_MSG)):
599 # It was marked as skipped before, but now we need to run it.
600 # Mark as untested.
601 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
602 else:
603 t.update_state(skip=skip)
604
Jon Salz0697cbf2012-07-04 15:14:04 +0800605 def show_next_active_test(self):
606 '''
607 Rotates to the next visible active test.
608 '''
609 self.reap_completed_tests()
610 active_tests = [
611 t for t in self.test_list.walk()
612 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
613 if not active_tests:
614 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800615
Jon Salz0697cbf2012-07-04 15:14:04 +0800616 try:
617 next_test = active_tests[
618 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
619 except ValueError: # visible_test not present in active_tests
620 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800621
Jon Salz0697cbf2012-07-04 15:14:04 +0800622 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800623
Jon Salz0697cbf2012-07-04 15:14:04 +0800624 def handle_event(self, event):
625 '''
626 Handles an event from the event server.
627 '''
628 handler = self.event_handlers.get(event.type)
629 if handler:
630 handler(event)
631 else:
632 # We don't register handlers for all event types - just ignore
633 # this event.
634 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800635
Jon Salz0697cbf2012-07-04 15:14:04 +0800636 def run_next_test(self):
637 '''
638 Runs the next eligible test (or tests) in self.tests_to_run.
639 '''
640 self.reap_completed_tests()
641 while self.tests_to_run:
642 logging.debug('Tests to run: %s',
643 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800644
Jon Salz0697cbf2012-07-04 15:14:04 +0800645 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800646
Jon Salz0697cbf2012-07-04 15:14:04 +0800647 if test in self.invocations:
648 logging.info('Next test %s is already running', test.path)
649 self.tests_to_run.popleft()
650 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800651
Jon Salza1412922012-07-23 16:04:17 +0800652 for requirement in test.require_run:
653 for i in requirement.test.walk():
654 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800655 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800656 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800657 return
658
Jon Salz0697cbf2012-07-04 15:14:04 +0800659 if self.invocations and not (test.backgroundable and all(
660 [x.backgroundable for x in self.invocations])):
661 logging.debug('Waiting for non-backgroundable tests to '
662 'complete before running %s', test.path)
663 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800664
Jon Salz3e6f5202012-10-15 15:08:29 +0800665 if test.get_state().skip:
666 factory.console.info('Skipping test %s', test.path)
667 test.update_state(status=TestState.PASSED,
668 error_msg=TestState.SKIPPED_MSG)
669 self.tests_to_run.popleft()
670 continue
671
Jon Salz0697cbf2012-07-04 15:14:04 +0800672 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800673
Jon Salz304a75d2012-07-06 11:14:15 +0800674 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800675 for requirement in test.require_run:
676 for i in requirement.test.walk():
677 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800678 # We've hit this test itself; stop checking
679 break
Jon Salza1412922012-07-23 16:04:17 +0800680 if ((i.get_state().status == TestState.UNTESTED) or
681 (requirement.passed and i.get_state().status !=
682 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800683 # Found an untested test; move on to the next
684 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800685 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800686 break
687
688 if untested:
689 untested_paths = ', '.join(sorted([x.path for x in untested]))
690 if self.state_instance.get_shared_data('engineering_mode',
691 optional=True):
692 # In engineering mode, we'll let it go.
693 factory.console.warn('In engineering mode; running '
694 '%s even though required tests '
695 '[%s] have not completed',
696 test.path, untested_paths)
697 else:
698 # Not in engineering mode; mark it failed.
699 error_msg = ('Required tests [%s] have not been run yet'
700 % untested_paths)
701 factory.console.error('Not running %s: %s',
702 test.path, error_msg)
703 test.update_state(status=TestState.FAILED,
704 error_msg=error_msg)
705 continue
706
Jon Salz0697cbf2012-07-04 15:14:04 +0800707 if isinstance(test, factory.ShutdownStep):
708 if os.path.exists(NO_REBOOT_FILE):
709 test.update_state(
710 status=TestState.FAILED, increment_count=1,
711 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800712 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800713 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800714
Jon Salz0697cbf2012-07-04 15:14:04 +0800715 test.update_state(status=TestState.ACTIVE, increment_count=1,
716 error_msg='', shutdown_count=0)
717 if self._prompt_cancel_shutdown(test, 1):
718 self.event_log.Log('reboot_cancelled')
719 test.update_state(
720 status=TestState.FAILED, increment_count=1,
721 error_msg='Shutdown aborted by operator',
722 shutdown_count=0)
chungyiafe8f772012-08-15 19:36:29 +0800723 continue
Jon Salz2f757d42012-06-27 17:06:42 +0800724
Jon Salz0697cbf2012-07-04 15:14:04 +0800725 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800726 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800727 'tests_after_shutdown',
728 [t.path for t in self.tests_to_run])
729 # Save shutdown time
730 self.state_instance.set_shared_data('shutdown_time',
731 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800732
Jon Salz0697cbf2012-07-04 15:14:04 +0800733 with self.env.lock:
734 self.event_log.Log('shutdown', operation=test.operation)
735 shutdown_result = self.env.shutdown(test.operation)
736 if shutdown_result:
737 # That's all, folks!
738 self.run_queue.put(None)
739 return
740 else:
741 # Just pass (e.g., in the chroot).
742 test.update_state(status=TestState.PASSED)
743 self.state_instance.set_shared_data(
744 'tests_after_shutdown', None)
745 # Send event with no fields to indicate that there is no
746 # longer a pending shutdown.
747 self.event_client.post_event(Event(
748 Event.Type.PENDING_SHUTDOWN))
749 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800750
Jon Salz1acc8742012-07-17 17:45:55 +0800751 self._run_test(test, test.iterations)
752
753 def _run_test(self, test, iterations_left=None):
754 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
755 new_state = test.update_state(
756 status=TestState.ACTIVE, increment_count=1, error_msg='',
Jon Salzbd42ce12012-09-18 08:03:59 +0800757 invocation=invoc.uuid, iterations_left=iterations_left,
758 visible=(self.visible_test == test))
Jon Salz1acc8742012-07-17 17:45:55 +0800759 invoc.count = new_state.count
760
761 self.invocations[test] = invoc
762 if self.visible_test is None and test.has_ui:
763 self.set_visible_test(test)
Vic Yang311ddb82012-09-26 12:08:28 +0800764 self.check_exclusive()
Jon Salz1acc8742012-07-17 17:45:55 +0800765 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800766
Vic Yang311ddb82012-09-26 12:08:28 +0800767 def check_exclusive(self):
768 current_exclusive_items = set([
769 item
770 for item in factory.FactoryTest.EXCLUSIVE_OPTIONS
771 if any([test.is_exclusive(item) for test in self.invocations])])
772
773 new_exclusive_items = current_exclusive_items - self.exclusive_items
774 if factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING in new_exclusive_items:
775 logging.info('Disabling network')
776 self.connection_manager.DisableNetworking()
777 if factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in new_exclusive_items:
778 logging.info('Stop controlling charger')
779
780 new_non_exclusive_items = self.exclusive_items - current_exclusive_items
781 if (factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING in
782 new_non_exclusive_items):
783 logging.info('Re-enabling network')
784 self.connection_manager.EnableNetworking()
785 if factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in new_non_exclusive_items:
786 logging.info('Start controlling charger')
787
788 # Only adjust charge state if not excluded
789 if (self.charge_manager and
790 not factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in
791 current_exclusive_items):
792 self.charge_manager.AdjustChargeState()
793
794 self.exclusive_items = current_exclusive_items
Jon Salz5da61e62012-05-31 13:06:22 +0800795
cychiang21886742012-07-05 15:16:32 +0800796 def check_for_updates(self):
797 '''
798 Schedules an asynchronous check for updates if necessary.
799 '''
800 if not self.test_list.options.update_period_secs:
801 # Not enabled.
802 return
803
804 now = time.time()
805 if self.last_update_check and (
806 now - self.last_update_check <
807 self.test_list.options.update_period_secs):
808 # Not yet time for another check.
809 return
810
811 self.last_update_check = now
812
813 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
814 if reached_shopfloor:
815 new_update_md5sum = md5sum if needs_update else None
816 if system.SystemInfo.update_md5sum != new_update_md5sum:
817 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
818 system.SystemInfo.update_md5sum = new_update_md5sum
819 self.run_queue.put(self.update_system_info)
820
821 updater.CheckForUpdateAsync(
822 handle_check_for_update,
823 self.test_list.options.shopfloor_timeout_secs)
824
Jon Salza6711d72012-07-18 14:33:03 +0800825 def cancel_pending_tests(self):
826 '''Cancels any tests in the run queue.'''
827 self.run_tests([])
828
Jon Salz0697cbf2012-07-04 15:14:04 +0800829 def run_tests(self, subtrees, untested_only=False):
830 '''
831 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800832
Jon Salz0697cbf2012-07-04 15:14:04 +0800833 The tests are run in order unless one fails (then stops).
834 Backgroundable tests are run simultaneously; when a foreground test is
835 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800836
Jon Salz0697cbf2012-07-04 15:14:04 +0800837 @param subtrees: Node or nodes containing tests to run (may either be
838 a single test or a list). Duplicates will be ignored.
839 '''
840 if type(subtrees) != list:
841 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800842
Jon Salz0697cbf2012-07-04 15:14:04 +0800843 # Nodes we've seen so far, to avoid duplicates.
844 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800845
Jon Salz0697cbf2012-07-04 15:14:04 +0800846 self.tests_to_run = deque()
847 for subtree in subtrees:
848 for test in subtree.walk():
849 if test in seen:
850 continue
851 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800852
Jon Salz0697cbf2012-07-04 15:14:04 +0800853 if not test.is_leaf():
854 continue
855 if (untested_only and
856 test.get_state().status != TestState.UNTESTED):
857 continue
858 self.tests_to_run.append(test)
859 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800860
Jon Salz0697cbf2012-07-04 15:14:04 +0800861 def reap_completed_tests(self):
862 '''
863 Removes completed tests from the set of active tests.
864
865 Also updates the visible test if it was reaped.
866 '''
867 for t, v in dict(self.invocations).iteritems():
868 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800869 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800870 del self.invocations[t]
871
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800872 # Stop on failure if flag is true.
873 if (self.test_list.options.stop_on_failure and
874 new_state.status == TestState.FAILED):
875 # Clean all the tests to cause goofy to stop.
876 self.tests_to_run = []
877 factory.console.info("Stop on failure triggered. Empty the queue.")
878
Jon Salz1acc8742012-07-17 17:45:55 +0800879 if new_state.iterations_left and new_state.status == TestState.PASSED:
880 # Play it again, Sam!
881 self._run_test(t)
882
Jon Salz0697cbf2012-07-04 15:14:04 +0800883 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800884 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800885 self.set_visible_test(None)
886 # Make the first running test, if any, the visible test
887 for t in self.test_list.walk():
888 if t in self.invocations:
889 self.set_visible_test(t)
890 break
891
Jon Salz85a39882012-07-05 16:45:04 +0800892 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800893 '''
894 Kills and waits for all active tests.
895
Jon Salz85a39882012-07-05 16:45:04 +0800896 Args:
897 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800898 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800899 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800900 '''
901 self.reap_completed_tests()
902 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800903 if root and not test.has_ancestor(root):
904 continue
905
Jon Salz0697cbf2012-07-04 15:14:04 +0800906 factory.console.info('Killing active test %s...' % test.path)
907 invoc.abort_and_join()
908 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800909 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800910 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800911
Jon Salz0697cbf2012-07-04 15:14:04 +0800912 if not abort:
913 test.update_state(status=TestState.UNTESTED)
914 self.reap_completed_tests()
915
Jon Salz85a39882012-07-05 16:45:04 +0800916 def stop(self, root=None, fail=False):
917 self.kill_active_tests(fail, root)
918 # Remove any tests in the run queue under the root.
919 self.tests_to_run = deque([x for x in self.tests_to_run
920 if root and not x.has_ancestor(root)])
921 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800922
Jon Salz4712ac72013-02-07 17:12:05 +0800923 def clear_state(self, root=None):
924 self.stop(root)
925 for f in root.walk():
926 if f.is_leaf():
927 f.update_state(status=TestState.UNTESTED)
928
Jon Salz0697cbf2012-07-04 15:14:04 +0800929 def abort_active_tests(self):
930 self.kill_active_tests(True)
931
932 def main(self):
933 try:
934 self.init()
935 self.event_log.Log('goofy_init',
936 success=True)
937 except:
938 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800939 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800940 self.event_log.Log('goofy_init',
941 success=False,
942 trace=traceback.format_exc())
943 except: # pylint: disable=W0702
944 pass
945 raise
946
947 self.run()
948
949 def update_system_info(self):
950 '''Updates system info.'''
951 system_info = system.SystemInfo()
952 self.state_instance.set_shared_data('system_info', system_info.__dict__)
953 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
954 system_info=system_info.__dict__))
955 logging.info('System info: %r', system_info.__dict__)
956
Jon Salzeb42f0d2012-07-27 19:14:04 +0800957 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
958 '''Commences updating factory software.
959
960 Args:
961 auto_run_on_restart: Auto-run when the machine comes back up.
962 post_update_hook: Code to call after update but immediately before
963 restart.
964
965 Returns:
966 Never if the update was successful (we just reboot).
967 False if the update was unnecessary (no update available).
968 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800969 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800970 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800971
Jon Salz5c344f62012-07-13 14:31:16 +0800972 def pre_update_hook():
973 if auto_run_on_restart:
974 self.state_instance.set_shared_data('tests_after_shutdown',
975 FORCE_AUTO_RUN)
976 self.state_instance.close()
977
Jon Salzeb42f0d2012-07-27 19:14:04 +0800978 if updater.TryUpdate(pre_update_hook=pre_update_hook):
979 if post_update_hook:
980 post_update_hook()
981 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800982
Jon Salzcef132a2012-08-30 04:58:08 +0800983 def handle_sigint(self, dummy_signum, dummy_frame):
Jon Salz77c151e2012-08-28 07:20:37 +0800984 logging.error('Received SIGINT')
985 self.run_queue.put(None)
986 raise KeyboardInterrupt()
987
Jon Salz0697cbf2012-07-04 15:14:04 +0800988 def init(self, args=None, env=None):
989 '''Initializes Goofy.
990
991 Args:
992 args: A list of command-line arguments. Uses sys.argv if
993 args is None.
994 env: An Environment instance to use (or None to choose
995 FakeChrootEnvironment or DUTEnvironment as appropriate).
996 '''
Jon Salz77c151e2012-08-28 07:20:37 +0800997 signal.signal(signal.SIGINT, self.handle_sigint)
998
Jon Salz0697cbf2012-07-04 15:14:04 +0800999 parser = OptionParser()
1000 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001001 action='store_true',
1002 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001003 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001004 metavar='FILE',
1005 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001006 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001007 action='store_true',
1008 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001009 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +08001010 choices=['none', 'gtk', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001011 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001012 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001013 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001014 type='int', default=1,
1015 help=('Factor by which to scale UI '
1016 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001017 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001018 metavar='FILE',
1019 help='Use FILE as test list')
Jon Salzc79a9982012-08-30 04:42:01 +08001020 parser.add_option('--dummy_shopfloor', action='store_true',
1021 help='Use a dummy shopfloor server')
chungyiafe8f772012-08-15 19:36:29 +08001022 parser.add_option('--automation', dest='automation',
1023 action='store_true',
1024 help='Enable automation on running factory test')
Ricky Liang09216dc2013-02-22 17:26:45 +08001025 parser.add_option('--one_pixel_less', dest='one_pixel_less',
1026 action='store_true',
1027 help=('Start Chrome one pixel less than the full screen.'
1028 'Needed by Exynos platform to run GTK.'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001029 (self.options, self.args) = parser.parse_args(args)
1030
Jon Salz46b89562012-07-05 11:49:22 +08001031 # Make sure factory directories exist.
1032 factory.get_log_root()
1033 factory.get_state_root()
1034 factory.get_test_data_root()
1035
Jon Salz0697cbf2012-07-04 15:14:04 +08001036 global _inited_logging # pylint: disable=W0603
1037 if not _inited_logging:
1038 factory.init_logging('goofy', verbose=self.options.verbose)
1039 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001040
Jon Salz0f996602012-10-03 15:26:48 +08001041 if self.options.print_test_list:
1042 print factory.read_test_list(
1043 self.options.print_test_list).__repr__(recursive=True)
1044 sys.exit(0)
1045
Jon Salzee85d522012-07-17 14:34:46 +08001046 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +08001047 self.event_log = EventLog('goofy')
1048
1049 if (not suppress_chroot_warning and
1050 factory.in_chroot() and
1051 self.options.ui == 'gtk' and
1052 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
1053 # That's not going to work! Tell the user how to run
1054 # this way.
1055 logging.warn(GOOFY_IN_CHROOT_WARNING)
1056 time.sleep(1)
1057
1058 if env:
1059 self.env = env
1060 elif factory.in_chroot():
1061 self.env = test_environment.FakeChrootEnvironment()
1062 logging.warn(
1063 'Using chroot environment: will not actually run autotests')
1064 else:
1065 self.env = test_environment.DUTEnvironment()
1066 self.env.goofy = self
1067
1068 if self.options.restart:
1069 state.clear_state()
1070
Jon Salz0697cbf2012-07-04 15:14:04 +08001071 if self.options.ui_scale_factor != 1 and utils.in_qemu():
1072 logging.warn(
1073 'In QEMU; ignoring ui_scale_factor argument')
1074 self.options.ui_scale_factor = 1
1075
1076 logging.info('Started')
1077
1078 self.start_state_server()
1079 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1080 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001081 self.options.ui_scale_factor)
1082 self.state_instance.set_shared_data('one_pixel_less',
1083 self.options.one_pixel_less)
Jon Salz0697cbf2012-07-04 15:14:04 +08001084 self.last_shutdown_time = (
1085 self.state_instance.get_shared_data('shutdown_time', optional=True))
1086 self.state_instance.del_shared_data('shutdown_time', optional=True)
1087
Jon Salzb19ea072013-02-07 16:35:00 +08001088 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001089 if not self.options.test_list:
1090 self.options.test_list = find_test_list()
Jon Salzb19ea072013-02-07 16:35:00 +08001091 if self.options.test_list:
Jon Salz0697cbf2012-07-04 15:14:04 +08001092 logging.info('Using test list %s', self.options.test_list)
Jon Salzb19ea072013-02-07 16:35:00 +08001093 try:
1094 self.test_list = factory.read_test_list(
1095 self.options.test_list,
1096 self.state_instance)
1097 except: # pylint: disable=W0702
1098 logging.exception('Unable to read test list %r', self.options.test_list)
1099 self.state_instance.set_shared_data('startup_error',
1100 'Unable to read test list %s\n%s' % (
1101 self.options.test_list,
1102 traceback.format_exc()))
1103 else:
1104 logging.error('No test list found.')
1105 self.state_instance.set_shared_data('startup_error',
1106 'No test list found.')
Jon Salz0697cbf2012-07-04 15:14:04 +08001107
Jon Salzb19ea072013-02-07 16:35:00 +08001108 if not self.test_list:
1109 if self.options.ui == 'chrome':
1110 # Create an empty test list with default options so that the rest of
1111 # startup can proceed.
1112 self.test_list = factory.FactoryTestList(
1113 [], self.state_instance, factory.Options())
1114 else:
1115 # Bail with an error; no point in starting up.
1116 sys.exit('No valid test list; exiting.')
1117
Jon Salz0697cbf2012-07-04 15:14:04 +08001118 if not self.state_instance.has_shared_data('ui_lang'):
1119 self.state_instance.set_shared_data('ui_lang',
1120 self.test_list.options.ui_lang)
1121 self.state_instance.set_shared_data(
1122 'test_list_options',
1123 self.test_list.options.__dict__)
1124 self.state_instance.test_list = self.test_list
1125
Jon Salz83ef34b2012-11-01 19:46:35 +08001126 if not utils.in_chroot() and self.test_list.options.disable_log_rotation:
1127 open('/var/lib/cleanup_logs_paused', 'w').close()
1128
Jon Salz23926422012-09-01 03:38:13 +08001129 if self.options.dummy_shopfloor:
1130 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1131 'http://localhost:%d/' % shopfloor.DEFAULT_SERVER_PORT)
1132 self.dummy_shopfloor = Spawn(
1133 [os.path.join(factory.FACTORY_PATH, 'bin', 'shopfloor_server'),
1134 '--dummy'])
1135 elif self.test_list.options.shopfloor_server_url:
1136 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
1137
Jon Salz0f996602012-10-03 15:26:48 +08001138 if self.test_list.options.time_sanitizer and not utils.in_chroot():
Jon Salz8fa8e832012-07-13 19:04:09 +08001139 self.time_sanitizer = time_sanitizer.TimeSanitizer(
1140 base_time=time_sanitizer.GetBaseTimeFromFile(
1141 # lsb-factory is written by the factory install shim during
1142 # installation, so it should have a good time obtained from
Jon Salz54882d02012-08-31 01:57:54 +08001143 # the mini-Omaha server. If it's not available, we'll use
1144 # /etc/lsb-factory (which will be much older, but reasonably
1145 # sane) and rely on a shopfloor sync to set a more accurate
1146 # time.
1147 '/usr/local/etc/lsb-factory',
1148 '/etc/lsb-release'))
Jon Salz8fa8e832012-07-13 19:04:09 +08001149 self.time_sanitizer.RunOnce()
1150
Jon Salz0697cbf2012-07-04 15:14:04 +08001151 self.init_states()
1152 self.start_event_server()
1153 self.connection_manager = self.env.create_connection_manager(
Tai-Hsu Lin371351a2012-08-27 14:17:14 +08001154 self.test_list.options.wlans,
1155 self.test_list.options.scan_wifi_period_secs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001156 # Note that we create a log watcher even if
1157 # sync_event_log_period_secs isn't set (no background
1158 # syncing), since we may use it to flush event logs as well.
1159 self.log_watcher = EventLogWatcher(
1160 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +08001161 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001162 if self.test_list.options.sync_event_log_period_secs:
1163 self.log_watcher.StartWatchThread()
1164
1165 self.update_system_info()
1166
Vic Yang4953fc12012-07-26 16:19:53 +08001167 assert ((self.test_list.options.min_charge_pct is None) ==
1168 (self.test_list.options.max_charge_pct is None))
Jon Salzad7353b2012-10-15 16:22:46 +08001169 if self.test_list.options.min_charge_pct is not None:
Vic Yang4953fc12012-07-26 16:19:53 +08001170 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1171 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001172 system.SystemStatus.charge_manager = self.charge_manager
Vic Yang4953fc12012-07-26 16:19:53 +08001173
Jon Salz0697cbf2012-07-04 15:14:04 +08001174 os.environ['CROS_FACTORY'] = '1'
1175 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1176
1177 # Set CROS_UI since some behaviors in ui.py depend on the
1178 # particular UI in use. TODO(jsalz): Remove this (and all
1179 # places it is used) when the GTK UI is removed.
1180 os.environ['CROS_UI'] = self.options.ui
1181
1182 if self.options.ui == 'chrome':
1183 self.env.launch_chrome()
1184 logging.info('Waiting for a web socket connection')
1185 self.web_socket_manager.wait()
1186
1187 # Wait for the test widget size to be set; this is done in
1188 # an asynchronous RPC so there is a small chance that the
1189 # web socket might be opened first.
1190 for _ in range(100): # 10 s
1191 try:
1192 if self.state_instance.get_shared_data('test_widget_size'):
1193 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001194 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001195 pass # Retry
1196 time.sleep(0.1) # 100 ms
1197 else:
1198 logging.warn('Never received test_widget_size from UI')
1199 elif self.options.ui == 'gtk':
1200 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001201
Ricky Liang650f6bf2012-09-28 13:22:54 +08001202 # Create download path for autotest beforehand or autotests run at
1203 # the same time might fail due to race condition.
1204 if not factory.in_chroot():
1205 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1206 'download'))
1207
Jon Salz0697cbf2012-07-04 15:14:04 +08001208 def state_change_callback(test, test_state):
1209 self.event_client.post_event(
1210 Event(Event.Type.STATE_CHANGE,
1211 path=test.path, state=test_state))
1212 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001213
Jon Salza6711d72012-07-18 14:33:03 +08001214 for handler in self.on_ui_startup:
1215 handler()
1216
1217 self.prespawner = Prespawner()
1218 self.prespawner.start()
1219
Jon Salz0697cbf2012-07-04 15:14:04 +08001220 try:
1221 tests_after_shutdown = self.state_instance.get_shared_data(
1222 'tests_after_shutdown')
1223 except KeyError:
1224 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001225
Jon Salz5c344f62012-07-13 14:31:16 +08001226 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1227 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001228 logging.info('Resuming tests after shutdown: %s',
1229 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001230 self.tests_to_run.extend(
1231 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1232 self.run_queue.put(self.run_next_test)
1233 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001234 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001235 self.run_queue.put(
1236 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001237 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001238
Dean Liao592e4d52013-01-10 20:06:39 +08001239 self.may_disable_cros_shortcut_keys()
1240
1241 def may_disable_cros_shortcut_keys(self):
1242 test_options = self.test_list.options
1243 if test_options.disable_cros_shortcut_keys:
1244 logging.info('Filter ChromeOS shortcut keys.')
1245 self.key_filter = KeyFilter(
1246 unmap_caps_lock=test_options.disable_caps_lock,
1247 caps_lock_keycode=test_options.caps_lock_keycode)
1248 self.key_filter.Start()
1249
Jon Salz0697cbf2012-07-04 15:14:04 +08001250 def run(self):
1251 '''Runs Goofy.'''
1252 # Process events forever.
1253 while self.run_once(True):
1254 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001255
Jon Salz0697cbf2012-07-04 15:14:04 +08001256 def run_once(self, block=False):
1257 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001258
Jon Salz0697cbf2012-07-04 15:14:04 +08001259 Args:
1260 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001261
Jon Salz0697cbf2012-07-04 15:14:04 +08001262 Returns:
1263 True to keep going or False to shut down.
1264 '''
1265 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001266 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001267 # Nothing on the run queue.
1268 self._run_queue_idle()
1269 if block:
1270 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001271 try:
1272 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1273 except Queue.Empty:
1274 # Keep going (calling _run_queue_idle() again at the top of
1275 # the loop)
1276 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001277 # ...and grab anything else that showed up at the same
1278 # time.
1279 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001280 else:
1281 break
Jon Salz51528e12012-07-02 18:54:45 +08001282
Jon Salz0697cbf2012-07-04 15:14:04 +08001283 for event in events:
1284 if not event:
1285 # Shutdown request.
1286 self.run_queue.task_done()
1287 return False
Jon Salz51528e12012-07-02 18:54:45 +08001288
Jon Salz0697cbf2012-07-04 15:14:04 +08001289 try:
1290 event()
Jon Salz85a39882012-07-05 16:45:04 +08001291 except: # pylint: disable=W0702
1292 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001293 self.record_exception(traceback.format_exception_only(
1294 *sys.exc_info()[:2]))
1295 # But keep going
1296 finally:
1297 self.run_queue.task_done()
1298 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001299
Jon Salz0e6532d2012-10-25 16:30:11 +08001300 def _should_sync_time(self, foreground=False):
1301 '''Returns True if we should attempt syncing time with shopfloor.
1302
1303 Args:
1304 foreground: If True, synchronizes even if background syncing
1305 is disabled (e.g., in explicit sync requests from the
1306 SyncShopfloor test).
1307 '''
1308 return ((foreground or
1309 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001310 self.time_sanitizer and
1311 (not self.time_synced) and
1312 (not factory.in_chroot()))
1313
Jon Salz0e6532d2012-10-25 16:30:11 +08001314 def sync_time_with_shopfloor_server(self, foreground=False):
Jon Salz54882d02012-08-31 01:57:54 +08001315 '''Syncs time with shopfloor server, if not yet synced.
1316
Jon Salz0e6532d2012-10-25 16:30:11 +08001317 Args:
1318 foreground: If True, synchronizes even if background syncing
1319 is disabled (e.g., in explicit sync requests from the
1320 SyncShopfloor test).
1321
Jon Salz54882d02012-08-31 01:57:54 +08001322 Returns:
1323 False if no time sanitizer is available, or True if this sync (or a
1324 previous sync) succeeded.
1325
1326 Raises:
1327 Exception if unable to contact the shopfloor server.
1328 '''
Jon Salz0e6532d2012-10-25 16:30:11 +08001329 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001330 self.time_sanitizer.SyncWithShopfloor()
1331 self.time_synced = True
1332 return self.time_synced
1333
Jon Salzb92c5112012-09-21 15:40:11 +08001334 def log_disk_space_stats(self):
1335 if not self.test_list.options.log_disk_space_period_secs:
1336 return
1337
1338 now = time.time()
1339 if (self.last_log_disk_space_time and
1340 now - self.last_log_disk_space_time <
1341 self.test_list.options.log_disk_space_period_secs):
1342 return
1343 self.last_log_disk_space_time = now
1344
1345 try:
1346 logging.info(disk_space.FormatSpaceUsedAll())
1347 except: # pylint: disable=W0702
1348 logging.exception('Unable to get disk space used')
1349
Jon Salz8fa8e832012-07-13 19:04:09 +08001350 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001351 '''Writes out current time and tries to sync with shopfloor server.'''
1352 if not self.time_sanitizer:
1353 return
1354
1355 # Write out the current time.
1356 self.time_sanitizer.SaveTime()
1357
Jon Salz54882d02012-08-31 01:57:54 +08001358 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001359 return
1360
1361 now = time.time()
1362 if self.last_sync_time and (
1363 now - self.last_sync_time <
1364 self.test_list.options.sync_time_period_secs):
1365 # Not yet time for another check.
1366 return
1367 self.last_sync_time = now
1368
1369 def target():
1370 try:
Jon Salz54882d02012-08-31 01:57:54 +08001371 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001372 except: # pylint: disable=W0702
1373 # Oh well. Log an error (but no trace)
1374 logging.info(
1375 'Unable to get time from shopfloor server: %s',
1376 utils.FormatExceptionOnly())
1377
1378 thread = threading.Thread(target=target)
1379 thread.daemon = True
1380 thread.start()
1381
Jon Salz0697cbf2012-07-04 15:14:04 +08001382 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001383 '''Invoked when the run queue has no events.
1384
1385 This method must not raise exception.
1386 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001387 now = time.time()
1388 if (self.last_idle and
1389 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1390 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1391 # 1) seconds.
1392 return
1393
1394 self.last_idle = now
1395
Vic Yang311ddb82012-09-26 12:08:28 +08001396 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001397 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001398 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001399 self.log_disk_space_stats()
Jon Salz57717ca2012-04-04 16:47:25 +08001400
Jon Salz16d10542012-07-23 12:18:45 +08001401 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001402 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001403
Jon Salz0697cbf2012-07-04 15:14:04 +08001404 Attempts to upload the event logs to the shopfloor server.
1405 '''
1406 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1407 start_time = time.time()
Jon Salz0697cbf2012-07-04 15:14:04 +08001408 shopfloor_client = shopfloor.get_instance(
1409 detect=True,
1410 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzb10cf512012-08-09 17:29:21 +08001411 shopfloor_client.UploadEvent(log_name, Binary(chunk))
Jon Salz0697cbf2012-07-04 15:14:04 +08001412 logging.info(
1413 'Successfully synced %s in %.03f s',
1414 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001415
Jon Salz0697cbf2012-07-04 15:14:04 +08001416 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1417 root=None):
1418 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001419
Jon Salz0697cbf2012-07-04 15:14:04 +08001420 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001421
Jon Salz0697cbf2012-07-04 15:14:04 +08001422 Args:
1423 starting_at: If provided, only auto-runs tests beginning with
1424 this test.
1425 '''
1426 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001427
Jon Salz0697cbf2012-07-04 15:14:04 +08001428 if starting_at:
1429 # Make sure they passed a test, not a string.
1430 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001431
Jon Salz0697cbf2012-07-04 15:14:04 +08001432 tests_to_reset = []
1433 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001434
Jon Salz0697cbf2012-07-04 15:14:04 +08001435 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001436
Jon Salz0697cbf2012-07-04 15:14:04 +08001437 for test in root.get_top_level_tests():
1438 if starting_at:
1439 if test == starting_at:
1440 # We've found starting_at; do auto-run on all
1441 # subsequent tests.
1442 found_starting_at = True
1443 if not found_starting_at:
1444 # Don't start this guy yet
1445 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001446
Jon Salz0697cbf2012-07-04 15:14:04 +08001447 status = test.get_state().status
1448 if status == TestState.ACTIVE or status in statuses_to_run:
1449 # Reset the test (later; we will need to abort
1450 # all active tests first).
1451 tests_to_reset.append(test)
1452 if status in statuses_to_run:
1453 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001454
Jon Salz0697cbf2012-07-04 15:14:04 +08001455 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001456
Jon Salz0697cbf2012-07-04 15:14:04 +08001457 # Reset all statuses of the tests to run (in case any tests were active;
1458 # we want them to be run again).
1459 for test_to_reset in tests_to_reset:
1460 for test in test_to_reset.walk():
1461 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001462
Jon Salz0697cbf2012-07-04 15:14:04 +08001463 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001464
Jon Salz0697cbf2012-07-04 15:14:04 +08001465 def restart_tests(self, root=None):
1466 '''Restarts all tests.'''
1467 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001468
Jon Salz0697cbf2012-07-04 15:14:04 +08001469 self.abort_active_tests()
1470 for test in root.walk():
1471 test.update_state(status=TestState.UNTESTED)
1472 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001473
Jon Salz0697cbf2012-07-04 15:14:04 +08001474 def auto_run(self, starting_at=None, root=None):
1475 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001476
Jon Salz0697cbf2012-07-04 15:14:04 +08001477 Args:
1478 starting_at: If provide, only auto-runs tests beginning with
1479 this test.
1480 '''
1481 root = root or self.test_list
1482 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1483 starting_at=starting_at,
1484 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001485
Jon Salz0697cbf2012-07-04 15:14:04 +08001486 def re_run_failed(self, root=None):
1487 '''Re-runs failed tests.'''
1488 root = root or self.test_list
1489 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001490
Jon Salz0697cbf2012-07-04 15:14:04 +08001491 def show_review_information(self):
1492 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001493
Jon Salz0697cbf2012-07-04 15:14:04 +08001494 The information screene is rendered by main UI program (ui.py), so in
1495 goofy we only need to kill all active tests, set them as untested, and
1496 clear remaining tests.
1497 '''
1498 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001499 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001500
Jon Salz0697cbf2012-07-04 15:14:04 +08001501 def handle_switch_test(self, event):
1502 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001503
Jon Salz0697cbf2012-07-04 15:14:04 +08001504 @param event: The SWITCH_TEST event.
1505 '''
1506 test = self.test_list.lookup_path(event.path)
1507 if not test:
1508 logging.error('Unknown test %r', event.key)
1509 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001510
Jon Salz0697cbf2012-07-04 15:14:04 +08001511 invoc = self.invocations.get(test)
1512 if invoc and test.backgroundable:
1513 # Already running: just bring to the front if it
1514 # has a UI.
1515 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001516 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001517 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001518
Jon Salz0697cbf2012-07-04 15:14:04 +08001519 self.abort_active_tests()
1520 for t in test.walk():
1521 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001522
Jon Salz0697cbf2012-07-04 15:14:04 +08001523 if self.test_list.options.auto_run_on_keypress:
1524 self.auto_run(starting_at=test)
1525 else:
1526 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001527
Jon Salz0697cbf2012-07-04 15:14:04 +08001528 def wait(self):
1529 '''Waits for all pending invocations.
1530
1531 Useful for testing.
1532 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001533 while self.invocations:
1534 for k, v in self.invocations.iteritems():
1535 logging.info('Waiting for %s to complete...', k)
1536 v.thread.join()
1537 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001538
1539 def check_exceptions(self):
1540 '''Raises an error if any exceptions have occurred in
1541 invocation threads.'''
1542 if self.exceptions:
1543 raise RuntimeError('Exception in invocation thread: %r' %
1544 self.exceptions)
1545
1546 def record_exception(self, msg):
1547 '''Records an exception in an invocation thread.
1548
1549 An exception with the given message will be rethrown when
1550 Goofy is destroyed.'''
1551 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001552
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001553
1554if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001555 goofy = Goofy()
1556 try:
1557 goofy.main()
Jon Salz0f996602012-10-03 15:26:48 +08001558 except SystemExit:
1559 # Propagate SystemExit without logging.
1560 raise
Jon Salz31373eb2012-09-21 16:19:49 +08001561 except:
Jon Salz0f996602012-10-03 15:26:48 +08001562 # Log the error before trying to shut down (unless it's a graceful
1563 # exit).
Jon Salz31373eb2012-09-21 16:19:49 +08001564 logging.exception('Error in main loop')
1565 raise
Jon Salz77c151e2012-08-28 07:20:37 +08001566 finally:
1567 goofy.destroy()