blob: fa53e672f46d7f7922a0ddb5b1021d820294c04c [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
Tom Wai-Hong Tamd33723e2013-04-10 21:14:37 +080029from cros.factory.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080030from cros.factory.goofy import test_environment
31from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080032from cros.factory.goofy import updater
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
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +080037from cros.factory.system.board import Board, BoardException
jcliangcd688182012-08-20 21:01:26 +080038from cros.factory.system.charge_manager import ChargeManager
Jon Salzb92c5112012-09-21 15:40:11 +080039from cros.factory.system import disk_space
jcliangcd688182012-08-20 21:01:26 +080040from cros.factory.test import factory
41from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080042from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080043from cros.factory.test import utils
44from cros.factory.test.event import Event
45from cros.factory.test.event import EventClient
46from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080047from cros.factory.test.factory import TestState
Dean Liao592e4d52013-01-10 20:06:39 +080048from cros.factory.tools.key_filter import KeyFilter
Jon Salz78c32392012-07-25 14:18:29 +080049from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080050
51
Jon Salz2f757d42012-06-27 17:06:42 +080052CUSTOM_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.
Jon Salzfb615892013-02-01 18:04:35 +080098
99 The algorithm is:
100
101 - Try $FACTORY/test_lists/active (the symlink reflecting the option chosen
102 in the UI).
103 - For each of $FACTORY/custom, $FACTORY/test_lists (and
104 autotest/site_tests/suite_Factory for backward compatibility):
105 - Try test_list_${hwid_cfg} (if hwid_cfg is set)
106 - Try test_list
107 - Try test_list.generic
Jon Salz0697cbf2012-07-04 15:14:04 +0800108 '''
Jon Salzfb615892013-02-01 18:04:35 +0800109 # If the 'active' symlink is present, that trumps everything else.
110 if os.path.lexists(factory.ACTIVE_TEST_LIST_SYMLINK):
111 return os.path.realpath(factory.ACTIVE_TEST_LIST_SYMLINK)
112
Jon Salz0697cbf2012-07-04 15:14:04 +0800113 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800114
Jon Salzfb615892013-02-01 18:04:35 +0800115 search_dirs = [CUSTOM_DIR, factory.TEST_LISTS_PATH]
Jon Salz4be56b02012-12-22 07:30:46 +0800116 if not utils.in_chroot():
117 # Also look in suite_Factory. For backward compatibility only;
118 # new boards should just put the test list in the "test_lists"
119 # directory.
120 search_dirs.insert(0, os.path.join(
121 os.path.dirname(factory.FACTORY_PATH),
122 'autotest', 'site_tests', 'suite_Factory'))
Jon Salz2f757d42012-06-27 17:06:42 +0800123
Jon Salzfb615892013-02-01 18:04:35 +0800124
125 search_files = []
Jon Salz0697cbf2012-07-04 15:14:04 +0800126 if hwid_cfg:
Jon Salzfb615892013-02-01 18:04:35 +0800127 search_files += [hwid_cfg]
128 search_files += ['test_list', 'test_list.generic']
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800129
Jon Salz0697cbf2012-07-04 15:14:04 +0800130 for d in search_dirs:
131 for f in search_files:
132 test_list = os.path.join(d, f)
133 if os.path.exists(test_list):
134 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800135
Jon Salz0697cbf2012-07-04 15:14:04 +0800136 logging.warn('Cannot find test lists named any of %s in any of %s',
137 search_files, search_dirs)
138 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800139
Jon Salzfb615892013-02-01 18:04:35 +0800140
Jon Salz73e0fd02012-04-04 11:46:38 +0800141_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800142
143class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800144 '''
145 The main factory flow.
146
147 Note that all methods in this class must be invoked from the main
148 (event) thread. Other threads, such as callbacks and TestInvocation
149 methods, should instead post events on the run queue.
150
151 TODO: Unit tests. (chrome-os-partner:7409)
152
153 Properties:
154 uuid: A unique UUID for this invocation of Goofy.
155 state_instance: An instance of FactoryState.
156 state_server: The FactoryState XML/RPC server.
157 state_server_thread: A thread running state_server.
158 event_server: The EventServer socket server.
159 event_server_thread: A thread running event_server.
160 event_client: A client to the event server.
161 connection_manager: The connection_manager object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800162 ui_process: The factory ui process object.
163 run_queue: A queue of callbacks to invoke from the main thread.
164 invocations: A map from FactoryTest objects to the corresponding
165 TestInvocations objects representing active tests.
166 tests_to_run: A deque of tests that should be run when the current
167 test(s) complete.
168 options: Command-line options.
169 args: Command-line args.
170 test_list: The test list.
171 event_handlers: Map of Event.Type to the method used to handle that
172 event. If the method has an 'event' argument, the event is passed
173 to the handler.
174 exceptions: Exceptions encountered in invocation threads.
Jon Salz3c493bb2013-02-07 17:24:58 +0800175 last_log_disk_space_message: The last message we logged about disk space
176 (to avoid duplication).
Jon Salz0697cbf2012-07-04 15:14:04 +0800177 '''
178 def __init__(self):
179 self.uuid = str(uuid.uuid4())
180 self.state_instance = None
181 self.state_server = None
182 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800183 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800184 self.event_server = None
185 self.event_server_thread = None
186 self.event_client = None
187 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800188 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800189 self.time_sanitizer = None
190 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800191 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800192 self.event_log = None
193 self.prespawner = None
194 self.ui_process = None
Jon Salzc79a9982012-08-30 04:42:01 +0800195 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800196 self.run_queue = Queue.Queue()
197 self.invocations = {}
198 self.tests_to_run = deque()
199 self.visible_test = None
200 self.chrome = None
201
202 self.options = None
203 self.args = None
204 self.test_list = None
205 self.on_ui_startup = []
206 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800207 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800208 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800209 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800210 self.last_sync_time = None
Jon Salzb92c5112012-09-21 15:40:11 +0800211 self.last_log_disk_space_time = None
Jon Salz3c493bb2013-02-07 17:24:58 +0800212 self.last_log_disk_space_message = None
Vic Yang311ddb82012-09-26 12:08:28 +0800213 self.exclusive_items = set()
Jon Salz0f996602012-10-03 15:26:48 +0800214 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800215 self.key_filter = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800216
Jon Salz85a39882012-07-05 16:45:04 +0800217 def test_or_root(event, parent_or_group=True):
218 '''Returns the test affected by a particular event.
219
220 Args:
221 event: The event containing an optional 'path' attribute.
222 parent_on_group: If True, returns the top-level parent for a test (the
223 root node of the tests that need to be run together if the given test
224 path is to be run).
225 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800226 try:
227 path = event.path
228 except AttributeError:
229 path = None
230
231 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800232 test = self.test_list.lookup_path(path)
233 if parent_or_group:
234 test = test.get_top_level_parent_or_group()
235 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800236 else:
237 return self.test_list
238
239 self.event_handlers = {
240 Event.Type.SWITCH_TEST: self.handle_switch_test,
241 Event.Type.SHOW_NEXT_ACTIVE_TEST:
242 lambda event: self.show_next_active_test(),
243 Event.Type.RESTART_TESTS:
244 lambda event: self.restart_tests(root=test_or_root(event)),
245 Event.Type.AUTO_RUN:
246 lambda event: self.auto_run(root=test_or_root(event)),
247 Event.Type.RE_RUN_FAILED:
248 lambda event: self.re_run_failed(root=test_or_root(event)),
249 Event.Type.RUN_TESTS_WITH_STATUS:
250 lambda event: self.run_tests_with_status(
251 event.status,
252 root=test_or_root(event)),
253 Event.Type.REVIEW:
254 lambda event: self.show_review_information(),
255 Event.Type.UPDATE_SYSTEM_INFO:
256 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800257 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800258 lambda event: self.stop(root=test_or_root(event, False),
259 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800260 Event.Type.SET_VISIBLE_TEST:
261 lambda event: self.set_visible_test(
262 self.test_list.lookup_path(event.path)),
Jon Salz4712ac72013-02-07 17:12:05 +0800263 Event.Type.CLEAR_STATE:
264 lambda event: self.clear_state(self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800265 }
266
267 self.exceptions = []
268 self.web_socket_manager = None
269
270 def destroy(self):
271 if self.chrome:
272 self.chrome.kill()
273 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800274 if self.dummy_shopfloor:
275 self.dummy_shopfloor.kill()
276 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800277 if self.ui_process:
278 utils.kill_process_tree(self.ui_process, 'ui')
279 self.ui_process = None
280 if self.web_socket_manager:
281 logging.info('Stopping web sockets')
282 self.web_socket_manager.close()
283 self.web_socket_manager = None
284 if self.state_server_thread:
285 logging.info('Stopping state server')
286 self.state_server.shutdown()
287 self.state_server_thread.join()
288 self.state_server.server_close()
289 self.state_server_thread = None
290 if self.state_instance:
291 self.state_instance.close()
292 if self.event_server_thread:
293 logging.info('Stopping event server')
294 self.event_server.shutdown() # pylint: disable=E1101
295 self.event_server_thread.join()
296 self.event_server.server_close()
297 self.event_server_thread = None
298 if self.log_watcher:
299 if self.log_watcher.IsThreadStarted():
300 self.log_watcher.StopWatchThread()
301 self.log_watcher = None
302 if self.prespawner:
303 logging.info('Stopping prespawner')
304 self.prespawner.stop()
305 self.prespawner = None
306 if self.event_client:
307 logging.info('Closing event client')
308 self.event_client.close()
309 self.event_client = None
310 if self.event_log:
311 self.event_log.Close()
312 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800313 if self.key_filter:
314 self.key_filter.Stop()
315
Jon Salz0697cbf2012-07-04 15:14:04 +0800316 self.check_exceptions()
317 logging.info('Done destroying Goofy')
318
319 def start_state_server(self):
320 self.state_instance, self.state_server = (
321 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800322 self.goofy_rpc = GoofyRPC(self)
323 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800324 logging.info('Starting state server')
325 self.state_server_thread = threading.Thread(
326 target=self.state_server.serve_forever,
327 name='StateServer')
328 self.state_server_thread.start()
329
330 def start_event_server(self):
331 self.event_server = EventServer()
332 logging.info('Starting factory event server')
333 self.event_server_thread = threading.Thread(
334 target=self.event_server.serve_forever,
335 name='EventServer') # pylint: disable=E1101
336 self.event_server_thread.start()
337
338 self.event_client = EventClient(
339 callback=self.handle_event, event_loop=self.run_queue)
340
341 self.web_socket_manager = WebSocketManager(self.uuid)
342 self.state_server.add_handler("/event",
343 self.web_socket_manager.handle_web_socket)
344
345 def start_ui(self):
346 ui_proc_args = [
347 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
348 self.options.test_list]
349 if self.options.verbose:
350 ui_proc_args.append('-v')
351 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800352 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800353 logging.info('Waiting for UI to come up...')
354 self.event_client.wait(
355 lambda event: event.type == Event.Type.UI_READY)
356 logging.info('UI has started')
357
358 def set_visible_test(self, test):
359 if self.visible_test == test:
360 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800361 if test and not test.has_ui:
362 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800363
364 if test:
365 test.update_state(visible=True)
366 if self.visible_test:
367 self.visible_test.update_state(visible=False)
368 self.visible_test = test
369
Jon Salzd4306c82012-11-30 15:16:36 +0800370 def _log_startup_messages(self):
371 '''Logs the tail of var/log/messages and mosys and EC console logs.'''
372 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
373 # for factory-3004.B only. Consolidate and merge back to ToT.
374 if utils.in_chroot():
375 return
376
377 try:
378 var_log_messages = (
379 utils.var_log_messages_before_reboot())
380 logging.info(
381 'Tail of /var/log/messages before last reboot:\n'
382 '%s', ('\n'.join(
383 ' ' + x for x in var_log_messages)))
384 except: # pylint: disable=W0702
385 logging.exception('Unable to grok /var/log/messages')
386
387 try:
388 mosys_log = utils.Spawn(
389 ['mosys', 'eventlog', 'list'],
390 read_stdout=True, log_stderr_on_error=True).stdout_data
391 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
392 except: # pylint: disable=W0702
393 logging.exception('Unable to read mosys eventlog')
394
395 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800396 board = system.GetBoard()
397 ec_console_log = board.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800398 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
399 except: # pylint: disable=W0702
400 logging.exception('Error retrieving EC console log')
401
Jon Salz0697cbf2012-07-04 15:14:04 +0800402 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800403 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800404 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800405
Jon Salz0697cbf2012-07-04 15:14:04 +0800406 @param test: The ShutdownStep.
407 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800408 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800409 test_state = test.update_state(increment_shutdown_count=1)
410 logging.info('Detected shutdown (%d of %d)',
411 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800412
Jon Salz0697cbf2012-07-04 15:14:04 +0800413 def log_and_update_state(status, error_msg, **kw):
414 self.event_log.Log('rebooted',
415 status=status, error_msg=error_msg, **kw)
Jon Salzd4306c82012-11-30 15:16:36 +0800416 logging.info('Rebooted: status=%s, %s', status,
417 (('error_msg=%s' % error_msg) if error_msg else None))
Jon Salz0697cbf2012-07-04 15:14:04 +0800418 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800419
Jon Salz0697cbf2012-07-04 15:14:04 +0800420 if not self.last_shutdown_time:
421 log_and_update_state(status=TestState.FAILED,
422 error_msg='Unable to read shutdown_time')
423 return
Jon Salz258a40c2012-04-19 12:34:01 +0800424
Jon Salz0697cbf2012-07-04 15:14:04 +0800425 now = time.time()
426 logging.info('%.03f s passed since reboot',
427 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800428
Jon Salz0697cbf2012-07-04 15:14:04 +0800429 if self.last_shutdown_time > now:
430 test.update_state(status=TestState.FAILED,
431 error_msg='Time moved backward during reboot')
432 elif (isinstance(test, factory.RebootStep) and
433 self.test_list.options.max_reboot_time_secs and
434 (now - self.last_shutdown_time >
435 self.test_list.options.max_reboot_time_secs)):
436 # A reboot took too long; fail. (We don't check this for
437 # HaltSteps, because the machine could be halted for a
438 # very long time, and even unplugged with battery backup,
439 # thus hosing the clock.)
440 log_and_update_state(
441 status=TestState.FAILED,
442 error_msg=('More than %d s elapsed during reboot '
443 '(%.03f s, from %s to %s)' % (
444 self.test_list.options.max_reboot_time_secs,
445 now - self.last_shutdown_time,
446 utils.TimeString(self.last_shutdown_time),
447 utils.TimeString(now))),
448 duration=(now-self.last_shutdown_time))
Jon Salzd4306c82012-11-30 15:16:36 +0800449 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800450 elif test_state.shutdown_count == test.iterations:
451 # Good!
452 log_and_update_state(status=TestState.PASSED,
453 duration=(now - self.last_shutdown_time),
454 error_msg='')
455 elif test_state.shutdown_count > test.iterations:
456 # Shut down too many times
457 log_and_update_state(status=TestState.FAILED,
458 error_msg='Too many shutdowns')
Jon Salzd4306c82012-11-30 15:16:36 +0800459 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800460 elif utils.are_shift_keys_depressed():
461 logging.info('Shift keys are depressed; cancelling restarts')
462 # Abort shutdown
463 log_and_update_state(
464 status=TestState.FAILED,
465 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800466 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800467 else:
468 def handler():
469 if self._prompt_cancel_shutdown(
470 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800471 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800472 log_and_update_state(
473 status=TestState.FAILED,
474 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800475 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800476 return
Jon Salz0405ab52012-03-16 15:26:52 +0800477
Jon Salz0697cbf2012-07-04 15:14:04 +0800478 # Time to shutdown again
479 log_and_update_state(
480 status=TestState.ACTIVE,
481 error_msg='',
482 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800483
Jon Salz0697cbf2012-07-04 15:14:04 +0800484 self.event_log.Log('shutdown', operation='reboot')
485 self.state_instance.set_shared_data('shutdown_time',
486 time.time())
487 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800488
Jon Salz0697cbf2012-07-04 15:14:04 +0800489 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800490
Jon Salz0697cbf2012-07-04 15:14:04 +0800491 def _prompt_cancel_shutdown(self, test, iteration):
492 if self.options.ui != 'chrome':
493 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800494
Jon Salz0697cbf2012-07-04 15:14:04 +0800495 pending_shutdown_data = {
496 'delay_secs': test.delay_secs,
497 'time': time.time() + test.delay_secs,
498 'operation': test.operation,
499 'iteration': iteration,
500 'iterations': test.iterations,
501 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800502
Jon Salz0697cbf2012-07-04 15:14:04 +0800503 # Create a new (threaded) event client since we
504 # don't want to use the event loop for this.
505 with EventClient() as event_client:
506 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
507 **pending_shutdown_data))
508 aborted = event_client.wait(
509 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
510 timeout=test.delay_secs) is not None
511 if aborted:
512 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
513 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800514
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 def init_states(self):
516 '''
517 Initializes all states on startup.
518 '''
519 for test in self.test_list.get_all_tests():
520 # Make sure the state server knows about all the tests,
521 # defaulting to an untested state.
522 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800523
Jon Salz0697cbf2012-07-04 15:14:04 +0800524 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800525 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800526 ec_console_log = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800527
Jon Salz0697cbf2012-07-04 15:14:04 +0800528 # Any 'active' tests should be marked as failed now.
529 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800530 if not test.is_leaf():
531 # Don't bother with parents; they will be updated when their
532 # children are updated.
533 continue
534
Jon Salz0697cbf2012-07-04 15:14:04 +0800535 test_state = test.get_state()
536 if test_state.status != TestState.ACTIVE:
537 continue
538 if isinstance(test, factory.ShutdownStep):
539 # Shutdown while the test was active - that's good.
540 self.handle_shutdown_complete(test, test_state)
541 else:
542 # Unexpected shutdown. Grab /var/log/messages for context.
543 if var_log_messages is None:
544 try:
545 var_log_messages = (
546 utils.var_log_messages_before_reboot())
547 # Write it to the log, to make it easier to
548 # correlate with /var/log/messages.
549 logging.info(
550 'Unexpected shutdown. '
551 'Tail of /var/log/messages before last reboot:\n'
552 '%s', ('\n'.join(
553 ' ' + x for x in var_log_messages)))
554 except: # pylint: disable=W0702
555 logging.exception('Unable to grok /var/log/messages')
556 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800557
Jon Salz008f4ea2012-08-28 05:39:45 +0800558 if mosys_log is None and not utils.in_chroot():
559 try:
560 mosys_log = utils.Spawn(
561 ['mosys', 'eventlog', 'list'],
562 read_stdout=True, log_stderr_on_error=True).stdout_data
563 # Write it to the log also.
564 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
565 except: # pylint: disable=W0702
566 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800567
Vic Yange4c275d2012-08-28 01:50:20 +0800568 if ec_console_log is None:
569 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800570 board = system.GetBoard()
571 ec_console_log = board.GetECConsoleLog()
Vic Yange4c275d2012-08-28 01:50:20 +0800572 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Jon Salzfe1f6652012-09-07 05:40:14 +0800573 except: # pylint: disable=W0702
Vic Yange4c275d2012-08-28 01:50:20 +0800574 logging.exception('Error retrieving EC console log')
575
Jon Salz0697cbf2012-07-04 15:14:04 +0800576 error_msg = 'Unexpected shutdown while test was running'
577 self.event_log.Log('end_test',
578 path=test.path,
579 status=TestState.FAILED,
580 invocation=test.get_state().invocation,
581 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800582 var_log_messages='\n'.join(var_log_messages),
583 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800584 test.update_state(
585 status=TestState.FAILED,
586 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800587
Jon Salz50efe942012-07-26 11:54:10 +0800588 if not test.never_fails:
589 # For "never_fails" tests (such as "Start"), don't cancel
590 # pending tests, since reboot is expected.
591 factory.console.info('Unexpected shutdown while test %s '
592 'running; cancelling any pending tests',
593 test.path)
594 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800595
Jon Salz008f4ea2012-08-28 05:39:45 +0800596 self.update_skipped_tests()
597
598 def update_skipped_tests(self):
599 '''
600 Updates skipped states based on run_if.
601 '''
602 for t in self.test_list.walk():
603 if t.is_leaf() and t.run_if_table_name:
604 skip = False
605 try:
606 aux = shopfloor.get_selected_aux_data(t.run_if_table_name)
607 value = aux.get(t.run_if_col)
608 if value is not None:
609 skip = (not value) ^ t.run_if_not
610 except ValueError:
611 # Not available; assume it shouldn't be skipped
612 pass
613
614 test_state = t.get_state()
615 if ((not skip) and
616 (test_state.status == TestState.PASSED) and
617 (test_state.error_msg == TestState.SKIPPED_MSG)):
618 # It was marked as skipped before, but now we need to run it.
619 # Mark as untested.
620 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
621 else:
622 t.update_state(skip=skip)
623
Jon Salz0697cbf2012-07-04 15:14:04 +0800624 def show_next_active_test(self):
625 '''
626 Rotates to the next visible active test.
627 '''
628 self.reap_completed_tests()
629 active_tests = [
630 t for t in self.test_list.walk()
631 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
632 if not active_tests:
633 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800634
Jon Salz0697cbf2012-07-04 15:14:04 +0800635 try:
636 next_test = active_tests[
637 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
638 except ValueError: # visible_test not present in active_tests
639 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800640
Jon Salz0697cbf2012-07-04 15:14:04 +0800641 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800642
Jon Salz0697cbf2012-07-04 15:14:04 +0800643 def handle_event(self, event):
644 '''
645 Handles an event from the event server.
646 '''
647 handler = self.event_handlers.get(event.type)
648 if handler:
649 handler(event)
650 else:
651 # We don't register handlers for all event types - just ignore
652 # this event.
653 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800654
Vic Yangaabf9fd2013-04-09 18:56:13 +0800655 def check_critical_factory_note(self):
656 '''
657 Returns True if the last factory note is critical.
658 '''
659 notes = self.state_instance.get_shared_data('factory_note', True)
660 return notes and notes[-1]['level'] == 'CRITICAL'
661
Jon Salz0697cbf2012-07-04 15:14:04 +0800662 def run_next_test(self):
663 '''
664 Runs the next eligible test (or tests) in self.tests_to_run.
665 '''
666 self.reap_completed_tests()
Vic Yangaabf9fd2013-04-09 18:56:13 +0800667 if self.tests_to_run and self.check_critical_factory_note():
668 self.tests_to_run.clear()
669 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800670 while self.tests_to_run:
671 logging.debug('Tests to run: %s',
672 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800673
Jon Salz0697cbf2012-07-04 15:14:04 +0800674 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800675
Jon Salz0697cbf2012-07-04 15:14:04 +0800676 if test in self.invocations:
677 logging.info('Next test %s is already running', test.path)
678 self.tests_to_run.popleft()
679 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800680
Jon Salza1412922012-07-23 16:04:17 +0800681 for requirement in test.require_run:
682 for i in requirement.test.walk():
683 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800684 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800685 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800686 return
687
Jon Salz0697cbf2012-07-04 15:14:04 +0800688 if self.invocations and not (test.backgroundable and all(
689 [x.backgroundable for x in self.invocations])):
690 logging.debug('Waiting for non-backgroundable tests to '
691 'complete before running %s', test.path)
692 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800693
Jon Salz3e6f5202012-10-15 15:08:29 +0800694 if test.get_state().skip:
695 factory.console.info('Skipping test %s', test.path)
696 test.update_state(status=TestState.PASSED,
697 error_msg=TestState.SKIPPED_MSG)
698 self.tests_to_run.popleft()
699 continue
700
Jon Salz0697cbf2012-07-04 15:14:04 +0800701 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800702
Jon Salz304a75d2012-07-06 11:14:15 +0800703 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800704 for requirement in test.require_run:
705 for i in requirement.test.walk():
706 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800707 # We've hit this test itself; stop checking
708 break
Jon Salza1412922012-07-23 16:04:17 +0800709 if ((i.get_state().status == TestState.UNTESTED) or
710 (requirement.passed and i.get_state().status !=
711 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800712 # Found an untested test; move on to the next
713 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800714 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800715 break
716
717 if untested:
718 untested_paths = ', '.join(sorted([x.path for x in untested]))
719 if self.state_instance.get_shared_data('engineering_mode',
720 optional=True):
721 # In engineering mode, we'll let it go.
722 factory.console.warn('In engineering mode; running '
723 '%s even though required tests '
724 '[%s] have not completed',
725 test.path, untested_paths)
726 else:
727 # Not in engineering mode; mark it failed.
728 error_msg = ('Required tests [%s] have not been run yet'
729 % untested_paths)
730 factory.console.error('Not running %s: %s',
731 test.path, error_msg)
732 test.update_state(status=TestState.FAILED,
733 error_msg=error_msg)
734 continue
735
Jon Salz0697cbf2012-07-04 15:14:04 +0800736 if isinstance(test, factory.ShutdownStep):
737 if os.path.exists(NO_REBOOT_FILE):
738 test.update_state(
739 status=TestState.FAILED, increment_count=1,
740 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800741 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800742 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800743
Jon Salz0697cbf2012-07-04 15:14:04 +0800744 test.update_state(status=TestState.ACTIVE, increment_count=1,
745 error_msg='', shutdown_count=0)
746 if self._prompt_cancel_shutdown(test, 1):
747 self.event_log.Log('reboot_cancelled')
748 test.update_state(
749 status=TestState.FAILED, increment_count=1,
750 error_msg='Shutdown aborted by operator',
751 shutdown_count=0)
chungyiafe8f772012-08-15 19:36:29 +0800752 continue
Jon Salz2f757d42012-06-27 17:06:42 +0800753
Jon Salz0697cbf2012-07-04 15:14:04 +0800754 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800755 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800756 'tests_after_shutdown',
757 [t.path for t in self.tests_to_run])
758 # Save shutdown time
759 self.state_instance.set_shared_data('shutdown_time',
760 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800761
Jon Salz0697cbf2012-07-04 15:14:04 +0800762 with self.env.lock:
763 self.event_log.Log('shutdown', operation=test.operation)
764 shutdown_result = self.env.shutdown(test.operation)
765 if shutdown_result:
766 # That's all, folks!
767 self.run_queue.put(None)
768 return
769 else:
770 # Just pass (e.g., in the chroot).
771 test.update_state(status=TestState.PASSED)
772 self.state_instance.set_shared_data(
773 'tests_after_shutdown', None)
774 # Send event with no fields to indicate that there is no
775 # longer a pending shutdown.
776 self.event_client.post_event(Event(
777 Event.Type.PENDING_SHUTDOWN))
778 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800779
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800780 self._run_test(test, test.iterations, test.retries)
Jon Salz1acc8742012-07-17 17:45:55 +0800781
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800782 def _run_test(self, test, iterations_left=None, retries_left=None):
Jon Salz1acc8742012-07-17 17:45:55 +0800783 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
784 new_state = test.update_state(
785 status=TestState.ACTIVE, increment_count=1, error_msg='',
Jon Salzbd42ce12012-09-18 08:03:59 +0800786 invocation=invoc.uuid, iterations_left=iterations_left,
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800787 retries_left=retries_left,
Jon Salzbd42ce12012-09-18 08:03:59 +0800788 visible=(self.visible_test == test))
Jon Salz1acc8742012-07-17 17:45:55 +0800789 invoc.count = new_state.count
790
791 self.invocations[test] = invoc
792 if self.visible_test is None and test.has_ui:
793 self.set_visible_test(test)
Vic Yang311ddb82012-09-26 12:08:28 +0800794 self.check_exclusive()
Jon Salz1acc8742012-07-17 17:45:55 +0800795 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800796
Vic Yang311ddb82012-09-26 12:08:28 +0800797 def check_exclusive(self):
798 current_exclusive_items = set([
799 item
800 for item in factory.FactoryTest.EXCLUSIVE_OPTIONS
801 if any([test.is_exclusive(item) for test in self.invocations])])
802
803 new_exclusive_items = current_exclusive_items - self.exclusive_items
804 if factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING in new_exclusive_items:
805 logging.info('Disabling network')
806 self.connection_manager.DisableNetworking()
807 if factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in new_exclusive_items:
808 logging.info('Stop controlling charger')
809
810 new_non_exclusive_items = self.exclusive_items - current_exclusive_items
811 if (factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING in
812 new_non_exclusive_items):
813 logging.info('Re-enabling network')
814 self.connection_manager.EnableNetworking()
815 if factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in new_non_exclusive_items:
816 logging.info('Start controlling charger')
817
818 # Only adjust charge state if not excluded
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +0800819 if (factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER not in
Vic Yange83d9a12013-04-19 20:00:20 +0800820 current_exclusive_items and not utils.in_chroot()):
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +0800821 if self.charge_manager:
822 self.charge_manager.AdjustChargeState()
823 else:
824 try:
825 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
826 except BoardException:
827 logging.exception('Unable to set charge state on this board')
Vic Yang311ddb82012-09-26 12:08:28 +0800828
829 self.exclusive_items = current_exclusive_items
Jon Salz5da61e62012-05-31 13:06:22 +0800830
cychiang21886742012-07-05 15:16:32 +0800831 def check_for_updates(self):
832 '''
833 Schedules an asynchronous check for updates if necessary.
834 '''
835 if not self.test_list.options.update_period_secs:
836 # Not enabled.
837 return
838
839 now = time.time()
840 if self.last_update_check and (
841 now - self.last_update_check <
842 self.test_list.options.update_period_secs):
843 # Not yet time for another check.
844 return
845
846 self.last_update_check = now
847
848 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
849 if reached_shopfloor:
850 new_update_md5sum = md5sum if needs_update else None
851 if system.SystemInfo.update_md5sum != new_update_md5sum:
852 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
853 system.SystemInfo.update_md5sum = new_update_md5sum
854 self.run_queue.put(self.update_system_info)
855
856 updater.CheckForUpdateAsync(
857 handle_check_for_update,
858 self.test_list.options.shopfloor_timeout_secs)
859
Jon Salza6711d72012-07-18 14:33:03 +0800860 def cancel_pending_tests(self):
861 '''Cancels any tests in the run queue.'''
862 self.run_tests([])
863
Jon Salz0697cbf2012-07-04 15:14:04 +0800864 def run_tests(self, subtrees, untested_only=False):
865 '''
866 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800867
Jon Salz0697cbf2012-07-04 15:14:04 +0800868 The tests are run in order unless one fails (then stops).
869 Backgroundable tests are run simultaneously; when a foreground test is
870 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800871
Jon Salz0697cbf2012-07-04 15:14:04 +0800872 @param subtrees: Node or nodes containing tests to run (may either be
873 a single test or a list). Duplicates will be ignored.
874 '''
875 if type(subtrees) != list:
876 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800877
Jon Salz0697cbf2012-07-04 15:14:04 +0800878 # Nodes we've seen so far, to avoid duplicates.
879 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800880
Jon Salz0697cbf2012-07-04 15:14:04 +0800881 self.tests_to_run = deque()
882 for subtree in subtrees:
883 for test in subtree.walk():
884 if test in seen:
885 continue
886 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800887
Jon Salz0697cbf2012-07-04 15:14:04 +0800888 if not test.is_leaf():
889 continue
890 if (untested_only and
891 test.get_state().status != TestState.UNTESTED):
892 continue
893 self.tests_to_run.append(test)
894 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800895
Jon Salz0697cbf2012-07-04 15:14:04 +0800896 def reap_completed_tests(self):
897 '''
898 Removes completed tests from the set of active tests.
899
900 Also updates the visible test if it was reaped.
901 '''
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800902 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800903 for t, v in dict(self.invocations).iteritems():
904 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800905 test_completed = True
Jon Salz1acc8742012-07-17 17:45:55 +0800906 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800907 del self.invocations[t]
908
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800909 # Stop on failure if flag is true.
910 if (self.test_list.options.stop_on_failure and
911 new_state.status == TestState.FAILED):
912 # Clean all the tests to cause goofy to stop.
913 self.tests_to_run = []
914 factory.console.info("Stop on failure triggered. Empty the queue.")
915
Jon Salz1acc8742012-07-17 17:45:55 +0800916 if new_state.iterations_left and new_state.status == TestState.PASSED:
917 # Play it again, Sam!
918 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800919 # new_state.retries_left is obtained after update.
920 # For retries_left == 0, test can still be run for the last time.
921 elif (new_state.retries_left >= 0 and
922 new_state.status == TestState.FAILED):
923 # Still have to retry, Sam!
924 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +0800925
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800926 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800927 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800928
Jon Salz0697cbf2012-07-04 15:14:04 +0800929 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800930 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800931 self.set_visible_test(None)
932 # Make the first running test, if any, the visible test
933 for t in self.test_list.walk():
934 if t in self.invocations:
935 self.set_visible_test(t)
936 break
937
Jon Salz85a39882012-07-05 16:45:04 +0800938 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800939 '''
940 Kills and waits for all active tests.
941
Jon Salz85a39882012-07-05 16:45:04 +0800942 Args:
943 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800944 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800945 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800946 '''
947 self.reap_completed_tests()
948 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800949 if root and not test.has_ancestor(root):
950 continue
951
Jon Salz0697cbf2012-07-04 15:14:04 +0800952 factory.console.info('Killing active test %s...' % test.path)
953 invoc.abort_and_join()
954 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800955 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800956 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800957
Jon Salz0697cbf2012-07-04 15:14:04 +0800958 if not abort:
959 test.update_state(status=TestState.UNTESTED)
960 self.reap_completed_tests()
961
Jon Salz85a39882012-07-05 16:45:04 +0800962 def stop(self, root=None, fail=False):
963 self.kill_active_tests(fail, root)
964 # Remove any tests in the run queue under the root.
965 self.tests_to_run = deque([x for x in self.tests_to_run
966 if root and not x.has_ancestor(root)])
967 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800968
Jon Salz4712ac72013-02-07 17:12:05 +0800969 def clear_state(self, root=None):
970 self.stop(root)
971 for f in root.walk():
972 if f.is_leaf():
973 f.update_state(status=TestState.UNTESTED)
974
Jon Salz0697cbf2012-07-04 15:14:04 +0800975 def abort_active_tests(self):
976 self.kill_active_tests(True)
977
978 def main(self):
979 try:
980 self.init()
981 self.event_log.Log('goofy_init',
982 success=True)
983 except:
984 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800985 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800986 self.event_log.Log('goofy_init',
987 success=False,
988 trace=traceback.format_exc())
989 except: # pylint: disable=W0702
990 pass
991 raise
992
993 self.run()
994
995 def update_system_info(self):
996 '''Updates system info.'''
997 system_info = system.SystemInfo()
998 self.state_instance.set_shared_data('system_info', system_info.__dict__)
999 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
1000 system_info=system_info.__dict__))
1001 logging.info('System info: %r', system_info.__dict__)
1002
Jon Salzeb42f0d2012-07-27 19:14:04 +08001003 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
1004 '''Commences updating factory software.
1005
1006 Args:
1007 auto_run_on_restart: Auto-run when the machine comes back up.
1008 post_update_hook: Code to call after update but immediately before
1009 restart.
1010
1011 Returns:
1012 Never if the update was successful (we just reboot).
1013 False if the update was unnecessary (no update available).
1014 '''
Jon Salz0697cbf2012-07-04 15:14:04 +08001015 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001016 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001017
Jon Salz5c344f62012-07-13 14:31:16 +08001018 def pre_update_hook():
1019 if auto_run_on_restart:
1020 self.state_instance.set_shared_data('tests_after_shutdown',
1021 FORCE_AUTO_RUN)
1022 self.state_instance.close()
1023
Jon Salzeb42f0d2012-07-27 19:14:04 +08001024 if updater.TryUpdate(pre_update_hook=pre_update_hook):
1025 if post_update_hook:
1026 post_update_hook()
1027 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +08001028
Jon Salzcef132a2012-08-30 04:58:08 +08001029 def handle_sigint(self, dummy_signum, dummy_frame):
Jon Salz77c151e2012-08-28 07:20:37 +08001030 logging.error('Received SIGINT')
1031 self.run_queue.put(None)
1032 raise KeyboardInterrupt()
1033
Jon Salz0697cbf2012-07-04 15:14:04 +08001034 def init(self, args=None, env=None):
1035 '''Initializes Goofy.
1036
1037 Args:
1038 args: A list of command-line arguments. Uses sys.argv if
1039 args is None.
1040 env: An Environment instance to use (or None to choose
1041 FakeChrootEnvironment or DUTEnvironment as appropriate).
1042 '''
Jon Salz77c151e2012-08-28 07:20:37 +08001043 signal.signal(signal.SIGINT, self.handle_sigint)
1044
Jon Salz0697cbf2012-07-04 15:14:04 +08001045 parser = OptionParser()
1046 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001047 action='store_true',
1048 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001049 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001050 metavar='FILE',
1051 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001052 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001053 action='store_true',
1054 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001055 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +08001056 choices=['none', 'gtk', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001057 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001058 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001059 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001060 type='int', default=1,
1061 help=('Factor by which to scale UI '
1062 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001063 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001064 metavar='FILE',
1065 help='Use FILE as test list')
Jon Salzc79a9982012-08-30 04:42:01 +08001066 parser.add_option('--dummy_shopfloor', action='store_true',
1067 help='Use a dummy shopfloor server')
chungyiafe8f772012-08-15 19:36:29 +08001068 parser.add_option('--automation', dest='automation',
1069 action='store_true',
1070 help='Enable automation on running factory test')
Ricky Liang09216dc2013-02-22 17:26:45 +08001071 parser.add_option('--one_pixel_less', dest='one_pixel_less',
1072 action='store_true',
1073 help=('Start Chrome one pixel less than the full screen.'
1074 'Needed by Exynos platform to run GTK.'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001075 (self.options, self.args) = parser.parse_args(args)
1076
Jon Salz46b89562012-07-05 11:49:22 +08001077 # Make sure factory directories exist.
1078 factory.get_log_root()
1079 factory.get_state_root()
1080 factory.get_test_data_root()
1081
Jon Salz0697cbf2012-07-04 15:14:04 +08001082 global _inited_logging # pylint: disable=W0603
1083 if not _inited_logging:
1084 factory.init_logging('goofy', verbose=self.options.verbose)
1085 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001086
Jon Salz0f996602012-10-03 15:26:48 +08001087 if self.options.print_test_list:
1088 print factory.read_test_list(
1089 self.options.print_test_list).__repr__(recursive=True)
1090 sys.exit(0)
1091
Jon Salzee85d522012-07-17 14:34:46 +08001092 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +08001093 self.event_log = EventLog('goofy')
1094
1095 if (not suppress_chroot_warning and
1096 factory.in_chroot() and
1097 self.options.ui == 'gtk' and
1098 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
1099 # That's not going to work! Tell the user how to run
1100 # this way.
1101 logging.warn(GOOFY_IN_CHROOT_WARNING)
1102 time.sleep(1)
1103
1104 if env:
1105 self.env = env
1106 elif factory.in_chroot():
1107 self.env = test_environment.FakeChrootEnvironment()
1108 logging.warn(
1109 'Using chroot environment: will not actually run autotests')
1110 else:
1111 self.env = test_environment.DUTEnvironment()
1112 self.env.goofy = self
1113
1114 if self.options.restart:
1115 state.clear_state()
1116
Jon Salz0697cbf2012-07-04 15:14:04 +08001117 if self.options.ui_scale_factor != 1 and utils.in_qemu():
1118 logging.warn(
1119 'In QEMU; ignoring ui_scale_factor argument')
1120 self.options.ui_scale_factor = 1
1121
1122 logging.info('Started')
1123
1124 self.start_state_server()
1125 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1126 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001127 self.options.ui_scale_factor)
1128 self.state_instance.set_shared_data('one_pixel_less',
1129 self.options.one_pixel_less)
Jon Salz0697cbf2012-07-04 15:14:04 +08001130 self.last_shutdown_time = (
1131 self.state_instance.get_shared_data('shutdown_time', optional=True))
1132 self.state_instance.del_shared_data('shutdown_time', optional=True)
1133
Jon Salzb19ea072013-02-07 16:35:00 +08001134 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001135 if not self.options.test_list:
1136 self.options.test_list = find_test_list()
Jon Salzb19ea072013-02-07 16:35:00 +08001137 if self.options.test_list:
Jon Salz0697cbf2012-07-04 15:14:04 +08001138 logging.info('Using test list %s', self.options.test_list)
Jon Salzb19ea072013-02-07 16:35:00 +08001139 try:
1140 self.test_list = factory.read_test_list(
1141 self.options.test_list,
1142 self.state_instance)
1143 except: # pylint: disable=W0702
1144 logging.exception('Unable to read test list %r', self.options.test_list)
1145 self.state_instance.set_shared_data('startup_error',
1146 'Unable to read test list %s\n%s' % (
1147 self.options.test_list,
1148 traceback.format_exc()))
1149 else:
1150 logging.error('No test list found.')
1151 self.state_instance.set_shared_data('startup_error',
1152 'No test list found.')
Jon Salz0697cbf2012-07-04 15:14:04 +08001153
Jon Salzb19ea072013-02-07 16:35:00 +08001154 if not self.test_list:
1155 if self.options.ui == 'chrome':
1156 # Create an empty test list with default options so that the rest of
1157 # startup can proceed.
1158 self.test_list = factory.FactoryTestList(
1159 [], self.state_instance, factory.Options())
1160 else:
1161 # Bail with an error; no point in starting up.
1162 sys.exit('No valid test list; exiting.')
1163
Jon Salz822838b2013-03-25 17:32:33 +08001164 if self.test_list.options.clear_state_on_start:
1165 self.state_instance.clear_test_state()
1166
Jon Salz0697cbf2012-07-04 15:14:04 +08001167 if not self.state_instance.has_shared_data('ui_lang'):
1168 self.state_instance.set_shared_data('ui_lang',
1169 self.test_list.options.ui_lang)
1170 self.state_instance.set_shared_data(
1171 'test_list_options',
1172 self.test_list.options.__dict__)
1173 self.state_instance.test_list = self.test_list
1174
Jon Salz83ef34b2012-11-01 19:46:35 +08001175 if not utils.in_chroot() and self.test_list.options.disable_log_rotation:
1176 open('/var/lib/cleanup_logs_paused', 'w').close()
1177
Jon Salz23926422012-09-01 03:38:13 +08001178 if self.options.dummy_shopfloor:
1179 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1180 'http://localhost:%d/' % shopfloor.DEFAULT_SERVER_PORT)
1181 self.dummy_shopfloor = Spawn(
1182 [os.path.join(factory.FACTORY_PATH, 'bin', 'shopfloor_server'),
1183 '--dummy'])
1184 elif self.test_list.options.shopfloor_server_url:
1185 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001186 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001187
Jon Salz0f996602012-10-03 15:26:48 +08001188 if self.test_list.options.time_sanitizer and not utils.in_chroot():
Jon Salz8fa8e832012-07-13 19:04:09 +08001189 self.time_sanitizer = time_sanitizer.TimeSanitizer(
1190 base_time=time_sanitizer.GetBaseTimeFromFile(
1191 # lsb-factory is written by the factory install shim during
1192 # installation, so it should have a good time obtained from
Jon Salz54882d02012-08-31 01:57:54 +08001193 # the mini-Omaha server. If it's not available, we'll use
1194 # /etc/lsb-factory (which will be much older, but reasonably
1195 # sane) and rely on a shopfloor sync to set a more accurate
1196 # time.
1197 '/usr/local/etc/lsb-factory',
1198 '/etc/lsb-release'))
Jon Salz8fa8e832012-07-13 19:04:09 +08001199 self.time_sanitizer.RunOnce()
1200
Jon Salz0697cbf2012-07-04 15:14:04 +08001201 self.init_states()
1202 self.start_event_server()
1203 self.connection_manager = self.env.create_connection_manager(
Tai-Hsu Lin371351a2012-08-27 14:17:14 +08001204 self.test_list.options.wlans,
1205 self.test_list.options.scan_wifi_period_secs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001206 # Note that we create a log watcher even if
1207 # sync_event_log_period_secs isn't set (no background
1208 # syncing), since we may use it to flush event logs as well.
1209 self.log_watcher = EventLogWatcher(
1210 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +08001211 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001212 if self.test_list.options.sync_event_log_period_secs:
1213 self.log_watcher.StartWatchThread()
1214
1215 self.update_system_info()
1216
Vic Yang4953fc12012-07-26 16:19:53 +08001217 assert ((self.test_list.options.min_charge_pct is None) ==
1218 (self.test_list.options.max_charge_pct is None))
Vic Yange83d9a12013-04-19 20:00:20 +08001219 if utils.in_chroot():
1220 logging.info('In chroot, ignoring charge manager and charge state')
1221 elif self.test_list.options.min_charge_pct is not None:
Vic Yang4953fc12012-07-26 16:19:53 +08001222 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1223 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001224 system.SystemStatus.charge_manager = self.charge_manager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +08001225 else:
1226 # Goofy should set charger state to charge if charge_manager is disabled.
1227 try:
1228 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
1229 except BoardException:
1230 logging.exception('Unable to set charge state on this board')
Vic Yang4953fc12012-07-26 16:19:53 +08001231
Jon Salz0697cbf2012-07-04 15:14:04 +08001232 os.environ['CROS_FACTORY'] = '1'
1233 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1234
1235 # Set CROS_UI since some behaviors in ui.py depend on the
1236 # particular UI in use. TODO(jsalz): Remove this (and all
1237 # places it is used) when the GTK UI is removed.
1238 os.environ['CROS_UI'] = self.options.ui
1239
1240 if self.options.ui == 'chrome':
1241 self.env.launch_chrome()
1242 logging.info('Waiting for a web socket connection')
Cheng-Yi Chiangfd8ed392013-03-08 21:37:31 +08001243 self.web_socket_manager.wait()
Jon Salz0697cbf2012-07-04 15:14:04 +08001244
1245 # Wait for the test widget size to be set; this is done in
1246 # an asynchronous RPC so there is a small chance that the
1247 # web socket might be opened first.
1248 for _ in range(100): # 10 s
1249 try:
1250 if self.state_instance.get_shared_data('test_widget_size'):
1251 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001252 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001253 pass # Retry
1254 time.sleep(0.1) # 100 ms
1255 else:
1256 logging.warn('Never received test_widget_size from UI')
1257 elif self.options.ui == 'gtk':
1258 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001259
Ricky Liang650f6bf2012-09-28 13:22:54 +08001260 # Create download path for autotest beforehand or autotests run at
1261 # the same time might fail due to race condition.
1262 if not factory.in_chroot():
1263 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1264 'download'))
1265
Jon Salz0697cbf2012-07-04 15:14:04 +08001266 def state_change_callback(test, test_state):
1267 self.event_client.post_event(
1268 Event(Event.Type.STATE_CHANGE,
1269 path=test.path, state=test_state))
1270 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001271
Jon Salza6711d72012-07-18 14:33:03 +08001272 for handler in self.on_ui_startup:
1273 handler()
1274
1275 self.prespawner = Prespawner()
1276 self.prespawner.start()
1277
Jon Salz0697cbf2012-07-04 15:14:04 +08001278 try:
1279 tests_after_shutdown = self.state_instance.get_shared_data(
1280 'tests_after_shutdown')
1281 except KeyError:
1282 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001283
Jon Salz5c344f62012-07-13 14:31:16 +08001284 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1285 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001286 logging.info('Resuming tests after shutdown: %s',
1287 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001288 self.tests_to_run.extend(
1289 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1290 self.run_queue.put(self.run_next_test)
1291 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001292 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001293 self.run_queue.put(
1294 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001295 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001296
Dean Liao592e4d52013-01-10 20:06:39 +08001297 self.may_disable_cros_shortcut_keys()
1298
1299 def may_disable_cros_shortcut_keys(self):
1300 test_options = self.test_list.options
1301 if test_options.disable_cros_shortcut_keys:
1302 logging.info('Filter ChromeOS shortcut keys.')
1303 self.key_filter = KeyFilter(
1304 unmap_caps_lock=test_options.disable_caps_lock,
1305 caps_lock_keycode=test_options.caps_lock_keycode)
1306 self.key_filter.Start()
1307
Jon Salz0697cbf2012-07-04 15:14:04 +08001308 def run(self):
1309 '''Runs Goofy.'''
1310 # Process events forever.
1311 while self.run_once(True):
1312 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001313
Jon Salz0697cbf2012-07-04 15:14:04 +08001314 def run_once(self, block=False):
1315 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001316
Jon Salz0697cbf2012-07-04 15:14:04 +08001317 Args:
1318 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001319
Jon Salz0697cbf2012-07-04 15:14:04 +08001320 Returns:
1321 True to keep going or False to shut down.
1322 '''
1323 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001324 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001325 # Nothing on the run queue.
1326 self._run_queue_idle()
1327 if block:
1328 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001329 try:
1330 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1331 except Queue.Empty:
1332 # Keep going (calling _run_queue_idle() again at the top of
1333 # the loop)
1334 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001335 # ...and grab anything else that showed up at the same
1336 # time.
1337 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001338 else:
1339 break
Jon Salz51528e12012-07-02 18:54:45 +08001340
Jon Salz0697cbf2012-07-04 15:14:04 +08001341 for event in events:
1342 if not event:
1343 # Shutdown request.
1344 self.run_queue.task_done()
1345 return False
Jon Salz51528e12012-07-02 18:54:45 +08001346
Jon Salz0697cbf2012-07-04 15:14:04 +08001347 try:
1348 event()
Jon Salz85a39882012-07-05 16:45:04 +08001349 except: # pylint: disable=W0702
1350 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001351 self.record_exception(traceback.format_exception_only(
1352 *sys.exc_info()[:2]))
1353 # But keep going
1354 finally:
1355 self.run_queue.task_done()
1356 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001357
Jon Salz0e6532d2012-10-25 16:30:11 +08001358 def _should_sync_time(self, foreground=False):
1359 '''Returns True if we should attempt syncing time with shopfloor.
1360
1361 Args:
1362 foreground: If True, synchronizes even if background syncing
1363 is disabled (e.g., in explicit sync requests from the
1364 SyncShopfloor test).
1365 '''
1366 return ((foreground or
1367 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001368 self.time_sanitizer and
1369 (not self.time_synced) and
1370 (not factory.in_chroot()))
1371
Jon Salz0e6532d2012-10-25 16:30:11 +08001372 def sync_time_with_shopfloor_server(self, foreground=False):
Jon Salz54882d02012-08-31 01:57:54 +08001373 '''Syncs time with shopfloor server, if not yet synced.
1374
Jon Salz0e6532d2012-10-25 16:30:11 +08001375 Args:
1376 foreground: If True, synchronizes even if background syncing
1377 is disabled (e.g., in explicit sync requests from the
1378 SyncShopfloor test).
1379
Jon Salz54882d02012-08-31 01:57:54 +08001380 Returns:
1381 False if no time sanitizer is available, or True if this sync (or a
1382 previous sync) succeeded.
1383
1384 Raises:
1385 Exception if unable to contact the shopfloor server.
1386 '''
Jon Salz0e6532d2012-10-25 16:30:11 +08001387 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001388 self.time_sanitizer.SyncWithShopfloor()
1389 self.time_synced = True
1390 return self.time_synced
1391
Jon Salzb92c5112012-09-21 15:40:11 +08001392 def log_disk_space_stats(self):
1393 if not self.test_list.options.log_disk_space_period_secs:
1394 return
1395
1396 now = time.time()
1397 if (self.last_log_disk_space_time and
1398 now - self.last_log_disk_space_time <
1399 self.test_list.options.log_disk_space_period_secs):
1400 return
1401 self.last_log_disk_space_time = now
1402
1403 try:
Jon Salz3c493bb2013-02-07 17:24:58 +08001404 message = disk_space.FormatSpaceUsedAll()
1405 if message != self.last_log_disk_space_message:
1406 logging.info(message)
1407 self.last_log_disk_space_message = message
Jon Salzb92c5112012-09-21 15:40:11 +08001408 except: # pylint: disable=W0702
1409 logging.exception('Unable to get disk space used')
1410
Jon Salz8fa8e832012-07-13 19:04:09 +08001411 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001412 '''Writes out current time and tries to sync with shopfloor server.'''
1413 if not self.time_sanitizer:
1414 return
1415
1416 # Write out the current time.
1417 self.time_sanitizer.SaveTime()
1418
Jon Salz54882d02012-08-31 01:57:54 +08001419 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001420 return
1421
1422 now = time.time()
1423 if self.last_sync_time and (
1424 now - self.last_sync_time <
1425 self.test_list.options.sync_time_period_secs):
1426 # Not yet time for another check.
1427 return
1428 self.last_sync_time = now
1429
1430 def target():
1431 try:
Jon Salz54882d02012-08-31 01:57:54 +08001432 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001433 except: # pylint: disable=W0702
1434 # Oh well. Log an error (but no trace)
1435 logging.info(
1436 'Unable to get time from shopfloor server: %s',
1437 utils.FormatExceptionOnly())
1438
1439 thread = threading.Thread(target=target)
1440 thread.daemon = True
1441 thread.start()
1442
Jon Salz0697cbf2012-07-04 15:14:04 +08001443 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001444 '''Invoked when the run queue has no events.
1445
1446 This method must not raise exception.
1447 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001448 now = time.time()
1449 if (self.last_idle and
1450 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1451 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1452 # 1) seconds.
1453 return
1454
1455 self.last_idle = now
1456
Vic Yang311ddb82012-09-26 12:08:28 +08001457 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001458 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001459 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001460 self.log_disk_space_stats()
Jon Salz57717ca2012-04-04 16:47:25 +08001461
Jon Salz16d10542012-07-23 12:18:45 +08001462 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001463 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001464
Jon Salz0697cbf2012-07-04 15:14:04 +08001465 Attempts to upload the event logs to the shopfloor server.
1466 '''
1467 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1468 start_time = time.time()
Jon Salz0697cbf2012-07-04 15:14:04 +08001469 shopfloor_client = shopfloor.get_instance(
1470 detect=True,
1471 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzb10cf512012-08-09 17:29:21 +08001472 shopfloor_client.UploadEvent(log_name, Binary(chunk))
Jon Salz0697cbf2012-07-04 15:14:04 +08001473 logging.info(
1474 'Successfully synced %s in %.03f s',
1475 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001476
Jon Salz0697cbf2012-07-04 15:14:04 +08001477 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1478 root=None):
1479 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001480
Jon Salz0697cbf2012-07-04 15:14:04 +08001481 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001482
Jon Salz0697cbf2012-07-04 15:14:04 +08001483 Args:
1484 starting_at: If provided, only auto-runs tests beginning with
1485 this test.
1486 '''
1487 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001488
Jon Salz0697cbf2012-07-04 15:14:04 +08001489 if starting_at:
1490 # Make sure they passed a test, not a string.
1491 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001492
Jon Salz0697cbf2012-07-04 15:14:04 +08001493 tests_to_reset = []
1494 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001495
Jon Salz0697cbf2012-07-04 15:14:04 +08001496 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001497
Jon Salz0697cbf2012-07-04 15:14:04 +08001498 for test in root.get_top_level_tests():
1499 if starting_at:
1500 if test == starting_at:
1501 # We've found starting_at; do auto-run on all
1502 # subsequent tests.
1503 found_starting_at = True
1504 if not found_starting_at:
1505 # Don't start this guy yet
1506 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001507
Jon Salz0697cbf2012-07-04 15:14:04 +08001508 status = test.get_state().status
1509 if status == TestState.ACTIVE or status in statuses_to_run:
1510 # Reset the test (later; we will need to abort
1511 # all active tests first).
1512 tests_to_reset.append(test)
1513 if status in statuses_to_run:
1514 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001515
Jon Salz0697cbf2012-07-04 15:14:04 +08001516 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001517
Jon Salz0697cbf2012-07-04 15:14:04 +08001518 # Reset all statuses of the tests to run (in case any tests were active;
1519 # we want them to be run again).
1520 for test_to_reset in tests_to_reset:
1521 for test in test_to_reset.walk():
1522 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001523
Jon Salz0697cbf2012-07-04 15:14:04 +08001524 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001525
Jon Salz0697cbf2012-07-04 15:14:04 +08001526 def restart_tests(self, root=None):
1527 '''Restarts all tests.'''
1528 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001529
Jon Salz0697cbf2012-07-04 15:14:04 +08001530 self.abort_active_tests()
1531 for test in root.walk():
1532 test.update_state(status=TestState.UNTESTED)
1533 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001534
Jon Salz0697cbf2012-07-04 15:14:04 +08001535 def auto_run(self, starting_at=None, root=None):
1536 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001537
Jon Salz0697cbf2012-07-04 15:14:04 +08001538 Args:
1539 starting_at: If provide, only auto-runs tests beginning with
1540 this test.
1541 '''
1542 root = root or self.test_list
1543 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1544 starting_at=starting_at,
1545 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001546
Jon Salz0697cbf2012-07-04 15:14:04 +08001547 def re_run_failed(self, root=None):
1548 '''Re-runs failed tests.'''
1549 root = root or self.test_list
1550 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001551
Jon Salz0697cbf2012-07-04 15:14:04 +08001552 def show_review_information(self):
1553 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001554
Jon Salz0697cbf2012-07-04 15:14:04 +08001555 The information screene is rendered by main UI program (ui.py), so in
1556 goofy we only need to kill all active tests, set them as untested, and
1557 clear remaining tests.
1558 '''
1559 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001560 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001561
Jon Salz0697cbf2012-07-04 15:14:04 +08001562 def handle_switch_test(self, event):
1563 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001564
Jon Salz0697cbf2012-07-04 15:14:04 +08001565 @param event: The SWITCH_TEST event.
1566 '''
1567 test = self.test_list.lookup_path(event.path)
1568 if not test:
1569 logging.error('Unknown test %r', event.key)
1570 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001571
Jon Salz0697cbf2012-07-04 15:14:04 +08001572 invoc = self.invocations.get(test)
1573 if invoc and test.backgroundable:
1574 # Already running: just bring to the front if it
1575 # has a UI.
1576 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001577 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001578 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001579
Jon Salz0697cbf2012-07-04 15:14:04 +08001580 self.abort_active_tests()
1581 for t in test.walk():
1582 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001583
Jon Salz0697cbf2012-07-04 15:14:04 +08001584 if self.test_list.options.auto_run_on_keypress:
1585 self.auto_run(starting_at=test)
1586 else:
1587 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001588
Jon Salz0697cbf2012-07-04 15:14:04 +08001589 def wait(self):
1590 '''Waits for all pending invocations.
1591
1592 Useful for testing.
1593 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001594 while self.invocations:
1595 for k, v in self.invocations.iteritems():
1596 logging.info('Waiting for %s to complete...', k)
1597 v.thread.join()
1598 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001599
1600 def check_exceptions(self):
1601 '''Raises an error if any exceptions have occurred in
1602 invocation threads.'''
1603 if self.exceptions:
1604 raise RuntimeError('Exception in invocation thread: %r' %
1605 self.exceptions)
1606
1607 def record_exception(self, msg):
1608 '''Records an exception in an invocation thread.
1609
1610 An exception with the given message will be rethrown when
1611 Goofy is destroyed.'''
1612 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001613
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001614
1615if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001616 goofy = Goofy()
1617 try:
1618 goofy.main()
Jon Salz0f996602012-10-03 15:26:48 +08001619 except SystemExit:
1620 # Propagate SystemExit without logging.
1621 raise
Jon Salz31373eb2012-09-21 16:19:49 +08001622 except:
Jon Salz0f996602012-10-03 15:26:48 +08001623 # Log the error before trying to shut down (unless it's a graceful
1624 # exit).
Jon Salz31373eb2012-09-21 16:19:49 +08001625 logging.exception('Error in main loop')
1626 raise
Jon Salz77c151e2012-08-28 07:20:37 +08001627 finally:
1628 goofy.destroy()