blob: 4a1758318288cf0440e392e18d1bdf8ab7afda8e [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
Dean Liao44392962013-03-07 17:10:08 +080036from cros.factory.goofy.web_socket_manager import WebSocketException
jcliangcd688182012-08-20 21:01:26 +080037from cros.factory.goofy.web_socket_manager import WebSocketManager
38from 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 +080052DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
53CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080054HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Chun-ta Lin279e7e92013-02-19 17:40:39 +080055CACHES_DIR = os.path.join(factory.get_state_root(), "caches")
Hung-Te Linf2f78f72012-02-08 19:27:11 +080056
Jon Salz8796e362012-05-24 11:39:09 +080057# File that suppresses reboot if present (e.g., for development).
58NO_REBOOT_FILE = '/var/log/factory.noreboot'
59
Jon Salz5c344f62012-07-13 14:31:16 +080060# Value for tests_after_shutdown that forces auto-run (e.g., after
61# a factory update, when the available set of tests might change).
62FORCE_AUTO_RUN = 'force_auto_run'
63
cychiang21886742012-07-05 15:16:32 +080064RUN_QUEUE_TIMEOUT_SECS = 10
65
Dean Liao44392962013-03-07 17:10:08 +080066WAIT_FOR_SOCKET_TIMEOUT_SECS = 5
67
Jon Salz758e6cc2012-04-03 15:47:07 +080068GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
69You are running Goofy inside the chroot. Autotests are not supported.
70
71To use Goofy in the chroot, first install an Xvnc server:
72
Jon Salz0697cbf2012-07-04 15:14:04 +080073 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080074
75...and then start a VNC X server outside the chroot:
76
Jon Salz0697cbf2012-07-04 15:14:04 +080077 vncserver :10 &
78 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080079
80...and run Goofy as follows:
81
Jon Salz0697cbf2012-07-04 15:14:04 +080082 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080083''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080084suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080085
86def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080087 '''
88 Returns the HWID config tag, or an empty string if none can be found.
89 '''
90 if 'CROS_HWID' in os.environ:
91 return os.environ['CROS_HWID']
92 if os.path.exists(HWID_CFG_PATH):
93 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
94 return hwid_cfg_handle.read().strip()
95 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080096
97
98def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080099 '''
100 Returns the path to the active test list, based on the HWID config tag.
101 '''
102 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800103
Jon Salz4be56b02012-12-22 07:30:46 +0800104 search_dirs = [DEFAULT_TEST_LISTS_DIR]
105 if not utils.in_chroot():
106 # Also look in suite_Factory. For backward compatibility only;
107 # new boards should just put the test list in the "test_lists"
108 # directory.
109 search_dirs.insert(0, os.path.join(
110 os.path.dirname(factory.FACTORY_PATH),
111 'autotest', 'site_tests', 'suite_Factory'))
Jon Salz2f757d42012-06-27 17:06:42 +0800112
Jon Salz0697cbf2012-07-04 15:14:04 +0800113 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
114 search_files = ['test_list', 'test_list.all']
115 if hwid_cfg:
116 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800117
Jon Salz0697cbf2012-07-04 15:14:04 +0800118 for d in search_dirs:
119 for f in search_files:
120 test_list = os.path.join(d, f)
121 if os.path.exists(test_list):
122 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800123
Jon Salz0697cbf2012-07-04 15:14:04 +0800124 logging.warn('Cannot find test lists named any of %s in any of %s',
125 search_files, search_dirs)
126 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800127
Jon Salz73e0fd02012-04-04 11:46:38 +0800128_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800129
130class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800131 '''
132 The main factory flow.
133
134 Note that all methods in this class must be invoked from the main
135 (event) thread. Other threads, such as callbacks and TestInvocation
136 methods, should instead post events on the run queue.
137
138 TODO: Unit tests. (chrome-os-partner:7409)
139
140 Properties:
141 uuid: A unique UUID for this invocation of Goofy.
142 state_instance: An instance of FactoryState.
143 state_server: The FactoryState XML/RPC server.
144 state_server_thread: A thread running state_server.
145 event_server: The EventServer socket server.
146 event_server_thread: A thread running event_server.
147 event_client: A client to the event server.
148 connection_manager: The connection_manager object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800149 ui_process: The factory ui process object.
150 run_queue: A queue of callbacks to invoke from the main thread.
151 invocations: A map from FactoryTest objects to the corresponding
152 TestInvocations objects representing active tests.
153 tests_to_run: A deque of tests that should be run when the current
154 test(s) complete.
155 options: Command-line options.
156 args: Command-line args.
157 test_list: The test list.
158 event_handlers: Map of Event.Type to the method used to handle that
159 event. If the method has an 'event' argument, the event is passed
160 to the handler.
161 exceptions: Exceptions encountered in invocation threads.
Jon Salz3c493bb2013-02-07 17:24:58 +0800162 last_log_disk_space_message: The last message we logged about disk space
163 (to avoid duplication).
Jon Salz0697cbf2012-07-04 15:14:04 +0800164 '''
165 def __init__(self):
166 self.uuid = str(uuid.uuid4())
167 self.state_instance = None
168 self.state_server = None
169 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800170 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800171 self.event_server = None
172 self.event_server_thread = None
173 self.event_client = None
174 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800175 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800176 self.time_sanitizer = None
177 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800178 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800179 self.event_log = None
180 self.prespawner = None
181 self.ui_process = None
Jon Salzc79a9982012-08-30 04:42:01 +0800182 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800183 self.run_queue = Queue.Queue()
184 self.invocations = {}
185 self.tests_to_run = deque()
186 self.visible_test = None
187 self.chrome = None
188
189 self.options = None
190 self.args = None
191 self.test_list = None
192 self.on_ui_startup = []
193 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800194 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800195 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800196 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800197 self.last_sync_time = None
Jon Salzb92c5112012-09-21 15:40:11 +0800198 self.last_log_disk_space_time = None
Jon Salz3c493bb2013-02-07 17:24:58 +0800199 self.last_log_disk_space_message = None
Vic Yang311ddb82012-09-26 12:08:28 +0800200 self.exclusive_items = set()
Jon Salz0f996602012-10-03 15:26:48 +0800201 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800202 self.key_filter = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800203
Jon Salz85a39882012-07-05 16:45:04 +0800204 def test_or_root(event, parent_or_group=True):
205 '''Returns the test affected by a particular event.
206
207 Args:
208 event: The event containing an optional 'path' attribute.
209 parent_on_group: If True, returns the top-level parent for a test (the
210 root node of the tests that need to be run together if the given test
211 path is to be run).
212 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800213 try:
214 path = event.path
215 except AttributeError:
216 path = None
217
218 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800219 test = self.test_list.lookup_path(path)
220 if parent_or_group:
221 test = test.get_top_level_parent_or_group()
222 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800223 else:
224 return self.test_list
225
226 self.event_handlers = {
227 Event.Type.SWITCH_TEST: self.handle_switch_test,
228 Event.Type.SHOW_NEXT_ACTIVE_TEST:
229 lambda event: self.show_next_active_test(),
230 Event.Type.RESTART_TESTS:
231 lambda event: self.restart_tests(root=test_or_root(event)),
232 Event.Type.AUTO_RUN:
233 lambda event: self.auto_run(root=test_or_root(event)),
234 Event.Type.RE_RUN_FAILED:
235 lambda event: self.re_run_failed(root=test_or_root(event)),
236 Event.Type.RUN_TESTS_WITH_STATUS:
237 lambda event: self.run_tests_with_status(
238 event.status,
239 root=test_or_root(event)),
240 Event.Type.REVIEW:
241 lambda event: self.show_review_information(),
242 Event.Type.UPDATE_SYSTEM_INFO:
243 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800244 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800245 lambda event: self.stop(root=test_or_root(event, False),
246 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800247 Event.Type.SET_VISIBLE_TEST:
248 lambda event: self.set_visible_test(
249 self.test_list.lookup_path(event.path)),
Jon Salz4712ac72013-02-07 17:12:05 +0800250 Event.Type.CLEAR_STATE:
251 lambda event: self.clear_state(self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800252 }
253
254 self.exceptions = []
255 self.web_socket_manager = None
256
257 def destroy(self):
258 if self.chrome:
259 self.chrome.kill()
260 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800261 if self.dummy_shopfloor:
262 self.dummy_shopfloor.kill()
263 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800264 if self.ui_process:
265 utils.kill_process_tree(self.ui_process, 'ui')
266 self.ui_process = None
267 if self.web_socket_manager:
268 logging.info('Stopping web sockets')
269 self.web_socket_manager.close()
270 self.web_socket_manager = None
271 if self.state_server_thread:
272 logging.info('Stopping state server')
273 self.state_server.shutdown()
274 self.state_server_thread.join()
275 self.state_server.server_close()
276 self.state_server_thread = None
277 if self.state_instance:
278 self.state_instance.close()
279 if self.event_server_thread:
280 logging.info('Stopping event server')
281 self.event_server.shutdown() # pylint: disable=E1101
282 self.event_server_thread.join()
283 self.event_server.server_close()
284 self.event_server_thread = None
285 if self.log_watcher:
286 if self.log_watcher.IsThreadStarted():
287 self.log_watcher.StopWatchThread()
288 self.log_watcher = None
289 if self.prespawner:
290 logging.info('Stopping prespawner')
291 self.prespawner.stop()
292 self.prespawner = None
293 if self.event_client:
294 logging.info('Closing event client')
295 self.event_client.close()
296 self.event_client = None
297 if self.event_log:
298 self.event_log.Close()
299 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800300 if self.key_filter:
301 self.key_filter.Stop()
302
Jon Salz0697cbf2012-07-04 15:14:04 +0800303 self.check_exceptions()
304 logging.info('Done destroying Goofy')
305
306 def start_state_server(self):
307 self.state_instance, self.state_server = (
308 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800309 self.goofy_rpc = GoofyRPC(self)
310 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800311 logging.info('Starting state server')
312 self.state_server_thread = threading.Thread(
313 target=self.state_server.serve_forever,
314 name='StateServer')
315 self.state_server_thread.start()
316
317 def start_event_server(self):
318 self.event_server = EventServer()
319 logging.info('Starting factory event server')
320 self.event_server_thread = threading.Thread(
321 target=self.event_server.serve_forever,
322 name='EventServer') # pylint: disable=E1101
323 self.event_server_thread.start()
324
325 self.event_client = EventClient(
326 callback=self.handle_event, event_loop=self.run_queue)
327
328 self.web_socket_manager = WebSocketManager(self.uuid)
329 self.state_server.add_handler("/event",
330 self.web_socket_manager.handle_web_socket)
331
332 def start_ui(self):
333 ui_proc_args = [
334 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
335 self.options.test_list]
336 if self.options.verbose:
337 ui_proc_args.append('-v')
338 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800339 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800340 logging.info('Waiting for UI to come up...')
341 self.event_client.wait(
342 lambda event: event.type == Event.Type.UI_READY)
343 logging.info('UI has started')
344
345 def set_visible_test(self, test):
346 if self.visible_test == test:
347 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800348 if test and not test.has_ui:
349 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800350
351 if test:
352 test.update_state(visible=True)
353 if self.visible_test:
354 self.visible_test.update_state(visible=False)
355 self.visible_test = test
356
Jon Salzd4306c82012-11-30 15:16:36 +0800357 def _log_startup_messages(self):
358 '''Logs the tail of var/log/messages and mosys and EC console logs.'''
359 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
360 # for factory-3004.B only. Consolidate and merge back to ToT.
361 if utils.in_chroot():
362 return
363
364 try:
365 var_log_messages = (
366 utils.var_log_messages_before_reboot())
367 logging.info(
368 'Tail of /var/log/messages before last reboot:\n'
369 '%s', ('\n'.join(
370 ' ' + x for x in var_log_messages)))
371 except: # pylint: disable=W0702
372 logging.exception('Unable to grok /var/log/messages')
373
374 try:
375 mosys_log = utils.Spawn(
376 ['mosys', 'eventlog', 'list'],
377 read_stdout=True, log_stderr_on_error=True).stdout_data
378 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
379 except: # pylint: disable=W0702
380 logging.exception('Unable to read mosys eventlog')
381
382 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800383 board = system.GetBoard()
384 ec_console_log = board.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800385 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
386 except: # pylint: disable=W0702
387 logging.exception('Error retrieving EC console log')
388
Jon Salz0697cbf2012-07-04 15:14:04 +0800389 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800390 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800391 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800392
Jon Salz0697cbf2012-07-04 15:14:04 +0800393 @param test: The ShutdownStep.
394 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800395 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800396 test_state = test.update_state(increment_shutdown_count=1)
397 logging.info('Detected shutdown (%d of %d)',
398 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800399
Jon Salz0697cbf2012-07-04 15:14:04 +0800400 def log_and_update_state(status, error_msg, **kw):
401 self.event_log.Log('rebooted',
402 status=status, error_msg=error_msg, **kw)
Jon Salzd4306c82012-11-30 15:16:36 +0800403 logging.info('Rebooted: status=%s, %s', status,
404 (('error_msg=%s' % error_msg) if error_msg else None))
Jon Salz0697cbf2012-07-04 15:14:04 +0800405 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800406
Jon Salz0697cbf2012-07-04 15:14:04 +0800407 if not self.last_shutdown_time:
408 log_and_update_state(status=TestState.FAILED,
409 error_msg='Unable to read shutdown_time')
410 return
Jon Salz258a40c2012-04-19 12:34:01 +0800411
Jon Salz0697cbf2012-07-04 15:14:04 +0800412 now = time.time()
413 logging.info('%.03f s passed since reboot',
414 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800415
Jon Salz0697cbf2012-07-04 15:14:04 +0800416 if self.last_shutdown_time > now:
417 test.update_state(status=TestState.FAILED,
418 error_msg='Time moved backward during reboot')
419 elif (isinstance(test, factory.RebootStep) and
420 self.test_list.options.max_reboot_time_secs and
421 (now - self.last_shutdown_time >
422 self.test_list.options.max_reboot_time_secs)):
423 # A reboot took too long; fail. (We don't check this for
424 # HaltSteps, because the machine could be halted for a
425 # very long time, and even unplugged with battery backup,
426 # thus hosing the clock.)
427 log_and_update_state(
428 status=TestState.FAILED,
429 error_msg=('More than %d s elapsed during reboot '
430 '(%.03f s, from %s to %s)' % (
431 self.test_list.options.max_reboot_time_secs,
432 now - self.last_shutdown_time,
433 utils.TimeString(self.last_shutdown_time),
434 utils.TimeString(now))),
435 duration=(now-self.last_shutdown_time))
Jon Salzd4306c82012-11-30 15:16:36 +0800436 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800437 elif test_state.shutdown_count == test.iterations:
438 # Good!
439 log_and_update_state(status=TestState.PASSED,
440 duration=(now - self.last_shutdown_time),
441 error_msg='')
442 elif test_state.shutdown_count > test.iterations:
443 # Shut down too many times
444 log_and_update_state(status=TestState.FAILED,
445 error_msg='Too many shutdowns')
Jon Salzd4306c82012-11-30 15:16:36 +0800446 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800447 elif utils.are_shift_keys_depressed():
448 logging.info('Shift keys are depressed; cancelling restarts')
449 # Abort shutdown
450 log_and_update_state(
451 status=TestState.FAILED,
452 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800453 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800454 else:
455 def handler():
456 if self._prompt_cancel_shutdown(
457 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800458 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800459 log_and_update_state(
460 status=TestState.FAILED,
461 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800462 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800463 return
Jon Salz0405ab52012-03-16 15:26:52 +0800464
Jon Salz0697cbf2012-07-04 15:14:04 +0800465 # Time to shutdown again
466 log_and_update_state(
467 status=TestState.ACTIVE,
468 error_msg='',
469 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800470
Jon Salz0697cbf2012-07-04 15:14:04 +0800471 self.event_log.Log('shutdown', operation='reboot')
472 self.state_instance.set_shared_data('shutdown_time',
473 time.time())
474 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800475
Jon Salz0697cbf2012-07-04 15:14:04 +0800476 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800477
Jon Salz0697cbf2012-07-04 15:14:04 +0800478 def _prompt_cancel_shutdown(self, test, iteration):
479 if self.options.ui != 'chrome':
480 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800481
Jon Salz0697cbf2012-07-04 15:14:04 +0800482 pending_shutdown_data = {
483 'delay_secs': test.delay_secs,
484 'time': time.time() + test.delay_secs,
485 'operation': test.operation,
486 'iteration': iteration,
487 'iterations': test.iterations,
488 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800489
Jon Salz0697cbf2012-07-04 15:14:04 +0800490 # Create a new (threaded) event client since we
491 # don't want to use the event loop for this.
492 with EventClient() as event_client:
493 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
494 **pending_shutdown_data))
495 aborted = event_client.wait(
496 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
497 timeout=test.delay_secs) is not None
498 if aborted:
499 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
500 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800501
Jon Salz0697cbf2012-07-04 15:14:04 +0800502 def init_states(self):
503 '''
504 Initializes all states on startup.
505 '''
506 for test in self.test_list.get_all_tests():
507 # Make sure the state server knows about all the tests,
508 # defaulting to an untested state.
509 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800510
Jon Salz0697cbf2012-07-04 15:14:04 +0800511 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800512 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800513 ec_console_log = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800514
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 # Any 'active' tests should be marked as failed now.
516 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800517 if not test.is_leaf():
518 # Don't bother with parents; they will be updated when their
519 # children are updated.
520 continue
521
Jon Salz0697cbf2012-07-04 15:14:04 +0800522 test_state = test.get_state()
523 if test_state.status != TestState.ACTIVE:
524 continue
525 if isinstance(test, factory.ShutdownStep):
526 # Shutdown while the test was active - that's good.
527 self.handle_shutdown_complete(test, test_state)
528 else:
529 # Unexpected shutdown. Grab /var/log/messages for context.
530 if var_log_messages is None:
531 try:
532 var_log_messages = (
533 utils.var_log_messages_before_reboot())
534 # Write it to the log, to make it easier to
535 # correlate with /var/log/messages.
536 logging.info(
537 'Unexpected shutdown. '
538 'Tail of /var/log/messages before last reboot:\n'
539 '%s', ('\n'.join(
540 ' ' + x for x in var_log_messages)))
541 except: # pylint: disable=W0702
542 logging.exception('Unable to grok /var/log/messages')
543 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800544
Jon Salz008f4ea2012-08-28 05:39:45 +0800545 if mosys_log is None and not utils.in_chroot():
546 try:
547 mosys_log = utils.Spawn(
548 ['mosys', 'eventlog', 'list'],
549 read_stdout=True, log_stderr_on_error=True).stdout_data
550 # Write it to the log also.
551 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
552 except: # pylint: disable=W0702
553 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800554
Vic Yange4c275d2012-08-28 01:50:20 +0800555 if ec_console_log is None:
556 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800557 board = system.GetBoard()
558 ec_console_log = board.GetECConsoleLog()
Vic Yange4c275d2012-08-28 01:50:20 +0800559 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Jon Salzfe1f6652012-09-07 05:40:14 +0800560 except: # pylint: disable=W0702
Vic Yange4c275d2012-08-28 01:50:20 +0800561 logging.exception('Error retrieving EC console log')
562
Jon Salz0697cbf2012-07-04 15:14:04 +0800563 error_msg = 'Unexpected shutdown while test was running'
564 self.event_log.Log('end_test',
565 path=test.path,
566 status=TestState.FAILED,
567 invocation=test.get_state().invocation,
568 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800569 var_log_messages='\n'.join(var_log_messages),
570 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800571 test.update_state(
572 status=TestState.FAILED,
573 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800574
Jon Salz50efe942012-07-26 11:54:10 +0800575 if not test.never_fails:
576 # For "never_fails" tests (such as "Start"), don't cancel
577 # pending tests, since reboot is expected.
578 factory.console.info('Unexpected shutdown while test %s '
579 'running; cancelling any pending tests',
580 test.path)
581 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800582
Jon Salz008f4ea2012-08-28 05:39:45 +0800583 self.update_skipped_tests()
584
585 def update_skipped_tests(self):
586 '''
587 Updates skipped states based on run_if.
588 '''
589 for t in self.test_list.walk():
590 if t.is_leaf() and t.run_if_table_name:
591 skip = False
592 try:
593 aux = shopfloor.get_selected_aux_data(t.run_if_table_name)
594 value = aux.get(t.run_if_col)
595 if value is not None:
596 skip = (not value) ^ t.run_if_not
597 except ValueError:
598 # Not available; assume it shouldn't be skipped
599 pass
600
601 test_state = t.get_state()
602 if ((not skip) and
603 (test_state.status == TestState.PASSED) and
604 (test_state.error_msg == TestState.SKIPPED_MSG)):
605 # It was marked as skipped before, but now we need to run it.
606 # Mark as untested.
607 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
608 else:
609 t.update_state(skip=skip)
610
Jon Salz0697cbf2012-07-04 15:14:04 +0800611 def show_next_active_test(self):
612 '''
613 Rotates to the next visible active test.
614 '''
615 self.reap_completed_tests()
616 active_tests = [
617 t for t in self.test_list.walk()
618 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
619 if not active_tests:
620 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800621
Jon Salz0697cbf2012-07-04 15:14:04 +0800622 try:
623 next_test = active_tests[
624 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
625 except ValueError: # visible_test not present in active_tests
626 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800627
Jon Salz0697cbf2012-07-04 15:14:04 +0800628 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800629
Jon Salz0697cbf2012-07-04 15:14:04 +0800630 def handle_event(self, event):
631 '''
632 Handles an event from the event server.
633 '''
634 handler = self.event_handlers.get(event.type)
635 if handler:
636 handler(event)
637 else:
638 # We don't register handlers for all event types - just ignore
639 # this event.
640 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800641
Jon Salz0697cbf2012-07-04 15:14:04 +0800642 def run_next_test(self):
643 '''
644 Runs the next eligible test (or tests) in self.tests_to_run.
645 '''
646 self.reap_completed_tests()
647 while self.tests_to_run:
648 logging.debug('Tests to run: %s',
649 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800650
Jon Salz0697cbf2012-07-04 15:14:04 +0800651 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800652
Jon Salz0697cbf2012-07-04 15:14:04 +0800653 if test in self.invocations:
654 logging.info('Next test %s is already running', test.path)
655 self.tests_to_run.popleft()
656 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800657
Jon Salza1412922012-07-23 16:04:17 +0800658 for requirement in test.require_run:
659 for i in requirement.test.walk():
660 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800661 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800662 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800663 return
664
Jon Salz0697cbf2012-07-04 15:14:04 +0800665 if self.invocations and not (test.backgroundable and all(
666 [x.backgroundable for x in self.invocations])):
667 logging.debug('Waiting for non-backgroundable tests to '
668 'complete before running %s', test.path)
669 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800670
Jon Salz3e6f5202012-10-15 15:08:29 +0800671 if test.get_state().skip:
672 factory.console.info('Skipping test %s', test.path)
673 test.update_state(status=TestState.PASSED,
674 error_msg=TestState.SKIPPED_MSG)
675 self.tests_to_run.popleft()
676 continue
677
Jon Salz0697cbf2012-07-04 15:14:04 +0800678 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800679
Jon Salz304a75d2012-07-06 11:14:15 +0800680 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800681 for requirement in test.require_run:
682 for i in requirement.test.walk():
683 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800684 # We've hit this test itself; stop checking
685 break
Jon Salza1412922012-07-23 16:04:17 +0800686 if ((i.get_state().status == TestState.UNTESTED) or
687 (requirement.passed and i.get_state().status !=
688 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800689 # Found an untested test; move on to the next
690 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800691 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800692 break
693
694 if untested:
695 untested_paths = ', '.join(sorted([x.path for x in untested]))
696 if self.state_instance.get_shared_data('engineering_mode',
697 optional=True):
698 # In engineering mode, we'll let it go.
699 factory.console.warn('In engineering mode; running '
700 '%s even though required tests '
701 '[%s] have not completed',
702 test.path, untested_paths)
703 else:
704 # Not in engineering mode; mark it failed.
705 error_msg = ('Required tests [%s] have not been run yet'
706 % untested_paths)
707 factory.console.error('Not running %s: %s',
708 test.path, error_msg)
709 test.update_state(status=TestState.FAILED,
710 error_msg=error_msg)
711 continue
712
Jon Salz0697cbf2012-07-04 15:14:04 +0800713 if isinstance(test, factory.ShutdownStep):
714 if os.path.exists(NO_REBOOT_FILE):
715 test.update_state(
716 status=TestState.FAILED, increment_count=1,
717 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800718 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800719 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800720
Jon Salz0697cbf2012-07-04 15:14:04 +0800721 test.update_state(status=TestState.ACTIVE, increment_count=1,
722 error_msg='', shutdown_count=0)
723 if self._prompt_cancel_shutdown(test, 1):
724 self.event_log.Log('reboot_cancelled')
725 test.update_state(
726 status=TestState.FAILED, increment_count=1,
727 error_msg='Shutdown aborted by operator',
728 shutdown_count=0)
chungyiafe8f772012-08-15 19:36:29 +0800729 continue
Jon Salz2f757d42012-06-27 17:06:42 +0800730
Jon Salz0697cbf2012-07-04 15:14:04 +0800731 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800732 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800733 'tests_after_shutdown',
734 [t.path for t in self.tests_to_run])
735 # Save shutdown time
736 self.state_instance.set_shared_data('shutdown_time',
737 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800738
Jon Salz0697cbf2012-07-04 15:14:04 +0800739 with self.env.lock:
740 self.event_log.Log('shutdown', operation=test.operation)
741 shutdown_result = self.env.shutdown(test.operation)
742 if shutdown_result:
743 # That's all, folks!
744 self.run_queue.put(None)
745 return
746 else:
747 # Just pass (e.g., in the chroot).
748 test.update_state(status=TestState.PASSED)
749 self.state_instance.set_shared_data(
750 'tests_after_shutdown', None)
751 # Send event with no fields to indicate that there is no
752 # longer a pending shutdown.
753 self.event_client.post_event(Event(
754 Event.Type.PENDING_SHUTDOWN))
755 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800756
Jon Salz1acc8742012-07-17 17:45:55 +0800757 self._run_test(test, test.iterations)
758
759 def _run_test(self, test, iterations_left=None):
760 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
761 new_state = test.update_state(
762 status=TestState.ACTIVE, increment_count=1, error_msg='',
Jon Salzbd42ce12012-09-18 08:03:59 +0800763 invocation=invoc.uuid, iterations_left=iterations_left,
764 visible=(self.visible_test == test))
Jon Salz1acc8742012-07-17 17:45:55 +0800765 invoc.count = new_state.count
766
767 self.invocations[test] = invoc
768 if self.visible_test is None and test.has_ui:
769 self.set_visible_test(test)
Vic Yang311ddb82012-09-26 12:08:28 +0800770 self.check_exclusive()
Jon Salz1acc8742012-07-17 17:45:55 +0800771 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800772
Vic Yang311ddb82012-09-26 12:08:28 +0800773 def check_exclusive(self):
774 current_exclusive_items = set([
775 item
776 for item in factory.FactoryTest.EXCLUSIVE_OPTIONS
777 if any([test.is_exclusive(item) for test in self.invocations])])
778
779 new_exclusive_items = current_exclusive_items - self.exclusive_items
780 if factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING in new_exclusive_items:
781 logging.info('Disabling network')
782 self.connection_manager.DisableNetworking()
783 if factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in new_exclusive_items:
784 logging.info('Stop controlling charger')
785
786 new_non_exclusive_items = self.exclusive_items - current_exclusive_items
787 if (factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING in
788 new_non_exclusive_items):
789 logging.info('Re-enabling network')
790 self.connection_manager.EnableNetworking()
791 if factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in new_non_exclusive_items:
792 logging.info('Start controlling charger')
793
794 # Only adjust charge state if not excluded
795 if (self.charge_manager and
796 not factory.FactoryTest.EXCLUSIVE_OPTIONS.CHARGER in
797 current_exclusive_items):
798 self.charge_manager.AdjustChargeState()
799
800 self.exclusive_items = current_exclusive_items
Jon Salz5da61e62012-05-31 13:06:22 +0800801
cychiang21886742012-07-05 15:16:32 +0800802 def check_for_updates(self):
803 '''
804 Schedules an asynchronous check for updates if necessary.
805 '''
806 if not self.test_list.options.update_period_secs:
807 # Not enabled.
808 return
809
810 now = time.time()
811 if self.last_update_check and (
812 now - self.last_update_check <
813 self.test_list.options.update_period_secs):
814 # Not yet time for another check.
815 return
816
817 self.last_update_check = now
818
819 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
820 if reached_shopfloor:
821 new_update_md5sum = md5sum if needs_update else None
822 if system.SystemInfo.update_md5sum != new_update_md5sum:
823 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
824 system.SystemInfo.update_md5sum = new_update_md5sum
825 self.run_queue.put(self.update_system_info)
826
827 updater.CheckForUpdateAsync(
828 handle_check_for_update,
829 self.test_list.options.shopfloor_timeout_secs)
830
Jon Salza6711d72012-07-18 14:33:03 +0800831 def cancel_pending_tests(self):
832 '''Cancels any tests in the run queue.'''
833 self.run_tests([])
834
Jon Salz0697cbf2012-07-04 15:14:04 +0800835 def run_tests(self, subtrees, untested_only=False):
836 '''
837 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800838
Jon Salz0697cbf2012-07-04 15:14:04 +0800839 The tests are run in order unless one fails (then stops).
840 Backgroundable tests are run simultaneously; when a foreground test is
841 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800842
Jon Salz0697cbf2012-07-04 15:14:04 +0800843 @param subtrees: Node or nodes containing tests to run (may either be
844 a single test or a list). Duplicates will be ignored.
845 '''
846 if type(subtrees) != list:
847 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800848
Jon Salz0697cbf2012-07-04 15:14:04 +0800849 # Nodes we've seen so far, to avoid duplicates.
850 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800851
Jon Salz0697cbf2012-07-04 15:14:04 +0800852 self.tests_to_run = deque()
853 for subtree in subtrees:
854 for test in subtree.walk():
855 if test in seen:
856 continue
857 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800858
Jon Salz0697cbf2012-07-04 15:14:04 +0800859 if not test.is_leaf():
860 continue
861 if (untested_only and
862 test.get_state().status != TestState.UNTESTED):
863 continue
864 self.tests_to_run.append(test)
865 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800866
Jon Salz0697cbf2012-07-04 15:14:04 +0800867 def reap_completed_tests(self):
868 '''
869 Removes completed tests from the set of active tests.
870
871 Also updates the visible test if it was reaped.
872 '''
873 for t, v in dict(self.invocations).iteritems():
874 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800875 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800876 del self.invocations[t]
877
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800878 # Stop on failure if flag is true.
879 if (self.test_list.options.stop_on_failure and
880 new_state.status == TestState.FAILED):
881 # Clean all the tests to cause goofy to stop.
882 self.tests_to_run = []
883 factory.console.info("Stop on failure triggered. Empty the queue.")
884
Jon Salz1acc8742012-07-17 17:45:55 +0800885 if new_state.iterations_left and new_state.status == TestState.PASSED:
886 # Play it again, Sam!
887 self._run_test(t)
888
Jon Salz0697cbf2012-07-04 15:14:04 +0800889 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800890 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800891 self.set_visible_test(None)
892 # Make the first running test, if any, the visible test
893 for t in self.test_list.walk():
894 if t in self.invocations:
895 self.set_visible_test(t)
896 break
897
Jon Salz85a39882012-07-05 16:45:04 +0800898 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800899 '''
900 Kills and waits for all active tests.
901
Jon Salz85a39882012-07-05 16:45:04 +0800902 Args:
903 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800904 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800905 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800906 '''
907 self.reap_completed_tests()
908 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800909 if root and not test.has_ancestor(root):
910 continue
911
Jon Salz0697cbf2012-07-04 15:14:04 +0800912 factory.console.info('Killing active test %s...' % test.path)
913 invoc.abort_and_join()
914 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800915 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800916 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800917
Jon Salz0697cbf2012-07-04 15:14:04 +0800918 if not abort:
919 test.update_state(status=TestState.UNTESTED)
920 self.reap_completed_tests()
921
Jon Salz85a39882012-07-05 16:45:04 +0800922 def stop(self, root=None, fail=False):
923 self.kill_active_tests(fail, root)
924 # Remove any tests in the run queue under the root.
925 self.tests_to_run = deque([x for x in self.tests_to_run
926 if root and not x.has_ancestor(root)])
927 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800928
Jon Salz4712ac72013-02-07 17:12:05 +0800929 def clear_state(self, root=None):
930 self.stop(root)
931 for f in root.walk():
932 if f.is_leaf():
933 f.update_state(status=TestState.UNTESTED)
934
Jon Salz0697cbf2012-07-04 15:14:04 +0800935 def abort_active_tests(self):
936 self.kill_active_tests(True)
937
938 def main(self):
939 try:
940 self.init()
941 self.event_log.Log('goofy_init',
942 success=True)
943 except:
944 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800945 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800946 self.event_log.Log('goofy_init',
947 success=False,
948 trace=traceback.format_exc())
949 except: # pylint: disable=W0702
950 pass
951 raise
952
953 self.run()
954
955 def update_system_info(self):
956 '''Updates system info.'''
957 system_info = system.SystemInfo()
958 self.state_instance.set_shared_data('system_info', system_info.__dict__)
959 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
960 system_info=system_info.__dict__))
961 logging.info('System info: %r', system_info.__dict__)
962
Jon Salzeb42f0d2012-07-27 19:14:04 +0800963 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
964 '''Commences updating factory software.
965
966 Args:
967 auto_run_on_restart: Auto-run when the machine comes back up.
968 post_update_hook: Code to call after update but immediately before
969 restart.
970
971 Returns:
972 Never if the update was successful (we just reboot).
973 False if the update was unnecessary (no update available).
974 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800975 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800976 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800977
Jon Salz5c344f62012-07-13 14:31:16 +0800978 def pre_update_hook():
979 if auto_run_on_restart:
980 self.state_instance.set_shared_data('tests_after_shutdown',
981 FORCE_AUTO_RUN)
982 self.state_instance.close()
983
Jon Salzeb42f0d2012-07-27 19:14:04 +0800984 if updater.TryUpdate(pre_update_hook=pre_update_hook):
985 if post_update_hook:
986 post_update_hook()
987 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800988
Jon Salzcef132a2012-08-30 04:58:08 +0800989 def handle_sigint(self, dummy_signum, dummy_frame):
Jon Salz77c151e2012-08-28 07:20:37 +0800990 logging.error('Received SIGINT')
991 self.run_queue.put(None)
992 raise KeyboardInterrupt()
993
Jon Salz0697cbf2012-07-04 15:14:04 +0800994 def init(self, args=None, env=None):
995 '''Initializes Goofy.
996
997 Args:
998 args: A list of command-line arguments. Uses sys.argv if
999 args is None.
1000 env: An Environment instance to use (or None to choose
1001 FakeChrootEnvironment or DUTEnvironment as appropriate).
1002 '''
Jon Salz77c151e2012-08-28 07:20:37 +08001003 signal.signal(signal.SIGINT, self.handle_sigint)
1004
Jon Salz0697cbf2012-07-04 15:14:04 +08001005 parser = OptionParser()
1006 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001007 action='store_true',
1008 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001009 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001010 metavar='FILE',
1011 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001012 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001013 action='store_true',
1014 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001015 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +08001016 choices=['none', 'gtk', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001017 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001018 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001019 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001020 type='int', default=1,
1021 help=('Factor by which to scale UI '
1022 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001023 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001024 metavar='FILE',
1025 help='Use FILE as test list')
Jon Salzc79a9982012-08-30 04:42:01 +08001026 parser.add_option('--dummy_shopfloor', action='store_true',
1027 help='Use a dummy shopfloor server')
chungyiafe8f772012-08-15 19:36:29 +08001028 parser.add_option('--automation', dest='automation',
1029 action='store_true',
1030 help='Enable automation on running factory test')
Ricky Liang09216dc2013-02-22 17:26:45 +08001031 parser.add_option('--one_pixel_less', dest='one_pixel_less',
1032 action='store_true',
1033 help=('Start Chrome one pixel less than the full screen.'
1034 'Needed by Exynos platform to run GTK.'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001035 (self.options, self.args) = parser.parse_args(args)
1036
Jon Salz46b89562012-07-05 11:49:22 +08001037 # Make sure factory directories exist.
1038 factory.get_log_root()
1039 factory.get_state_root()
1040 factory.get_test_data_root()
1041
Jon Salz0697cbf2012-07-04 15:14:04 +08001042 global _inited_logging # pylint: disable=W0603
1043 if not _inited_logging:
1044 factory.init_logging('goofy', verbose=self.options.verbose)
1045 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001046
Jon Salz0f996602012-10-03 15:26:48 +08001047 if self.options.print_test_list:
1048 print factory.read_test_list(
1049 self.options.print_test_list).__repr__(recursive=True)
1050 sys.exit(0)
1051
Jon Salzee85d522012-07-17 14:34:46 +08001052 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +08001053 self.event_log = EventLog('goofy')
1054
1055 if (not suppress_chroot_warning and
1056 factory.in_chroot() and
1057 self.options.ui == 'gtk' and
1058 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
1059 # That's not going to work! Tell the user how to run
1060 # this way.
1061 logging.warn(GOOFY_IN_CHROOT_WARNING)
1062 time.sleep(1)
1063
1064 if env:
1065 self.env = env
1066 elif factory.in_chroot():
1067 self.env = test_environment.FakeChrootEnvironment()
1068 logging.warn(
1069 'Using chroot environment: will not actually run autotests')
1070 else:
1071 self.env = test_environment.DUTEnvironment()
1072 self.env.goofy = self
1073
1074 if self.options.restart:
1075 state.clear_state()
1076
Jon Salz0697cbf2012-07-04 15:14:04 +08001077 if self.options.ui_scale_factor != 1 and utils.in_qemu():
1078 logging.warn(
1079 'In QEMU; ignoring ui_scale_factor argument')
1080 self.options.ui_scale_factor = 1
1081
1082 logging.info('Started')
1083
1084 self.start_state_server()
1085 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1086 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001087 self.options.ui_scale_factor)
1088 self.state_instance.set_shared_data('one_pixel_less',
1089 self.options.one_pixel_less)
Jon Salz0697cbf2012-07-04 15:14:04 +08001090 self.last_shutdown_time = (
1091 self.state_instance.get_shared_data('shutdown_time', optional=True))
1092 self.state_instance.del_shared_data('shutdown_time', optional=True)
1093
Jon Salzb19ea072013-02-07 16:35:00 +08001094 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001095 if not self.options.test_list:
1096 self.options.test_list = find_test_list()
Jon Salzb19ea072013-02-07 16:35:00 +08001097 if self.options.test_list:
Jon Salz0697cbf2012-07-04 15:14:04 +08001098 logging.info('Using test list %s', self.options.test_list)
Jon Salzb19ea072013-02-07 16:35:00 +08001099 try:
1100 self.test_list = factory.read_test_list(
1101 self.options.test_list,
1102 self.state_instance)
1103 except: # pylint: disable=W0702
1104 logging.exception('Unable to read test list %r', self.options.test_list)
1105 self.state_instance.set_shared_data('startup_error',
1106 'Unable to read test list %s\n%s' % (
1107 self.options.test_list,
1108 traceback.format_exc()))
1109 else:
1110 logging.error('No test list found.')
1111 self.state_instance.set_shared_data('startup_error',
1112 'No test list found.')
Jon Salz0697cbf2012-07-04 15:14:04 +08001113
Jon Salzb19ea072013-02-07 16:35:00 +08001114 if not self.test_list:
1115 if self.options.ui == 'chrome':
1116 # Create an empty test list with default options so that the rest of
1117 # startup can proceed.
1118 self.test_list = factory.FactoryTestList(
1119 [], self.state_instance, factory.Options())
1120 else:
1121 # Bail with an error; no point in starting up.
1122 sys.exit('No valid test list; exiting.')
1123
Jon Salz0697cbf2012-07-04 15:14:04 +08001124 if not self.state_instance.has_shared_data('ui_lang'):
1125 self.state_instance.set_shared_data('ui_lang',
1126 self.test_list.options.ui_lang)
1127 self.state_instance.set_shared_data(
1128 'test_list_options',
1129 self.test_list.options.__dict__)
1130 self.state_instance.test_list = self.test_list
1131
Jon Salz83ef34b2012-11-01 19:46:35 +08001132 if not utils.in_chroot() and self.test_list.options.disable_log_rotation:
1133 open('/var/lib/cleanup_logs_paused', 'w').close()
1134
Jon Salz23926422012-09-01 03:38:13 +08001135 if self.options.dummy_shopfloor:
1136 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1137 'http://localhost:%d/' % shopfloor.DEFAULT_SERVER_PORT)
1138 self.dummy_shopfloor = Spawn(
1139 [os.path.join(factory.FACTORY_PATH, 'bin', 'shopfloor_server'),
1140 '--dummy'])
1141 elif self.test_list.options.shopfloor_server_url:
1142 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
1143
Jon Salz0f996602012-10-03 15:26:48 +08001144 if self.test_list.options.time_sanitizer and not utils.in_chroot():
Jon Salz8fa8e832012-07-13 19:04:09 +08001145 self.time_sanitizer = time_sanitizer.TimeSanitizer(
1146 base_time=time_sanitizer.GetBaseTimeFromFile(
1147 # lsb-factory is written by the factory install shim during
1148 # installation, so it should have a good time obtained from
Jon Salz54882d02012-08-31 01:57:54 +08001149 # the mini-Omaha server. If it's not available, we'll use
1150 # /etc/lsb-factory (which will be much older, but reasonably
1151 # sane) and rely on a shopfloor sync to set a more accurate
1152 # time.
1153 '/usr/local/etc/lsb-factory',
1154 '/etc/lsb-release'))
Jon Salz8fa8e832012-07-13 19:04:09 +08001155 self.time_sanitizer.RunOnce()
1156
Jon Salz0697cbf2012-07-04 15:14:04 +08001157 self.init_states()
1158 self.start_event_server()
1159 self.connection_manager = self.env.create_connection_manager(
Tai-Hsu Lin371351a2012-08-27 14:17:14 +08001160 self.test_list.options.wlans,
1161 self.test_list.options.scan_wifi_period_secs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001162 # Note that we create a log watcher even if
1163 # sync_event_log_period_secs isn't set (no background
1164 # syncing), since we may use it to flush event logs as well.
1165 self.log_watcher = EventLogWatcher(
1166 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +08001167 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001168 if self.test_list.options.sync_event_log_period_secs:
1169 self.log_watcher.StartWatchThread()
1170
1171 self.update_system_info()
1172
Vic Yang4953fc12012-07-26 16:19:53 +08001173 assert ((self.test_list.options.min_charge_pct is None) ==
1174 (self.test_list.options.max_charge_pct is None))
Jon Salzad7353b2012-10-15 16:22:46 +08001175 if self.test_list.options.min_charge_pct is not None:
Vic Yang4953fc12012-07-26 16:19:53 +08001176 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1177 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001178 system.SystemStatus.charge_manager = self.charge_manager
Vic Yang4953fc12012-07-26 16:19:53 +08001179
Jon Salz0697cbf2012-07-04 15:14:04 +08001180 os.environ['CROS_FACTORY'] = '1'
1181 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1182
1183 # Set CROS_UI since some behaviors in ui.py depend on the
1184 # particular UI in use. TODO(jsalz): Remove this (and all
1185 # places it is used) when the GTK UI is removed.
1186 os.environ['CROS_UI'] = self.options.ui
1187
1188 if self.options.ui == 'chrome':
1189 self.env.launch_chrome()
1190 logging.info('Waiting for a web socket connection')
Dean Liao44392962013-03-07 17:10:08 +08001191 try:
1192 self.web_socket_manager.wait(timeout_sec=(
1193 None if factory.in_chroot() else WAIT_FOR_SOCKET_TIMEOUT_SECS))
1194 except WebSocketException:
1195 sys.exit('Failed to wait for a socket. Exiting Goofy.')
Jon Salz0697cbf2012-07-04 15:14:04 +08001196
1197 # Wait for the test widget size to be set; this is done in
1198 # an asynchronous RPC so there is a small chance that the
1199 # web socket might be opened first.
1200 for _ in range(100): # 10 s
1201 try:
1202 if self.state_instance.get_shared_data('test_widget_size'):
1203 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001204 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001205 pass # Retry
1206 time.sleep(0.1) # 100 ms
1207 else:
1208 logging.warn('Never received test_widget_size from UI')
1209 elif self.options.ui == 'gtk':
1210 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001211
Ricky Liang650f6bf2012-09-28 13:22:54 +08001212 # Create download path for autotest beforehand or autotests run at
1213 # the same time might fail due to race condition.
1214 if not factory.in_chroot():
1215 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1216 'download'))
1217
Jon Salz0697cbf2012-07-04 15:14:04 +08001218 def state_change_callback(test, test_state):
1219 self.event_client.post_event(
1220 Event(Event.Type.STATE_CHANGE,
1221 path=test.path, state=test_state))
1222 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001223
Jon Salza6711d72012-07-18 14:33:03 +08001224 for handler in self.on_ui_startup:
1225 handler()
1226
1227 self.prespawner = Prespawner()
1228 self.prespawner.start()
1229
Jon Salz0697cbf2012-07-04 15:14:04 +08001230 try:
1231 tests_after_shutdown = self.state_instance.get_shared_data(
1232 'tests_after_shutdown')
1233 except KeyError:
1234 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001235
Jon Salz5c344f62012-07-13 14:31:16 +08001236 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1237 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001238 logging.info('Resuming tests after shutdown: %s',
1239 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001240 self.tests_to_run.extend(
1241 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1242 self.run_queue.put(self.run_next_test)
1243 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001244 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001245 self.run_queue.put(
1246 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001247 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001248
Dean Liao592e4d52013-01-10 20:06:39 +08001249 self.may_disable_cros_shortcut_keys()
1250
1251 def may_disable_cros_shortcut_keys(self):
1252 test_options = self.test_list.options
1253 if test_options.disable_cros_shortcut_keys:
1254 logging.info('Filter ChromeOS shortcut keys.')
1255 self.key_filter = KeyFilter(
1256 unmap_caps_lock=test_options.disable_caps_lock,
1257 caps_lock_keycode=test_options.caps_lock_keycode)
1258 self.key_filter.Start()
1259
Jon Salz0697cbf2012-07-04 15:14:04 +08001260 def run(self):
1261 '''Runs Goofy.'''
1262 # Process events forever.
1263 while self.run_once(True):
1264 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001265
Jon Salz0697cbf2012-07-04 15:14:04 +08001266 def run_once(self, block=False):
1267 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001268
Jon Salz0697cbf2012-07-04 15:14:04 +08001269 Args:
1270 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001271
Jon Salz0697cbf2012-07-04 15:14:04 +08001272 Returns:
1273 True to keep going or False to shut down.
1274 '''
1275 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001276 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001277 # Nothing on the run queue.
1278 self._run_queue_idle()
1279 if block:
1280 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001281 try:
1282 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1283 except Queue.Empty:
1284 # Keep going (calling _run_queue_idle() again at the top of
1285 # the loop)
1286 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001287 # ...and grab anything else that showed up at the same
1288 # time.
1289 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001290 else:
1291 break
Jon Salz51528e12012-07-02 18:54:45 +08001292
Jon Salz0697cbf2012-07-04 15:14:04 +08001293 for event in events:
1294 if not event:
1295 # Shutdown request.
1296 self.run_queue.task_done()
1297 return False
Jon Salz51528e12012-07-02 18:54:45 +08001298
Jon Salz0697cbf2012-07-04 15:14:04 +08001299 try:
1300 event()
Jon Salz85a39882012-07-05 16:45:04 +08001301 except: # pylint: disable=W0702
1302 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001303 self.record_exception(traceback.format_exception_only(
1304 *sys.exc_info()[:2]))
1305 # But keep going
1306 finally:
1307 self.run_queue.task_done()
1308 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001309
Jon Salz0e6532d2012-10-25 16:30:11 +08001310 def _should_sync_time(self, foreground=False):
1311 '''Returns True if we should attempt syncing time with shopfloor.
1312
1313 Args:
1314 foreground: If True, synchronizes even if background syncing
1315 is disabled (e.g., in explicit sync requests from the
1316 SyncShopfloor test).
1317 '''
1318 return ((foreground or
1319 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001320 self.time_sanitizer and
1321 (not self.time_synced) and
1322 (not factory.in_chroot()))
1323
Jon Salz0e6532d2012-10-25 16:30:11 +08001324 def sync_time_with_shopfloor_server(self, foreground=False):
Jon Salz54882d02012-08-31 01:57:54 +08001325 '''Syncs time with shopfloor server, if not yet synced.
1326
Jon Salz0e6532d2012-10-25 16:30:11 +08001327 Args:
1328 foreground: If True, synchronizes even if background syncing
1329 is disabled (e.g., in explicit sync requests from the
1330 SyncShopfloor test).
1331
Jon Salz54882d02012-08-31 01:57:54 +08001332 Returns:
1333 False if no time sanitizer is available, or True if this sync (or a
1334 previous sync) succeeded.
1335
1336 Raises:
1337 Exception if unable to contact the shopfloor server.
1338 '''
Jon Salz0e6532d2012-10-25 16:30:11 +08001339 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001340 self.time_sanitizer.SyncWithShopfloor()
1341 self.time_synced = True
1342 return self.time_synced
1343
Jon Salzb92c5112012-09-21 15:40:11 +08001344 def log_disk_space_stats(self):
1345 if not self.test_list.options.log_disk_space_period_secs:
1346 return
1347
1348 now = time.time()
1349 if (self.last_log_disk_space_time and
1350 now - self.last_log_disk_space_time <
1351 self.test_list.options.log_disk_space_period_secs):
1352 return
1353 self.last_log_disk_space_time = now
1354
1355 try:
Jon Salz3c493bb2013-02-07 17:24:58 +08001356 message = disk_space.FormatSpaceUsedAll()
1357 if message != self.last_log_disk_space_message:
1358 logging.info(message)
1359 self.last_log_disk_space_message = message
Jon Salzb92c5112012-09-21 15:40:11 +08001360 except: # pylint: disable=W0702
1361 logging.exception('Unable to get disk space used')
1362
Jon Salz8fa8e832012-07-13 19:04:09 +08001363 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001364 '''Writes out current time and tries to sync with shopfloor server.'''
1365 if not self.time_sanitizer:
1366 return
1367
1368 # Write out the current time.
1369 self.time_sanitizer.SaveTime()
1370
Jon Salz54882d02012-08-31 01:57:54 +08001371 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001372 return
1373
1374 now = time.time()
1375 if self.last_sync_time and (
1376 now - self.last_sync_time <
1377 self.test_list.options.sync_time_period_secs):
1378 # Not yet time for another check.
1379 return
1380 self.last_sync_time = now
1381
1382 def target():
1383 try:
Jon Salz54882d02012-08-31 01:57:54 +08001384 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001385 except: # pylint: disable=W0702
1386 # Oh well. Log an error (but no trace)
1387 logging.info(
1388 'Unable to get time from shopfloor server: %s',
1389 utils.FormatExceptionOnly())
1390
1391 thread = threading.Thread(target=target)
1392 thread.daemon = True
1393 thread.start()
1394
Jon Salz0697cbf2012-07-04 15:14:04 +08001395 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001396 '''Invoked when the run queue has no events.
1397
1398 This method must not raise exception.
1399 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001400 now = time.time()
1401 if (self.last_idle and
1402 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1403 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1404 # 1) seconds.
1405 return
1406
1407 self.last_idle = now
1408
Vic Yang311ddb82012-09-26 12:08:28 +08001409 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001410 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001411 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001412 self.log_disk_space_stats()
Jon Salz57717ca2012-04-04 16:47:25 +08001413
Jon Salz16d10542012-07-23 12:18:45 +08001414 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001415 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001416
Jon Salz0697cbf2012-07-04 15:14:04 +08001417 Attempts to upload the event logs to the shopfloor server.
1418 '''
1419 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1420 start_time = time.time()
Jon Salz0697cbf2012-07-04 15:14:04 +08001421 shopfloor_client = shopfloor.get_instance(
1422 detect=True,
1423 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzb10cf512012-08-09 17:29:21 +08001424 shopfloor_client.UploadEvent(log_name, Binary(chunk))
Jon Salz0697cbf2012-07-04 15:14:04 +08001425 logging.info(
1426 'Successfully synced %s in %.03f s',
1427 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001428
Jon Salz0697cbf2012-07-04 15:14:04 +08001429 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1430 root=None):
1431 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001432
Jon Salz0697cbf2012-07-04 15:14:04 +08001433 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001434
Jon Salz0697cbf2012-07-04 15:14:04 +08001435 Args:
1436 starting_at: If provided, only auto-runs tests beginning with
1437 this test.
1438 '''
1439 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001440
Jon Salz0697cbf2012-07-04 15:14:04 +08001441 if starting_at:
1442 # Make sure they passed a test, not a string.
1443 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001444
Jon Salz0697cbf2012-07-04 15:14:04 +08001445 tests_to_reset = []
1446 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001447
Jon Salz0697cbf2012-07-04 15:14:04 +08001448 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001449
Jon Salz0697cbf2012-07-04 15:14:04 +08001450 for test in root.get_top_level_tests():
1451 if starting_at:
1452 if test == starting_at:
1453 # We've found starting_at; do auto-run on all
1454 # subsequent tests.
1455 found_starting_at = True
1456 if not found_starting_at:
1457 # Don't start this guy yet
1458 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001459
Jon Salz0697cbf2012-07-04 15:14:04 +08001460 status = test.get_state().status
1461 if status == TestState.ACTIVE or status in statuses_to_run:
1462 # Reset the test (later; we will need to abort
1463 # all active tests first).
1464 tests_to_reset.append(test)
1465 if status in statuses_to_run:
1466 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001467
Jon Salz0697cbf2012-07-04 15:14:04 +08001468 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001469
Jon Salz0697cbf2012-07-04 15:14:04 +08001470 # Reset all statuses of the tests to run (in case any tests were active;
1471 # we want them to be run again).
1472 for test_to_reset in tests_to_reset:
1473 for test in test_to_reset.walk():
1474 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001475
Jon Salz0697cbf2012-07-04 15:14:04 +08001476 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001477
Jon Salz0697cbf2012-07-04 15:14:04 +08001478 def restart_tests(self, root=None):
1479 '''Restarts all tests.'''
1480 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001481
Jon Salz0697cbf2012-07-04 15:14:04 +08001482 self.abort_active_tests()
1483 for test in root.walk():
1484 test.update_state(status=TestState.UNTESTED)
1485 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001486
Jon Salz0697cbf2012-07-04 15:14:04 +08001487 def auto_run(self, starting_at=None, root=None):
1488 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001489
Jon Salz0697cbf2012-07-04 15:14:04 +08001490 Args:
1491 starting_at: If provide, only auto-runs tests beginning with
1492 this test.
1493 '''
1494 root = root or self.test_list
1495 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1496 starting_at=starting_at,
1497 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001498
Jon Salz0697cbf2012-07-04 15:14:04 +08001499 def re_run_failed(self, root=None):
1500 '''Re-runs failed tests.'''
1501 root = root or self.test_list
1502 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001503
Jon Salz0697cbf2012-07-04 15:14:04 +08001504 def show_review_information(self):
1505 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001506
Jon Salz0697cbf2012-07-04 15:14:04 +08001507 The information screene is rendered by main UI program (ui.py), so in
1508 goofy we only need to kill all active tests, set them as untested, and
1509 clear remaining tests.
1510 '''
1511 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001512 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001513
Jon Salz0697cbf2012-07-04 15:14:04 +08001514 def handle_switch_test(self, event):
1515 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001516
Jon Salz0697cbf2012-07-04 15:14:04 +08001517 @param event: The SWITCH_TEST event.
1518 '''
1519 test = self.test_list.lookup_path(event.path)
1520 if not test:
1521 logging.error('Unknown test %r', event.key)
1522 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001523
Jon Salz0697cbf2012-07-04 15:14:04 +08001524 invoc = self.invocations.get(test)
1525 if invoc and test.backgroundable:
1526 # Already running: just bring to the front if it
1527 # has a UI.
1528 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001529 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001530 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001531
Jon Salz0697cbf2012-07-04 15:14:04 +08001532 self.abort_active_tests()
1533 for t in test.walk():
1534 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001535
Jon Salz0697cbf2012-07-04 15:14:04 +08001536 if self.test_list.options.auto_run_on_keypress:
1537 self.auto_run(starting_at=test)
1538 else:
1539 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001540
Jon Salz0697cbf2012-07-04 15:14:04 +08001541 def wait(self):
1542 '''Waits for all pending invocations.
1543
1544 Useful for testing.
1545 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001546 while self.invocations:
1547 for k, v in self.invocations.iteritems():
1548 logging.info('Waiting for %s to complete...', k)
1549 v.thread.join()
1550 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001551
1552 def check_exceptions(self):
1553 '''Raises an error if any exceptions have occurred in
1554 invocation threads.'''
1555 if self.exceptions:
1556 raise RuntimeError('Exception in invocation thread: %r' %
1557 self.exceptions)
1558
1559 def record_exception(self, msg):
1560 '''Records an exception in an invocation thread.
1561
1562 An exception with the given message will be rethrown when
1563 Goofy is destroyed.'''
1564 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001565
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001566
1567if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001568 goofy = Goofy()
1569 try:
1570 goofy.main()
Jon Salz0f996602012-10-03 15:26:48 +08001571 except SystemExit:
1572 # Propagate SystemExit without logging.
1573 raise
Jon Salz31373eb2012-09-21 16:19:49 +08001574 except:
Jon Salz0f996602012-10-03 15:26:48 +08001575 # Log the error before trying to shut down (unless it's a graceful
1576 # exit).
Jon Salz31373eb2012-09-21 16:19:49 +08001577 logging.exception('Error in main loop')
1578 raise
Jon Salz77c151e2012-08-28 07:20:37 +08001579 finally:
1580 goofy.destroy()