blob: 0835044f4b4110d51cb9ac7704afb73787ea8dd0 [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
Hung-Te Linf2f78f72012-02-08 19:27:11 +08002# -*- coding: utf-8 -*-
3#
Jon Salz37eccbd2012-05-25 16:06:52 +08004# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08005# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8'''
9The main factory flow that runs the factory test and finalizes a device.
10'''
11
Jon Salz0405ab52012-03-16 15:26:52 +080012import logging
13import os
Jon Salz73e0fd02012-04-04 11:46:38 +080014import Queue
Jon Salz77c151e2012-08-28 07:20:37 +080015import signal
Jon Salz0405ab52012-03-16 15:26:52 +080016import sys
Jon Salz0405ab52012-03-16 15:26:52 +080017import threading
18import time
19import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080020import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080021from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080022from collections import deque
23from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080024
Jon Salz0697cbf2012-07-04 15:14:04 +080025import factory_common # pylint: disable=W0611
jcliangcd688182012-08-20 21:01:26 +080026from cros.factory import event_log
27from cros.factory import system
28from cros.factory.event_log import EventLog
29from cros.factory.goofy import test_environment
30from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080031from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080032from cros.factory.goofy.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080033from cros.factory.goofy.goofy_rpc import GoofyRPC
34from cros.factory.goofy.invocation import TestInvocation
35from cros.factory.goofy.prespawner import Prespawner
36from cros.factory.goofy.web_socket_manager import WebSocketManager
37from cros.factory.system.charge_manager import ChargeManager
38from cros.factory.test import factory
39from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080040from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080041from cros.factory.test import utils
42from cros.factory.test.event import Event
43from cros.factory.test.event import EventClient
44from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080045from cros.factory.test.factory import TestState
Jon Salz78c32392012-07-25 14:18:29 +080046from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080047
48
Jon Salz2f757d42012-06-27 17:06:42 +080049DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
50CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080051HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
52
Jon Salz8796e362012-05-24 11:39:09 +080053# File that suppresses reboot if present (e.g., for development).
54NO_REBOOT_FILE = '/var/log/factory.noreboot'
55
Jon Salz5c344f62012-07-13 14:31:16 +080056# Value for tests_after_shutdown that forces auto-run (e.g., after
57# a factory update, when the available set of tests might change).
58FORCE_AUTO_RUN = 'force_auto_run'
59
cychiang21886742012-07-05 15:16:32 +080060RUN_QUEUE_TIMEOUT_SECS = 10
61
Jon Salz758e6cc2012-04-03 15:47:07 +080062GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
63You are running Goofy inside the chroot. Autotests are not supported.
64
65To use Goofy in the chroot, first install an Xvnc server:
66
Jon Salz0697cbf2012-07-04 15:14:04 +080067 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080068
69...and then start a VNC X server outside the chroot:
70
Jon Salz0697cbf2012-07-04 15:14:04 +080071 vncserver :10 &
72 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080073
74...and run Goofy as follows:
75
Jon Salz0697cbf2012-07-04 15:14:04 +080076 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080077''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080078suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080079
80def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080081 '''
82 Returns the HWID config tag, or an empty string if none can be found.
83 '''
84 if 'CROS_HWID' in os.environ:
85 return os.environ['CROS_HWID']
86 if os.path.exists(HWID_CFG_PATH):
87 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
88 return hwid_cfg_handle.read().strip()
89 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080090
91
92def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080093 '''
94 Returns the path to the active test list, based on the HWID config tag.
95 '''
96 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +080097
Jon Salz0697cbf2012-07-04 15:14:04 +080098 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
Jon Salz2f757d42012-06-27 17:06:42 +080099
Jon Salz0697cbf2012-07-04 15:14:04 +0800100 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
101 search_files = ['test_list', 'test_list.all']
102 if hwid_cfg:
103 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800104
Jon Salz0697cbf2012-07-04 15:14:04 +0800105 for d in search_dirs:
106 for f in search_files:
107 test_list = os.path.join(d, f)
108 if os.path.exists(test_list):
109 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800110
Jon Salz0697cbf2012-07-04 15:14:04 +0800111 logging.warn('Cannot find test lists named any of %s in any of %s',
112 search_files, search_dirs)
113 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800114
Jon Salz73e0fd02012-04-04 11:46:38 +0800115_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800116
117class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800118 '''
119 The main factory flow.
120
121 Note that all methods in this class must be invoked from the main
122 (event) thread. Other threads, such as callbacks and TestInvocation
123 methods, should instead post events on the run queue.
124
125 TODO: Unit tests. (chrome-os-partner:7409)
126
127 Properties:
128 uuid: A unique UUID for this invocation of Goofy.
129 state_instance: An instance of FactoryState.
130 state_server: The FactoryState XML/RPC server.
131 state_server_thread: A thread running state_server.
132 event_server: The EventServer socket server.
133 event_server_thread: A thread running event_server.
134 event_client: A client to the event server.
135 connection_manager: The connection_manager object.
136 network_enabled: Whether the connection_manager is currently
137 enabling connections.
138 ui_process: The factory ui process object.
139 run_queue: A queue of callbacks to invoke from the main thread.
140 invocations: A map from FactoryTest objects to the corresponding
141 TestInvocations objects representing active tests.
142 tests_to_run: A deque of tests that should be run when the current
143 test(s) complete.
144 options: Command-line options.
145 args: Command-line args.
146 test_list: The test list.
147 event_handlers: Map of Event.Type to the method used to handle that
148 event. If the method has an 'event' argument, the event is passed
149 to the handler.
150 exceptions: Exceptions encountered in invocation threads.
151 '''
152 def __init__(self):
153 self.uuid = str(uuid.uuid4())
154 self.state_instance = None
155 self.state_server = None
156 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800157 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800158 self.event_server = None
159 self.event_server_thread = None
160 self.event_client = None
161 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800162 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800163 self.time_sanitizer = None
164 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800165 self.log_watcher = None
166 self.network_enabled = True
167 self.event_log = None
168 self.prespawner = None
169 self.ui_process = None
170 self.run_queue = Queue.Queue()
171 self.invocations = {}
172 self.tests_to_run = deque()
173 self.visible_test = None
174 self.chrome = None
175
176 self.options = None
177 self.args = None
178 self.test_list = None
179 self.on_ui_startup = []
180 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800181 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800182 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800183 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800184 self.last_sync_time = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800185
Jon Salz85a39882012-07-05 16:45:04 +0800186 def test_or_root(event, parent_or_group=True):
187 '''Returns the test affected by a particular event.
188
189 Args:
190 event: The event containing an optional 'path' attribute.
191 parent_on_group: If True, returns the top-level parent for a test (the
192 root node of the tests that need to be run together if the given test
193 path is to be run).
194 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800195 try:
196 path = event.path
197 except AttributeError:
198 path = None
199
200 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800201 test = self.test_list.lookup_path(path)
202 if parent_or_group:
203 test = test.get_top_level_parent_or_group()
204 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800205 else:
206 return self.test_list
207
208 self.event_handlers = {
209 Event.Type.SWITCH_TEST: self.handle_switch_test,
210 Event.Type.SHOW_NEXT_ACTIVE_TEST:
211 lambda event: self.show_next_active_test(),
212 Event.Type.RESTART_TESTS:
213 lambda event: self.restart_tests(root=test_or_root(event)),
214 Event.Type.AUTO_RUN:
215 lambda event: self.auto_run(root=test_or_root(event)),
216 Event.Type.RE_RUN_FAILED:
217 lambda event: self.re_run_failed(root=test_or_root(event)),
218 Event.Type.RUN_TESTS_WITH_STATUS:
219 lambda event: self.run_tests_with_status(
220 event.status,
221 root=test_or_root(event)),
222 Event.Type.REVIEW:
223 lambda event: self.show_review_information(),
224 Event.Type.UPDATE_SYSTEM_INFO:
225 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800226 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800227 lambda event: self.stop(root=test_or_root(event, False),
228 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800229 Event.Type.SET_VISIBLE_TEST:
230 lambda event: self.set_visible_test(
231 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800232 }
233
234 self.exceptions = []
235 self.web_socket_manager = None
236
237 def destroy(self):
238 if self.chrome:
239 self.chrome.kill()
240 self.chrome = None
241 if self.ui_process:
242 utils.kill_process_tree(self.ui_process, 'ui')
243 self.ui_process = None
244 if self.web_socket_manager:
245 logging.info('Stopping web sockets')
246 self.web_socket_manager.close()
247 self.web_socket_manager = None
248 if self.state_server_thread:
249 logging.info('Stopping state server')
250 self.state_server.shutdown()
251 self.state_server_thread.join()
252 self.state_server.server_close()
253 self.state_server_thread = None
254 if self.state_instance:
255 self.state_instance.close()
256 if self.event_server_thread:
257 logging.info('Stopping event server')
258 self.event_server.shutdown() # pylint: disable=E1101
259 self.event_server_thread.join()
260 self.event_server.server_close()
261 self.event_server_thread = None
262 if self.log_watcher:
263 if self.log_watcher.IsThreadStarted():
264 self.log_watcher.StopWatchThread()
265 self.log_watcher = None
266 if self.prespawner:
267 logging.info('Stopping prespawner')
268 self.prespawner.stop()
269 self.prespawner = None
270 if self.event_client:
271 logging.info('Closing event client')
272 self.event_client.close()
273 self.event_client = None
274 if self.event_log:
275 self.event_log.Close()
276 self.event_log = None
277 self.check_exceptions()
278 logging.info('Done destroying Goofy')
279
280 def start_state_server(self):
281 self.state_instance, self.state_server = (
282 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800283 self.goofy_rpc = GoofyRPC(self)
284 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800285 logging.info('Starting state server')
286 self.state_server_thread = threading.Thread(
287 target=self.state_server.serve_forever,
288 name='StateServer')
289 self.state_server_thread.start()
290
291 def start_event_server(self):
292 self.event_server = EventServer()
293 logging.info('Starting factory event server')
294 self.event_server_thread = threading.Thread(
295 target=self.event_server.serve_forever,
296 name='EventServer') # pylint: disable=E1101
297 self.event_server_thread.start()
298
299 self.event_client = EventClient(
300 callback=self.handle_event, event_loop=self.run_queue)
301
302 self.web_socket_manager = WebSocketManager(self.uuid)
303 self.state_server.add_handler("/event",
304 self.web_socket_manager.handle_web_socket)
305
306 def start_ui(self):
307 ui_proc_args = [
308 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
309 self.options.test_list]
310 if self.options.verbose:
311 ui_proc_args.append('-v')
312 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800313 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800314 logging.info('Waiting for UI to come up...')
315 self.event_client.wait(
316 lambda event: event.type == Event.Type.UI_READY)
317 logging.info('UI has started')
318
319 def set_visible_test(self, test):
320 if self.visible_test == test:
321 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800322 if test and not test.has_ui:
323 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800324
325 if test:
326 test.update_state(visible=True)
327 if self.visible_test:
328 self.visible_test.update_state(visible=False)
329 self.visible_test = test
330
331 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800332 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800333 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800334
Jon Salz0697cbf2012-07-04 15:14:04 +0800335 @param test: The ShutdownStep.
336 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800337 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800338 test_state = test.update_state(increment_shutdown_count=1)
339 logging.info('Detected shutdown (%d of %d)',
340 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800341
Jon Salz0697cbf2012-07-04 15:14:04 +0800342 def log_and_update_state(status, error_msg, **kw):
343 self.event_log.Log('rebooted',
344 status=status, error_msg=error_msg, **kw)
345 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800346
Jon Salz0697cbf2012-07-04 15:14:04 +0800347 if not self.last_shutdown_time:
348 log_and_update_state(status=TestState.FAILED,
349 error_msg='Unable to read shutdown_time')
350 return
Jon Salz258a40c2012-04-19 12:34:01 +0800351
Jon Salz0697cbf2012-07-04 15:14:04 +0800352 now = time.time()
353 logging.info('%.03f s passed since reboot',
354 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800355
Jon Salz0697cbf2012-07-04 15:14:04 +0800356 if self.last_shutdown_time > now:
357 test.update_state(status=TestState.FAILED,
358 error_msg='Time moved backward during reboot')
359 elif (isinstance(test, factory.RebootStep) and
360 self.test_list.options.max_reboot_time_secs and
361 (now - self.last_shutdown_time >
362 self.test_list.options.max_reboot_time_secs)):
363 # A reboot took too long; fail. (We don't check this for
364 # HaltSteps, because the machine could be halted for a
365 # very long time, and even unplugged with battery backup,
366 # thus hosing the clock.)
367 log_and_update_state(
368 status=TestState.FAILED,
369 error_msg=('More than %d s elapsed during reboot '
370 '(%.03f s, from %s to %s)' % (
371 self.test_list.options.max_reboot_time_secs,
372 now - self.last_shutdown_time,
373 utils.TimeString(self.last_shutdown_time),
374 utils.TimeString(now))),
375 duration=(now-self.last_shutdown_time))
376 elif test_state.shutdown_count == test.iterations:
377 # Good!
378 log_and_update_state(status=TestState.PASSED,
379 duration=(now - self.last_shutdown_time),
380 error_msg='')
381 elif test_state.shutdown_count > test.iterations:
382 # Shut down too many times
383 log_and_update_state(status=TestState.FAILED,
384 error_msg='Too many shutdowns')
385 elif utils.are_shift_keys_depressed():
386 logging.info('Shift keys are depressed; cancelling restarts')
387 # Abort shutdown
388 log_and_update_state(
389 status=TestState.FAILED,
390 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800391 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800392 else:
393 def handler():
394 if self._prompt_cancel_shutdown(
395 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800396 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800397 log_and_update_state(
398 status=TestState.FAILED,
399 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800400 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800401 return
Jon Salz0405ab52012-03-16 15:26:52 +0800402
Jon Salz0697cbf2012-07-04 15:14:04 +0800403 # Time to shutdown again
404 log_and_update_state(
405 status=TestState.ACTIVE,
406 error_msg='',
407 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800408
Jon Salz0697cbf2012-07-04 15:14:04 +0800409 self.event_log.Log('shutdown', operation='reboot')
410 self.state_instance.set_shared_data('shutdown_time',
411 time.time())
412 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800413
Jon Salz0697cbf2012-07-04 15:14:04 +0800414 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800415
Jon Salz0697cbf2012-07-04 15:14:04 +0800416 def _prompt_cancel_shutdown(self, test, iteration):
417 if self.options.ui != 'chrome':
418 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800419
Jon Salz0697cbf2012-07-04 15:14:04 +0800420 pending_shutdown_data = {
421 'delay_secs': test.delay_secs,
422 'time': time.time() + test.delay_secs,
423 'operation': test.operation,
424 'iteration': iteration,
425 'iterations': test.iterations,
426 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800427
Jon Salz0697cbf2012-07-04 15:14:04 +0800428 # Create a new (threaded) event client since we
429 # don't want to use the event loop for this.
430 with EventClient() as event_client:
431 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
432 **pending_shutdown_data))
433 aborted = event_client.wait(
434 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
435 timeout=test.delay_secs) is not None
436 if aborted:
437 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
438 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800439
Jon Salz0697cbf2012-07-04 15:14:04 +0800440 def init_states(self):
441 '''
442 Initializes all states on startup.
443 '''
444 for test in self.test_list.get_all_tests():
445 # Make sure the state server knows about all the tests,
446 # defaulting to an untested state.
447 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800448
Jon Salz0697cbf2012-07-04 15:14:04 +0800449 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800450 mosys_log = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800451
Jon Salz0697cbf2012-07-04 15:14:04 +0800452 # Any 'active' tests should be marked as failed now.
453 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800454 if not test.is_leaf():
455 # Don't bother with parents; they will be updated when their
456 # children are updated.
457 continue
458
Jon Salz0697cbf2012-07-04 15:14:04 +0800459 test_state = test.get_state()
460 if test_state.status != TestState.ACTIVE:
461 continue
462 if isinstance(test, factory.ShutdownStep):
463 # Shutdown while the test was active - that's good.
464 self.handle_shutdown_complete(test, test_state)
465 else:
466 # Unexpected shutdown. Grab /var/log/messages for context.
467 if var_log_messages is None:
468 try:
469 var_log_messages = (
470 utils.var_log_messages_before_reboot())
471 # Write it to the log, to make it easier to
472 # correlate with /var/log/messages.
473 logging.info(
474 'Unexpected shutdown. '
475 'Tail of /var/log/messages before last reboot:\n'
476 '%s', ('\n'.join(
477 ' ' + x for x in var_log_messages)))
478 except: # pylint: disable=W0702
479 logging.exception('Unable to grok /var/log/messages')
480 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800481
Jon Salz008f4ea2012-08-28 05:39:45 +0800482 if mosys_log is None and not utils.in_chroot():
483 try:
484 mosys_log = utils.Spawn(
485 ['mosys', 'eventlog', 'list'],
486 read_stdout=True, log_stderr_on_error=True).stdout_data
487 # Write it to the log also.
488 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
489 except: # pylint: disable=W0702
490 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800491
Jon Salz0697cbf2012-07-04 15:14:04 +0800492 error_msg = 'Unexpected shutdown while test was running'
493 self.event_log.Log('end_test',
494 path=test.path,
495 status=TestState.FAILED,
496 invocation=test.get_state().invocation,
497 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800498 var_log_messages='\n'.join(var_log_messages),
499 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800500 test.update_state(
501 status=TestState.FAILED,
502 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800503
Jon Salz50efe942012-07-26 11:54:10 +0800504 if not test.never_fails:
505 # For "never_fails" tests (such as "Start"), don't cancel
506 # pending tests, since reboot is expected.
507 factory.console.info('Unexpected shutdown while test %s '
508 'running; cancelling any pending tests',
509 test.path)
510 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800511
Jon Salz008f4ea2012-08-28 05:39:45 +0800512 self.update_skipped_tests()
513
514 def update_skipped_tests(self):
515 '''
516 Updates skipped states based on run_if.
517 '''
518 for t in self.test_list.walk():
519 if t.is_leaf() and t.run_if_table_name:
520 skip = False
521 try:
522 aux = shopfloor.get_selected_aux_data(t.run_if_table_name)
523 value = aux.get(t.run_if_col)
524 if value is not None:
525 skip = (not value) ^ t.run_if_not
526 except ValueError:
527 # Not available; assume it shouldn't be skipped
528 pass
529
530 test_state = t.get_state()
531 if ((not skip) and
532 (test_state.status == TestState.PASSED) and
533 (test_state.error_msg == TestState.SKIPPED_MSG)):
534 # It was marked as skipped before, but now we need to run it.
535 # Mark as untested.
536 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
537 else:
538 t.update_state(skip=skip)
539
Jon Salz0697cbf2012-07-04 15:14:04 +0800540 def show_next_active_test(self):
541 '''
542 Rotates to the next visible active test.
543 '''
544 self.reap_completed_tests()
545 active_tests = [
546 t for t in self.test_list.walk()
547 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
548 if not active_tests:
549 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800550
Jon Salz0697cbf2012-07-04 15:14:04 +0800551 try:
552 next_test = active_tests[
553 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
554 except ValueError: # visible_test not present in active_tests
555 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800556
Jon Salz0697cbf2012-07-04 15:14:04 +0800557 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800558
Jon Salz0697cbf2012-07-04 15:14:04 +0800559 def handle_event(self, event):
560 '''
561 Handles an event from the event server.
562 '''
563 handler = self.event_handlers.get(event.type)
564 if handler:
565 handler(event)
566 else:
567 # We don't register handlers for all event types - just ignore
568 # this event.
569 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800570
Jon Salz0697cbf2012-07-04 15:14:04 +0800571 def run_next_test(self):
572 '''
573 Runs the next eligible test (or tests) in self.tests_to_run.
574 '''
575 self.reap_completed_tests()
576 while self.tests_to_run:
577 logging.debug('Tests to run: %s',
578 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800579
Jon Salz0697cbf2012-07-04 15:14:04 +0800580 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800581
Jon Salz0697cbf2012-07-04 15:14:04 +0800582 if test in self.invocations:
583 logging.info('Next test %s is already running', test.path)
584 self.tests_to_run.popleft()
585 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800586
Jon Salz008f4ea2012-08-28 05:39:45 +0800587 if test.get_state().skip:
588 factory.console.info('Skipping test %s', test.path)
589 test.update_state(status=TestState.PASSED,
590 error_msg=TestState.SKIPPED_MSG)
591 self.tests_to_run.popleft()
592 return
593
Jon Salza1412922012-07-23 16:04:17 +0800594 for requirement in test.require_run:
595 for i in requirement.test.walk():
596 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800597 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800598 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800599 return
600
Jon Salz0697cbf2012-07-04 15:14:04 +0800601 if self.invocations and not (test.backgroundable and all(
602 [x.backgroundable for x in self.invocations])):
603 logging.debug('Waiting for non-backgroundable tests to '
604 'complete before running %s', test.path)
605 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800606
Jon Salz0697cbf2012-07-04 15:14:04 +0800607 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800608
Jon Salz304a75d2012-07-06 11:14:15 +0800609 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800610 for requirement in test.require_run:
611 for i in requirement.test.walk():
612 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800613 # We've hit this test itself; stop checking
614 break
Jon Salza1412922012-07-23 16:04:17 +0800615 if ((i.get_state().status == TestState.UNTESTED) or
616 (requirement.passed and i.get_state().status !=
617 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800618 # Found an untested test; move on to the next
619 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800620 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800621 break
622
623 if untested:
624 untested_paths = ', '.join(sorted([x.path for x in untested]))
625 if self.state_instance.get_shared_data('engineering_mode',
626 optional=True):
627 # In engineering mode, we'll let it go.
628 factory.console.warn('In engineering mode; running '
629 '%s even though required tests '
630 '[%s] have not completed',
631 test.path, untested_paths)
632 else:
633 # Not in engineering mode; mark it failed.
634 error_msg = ('Required tests [%s] have not been run yet'
635 % untested_paths)
636 factory.console.error('Not running %s: %s',
637 test.path, error_msg)
638 test.update_state(status=TestState.FAILED,
639 error_msg=error_msg)
640 continue
641
Jon Salz0697cbf2012-07-04 15:14:04 +0800642 if isinstance(test, factory.ShutdownStep):
643 if os.path.exists(NO_REBOOT_FILE):
644 test.update_state(
645 status=TestState.FAILED, increment_count=1,
646 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800647 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800648 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800649
Jon Salz0697cbf2012-07-04 15:14:04 +0800650 test.update_state(status=TestState.ACTIVE, increment_count=1,
651 error_msg='', shutdown_count=0)
652 if self._prompt_cancel_shutdown(test, 1):
653 self.event_log.Log('reboot_cancelled')
654 test.update_state(
655 status=TestState.FAILED, increment_count=1,
656 error_msg='Shutdown aborted by operator',
657 shutdown_count=0)
658 return
Jon Salz2f757d42012-06-27 17:06:42 +0800659
Jon Salz0697cbf2012-07-04 15:14:04 +0800660 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800661 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800662 'tests_after_shutdown',
663 [t.path for t in self.tests_to_run])
664 # Save shutdown time
665 self.state_instance.set_shared_data('shutdown_time',
666 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800667
Jon Salz0697cbf2012-07-04 15:14:04 +0800668 with self.env.lock:
669 self.event_log.Log('shutdown', operation=test.operation)
670 shutdown_result = self.env.shutdown(test.operation)
671 if shutdown_result:
672 # That's all, folks!
673 self.run_queue.put(None)
674 return
675 else:
676 # Just pass (e.g., in the chroot).
677 test.update_state(status=TestState.PASSED)
678 self.state_instance.set_shared_data(
679 'tests_after_shutdown', None)
680 # Send event with no fields to indicate that there is no
681 # longer a pending shutdown.
682 self.event_client.post_event(Event(
683 Event.Type.PENDING_SHUTDOWN))
684 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800685
Jon Salz1acc8742012-07-17 17:45:55 +0800686 self._run_test(test, test.iterations)
687
688 def _run_test(self, test, iterations_left=None):
689 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
690 new_state = test.update_state(
691 status=TestState.ACTIVE, increment_count=1, error_msg='',
692 invocation=invoc.uuid, iterations_left=iterations_left)
693 invoc.count = new_state.count
694
695 self.invocations[test] = invoc
696 if self.visible_test is None and test.has_ui:
697 self.set_visible_test(test)
698 self.check_connection_manager()
699 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800700
Jon Salz0697cbf2012-07-04 15:14:04 +0800701 def check_connection_manager(self):
702 exclusive_tests = [
703 test.path
704 for test in self.invocations
705 if test.is_exclusive(
706 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
707 if exclusive_tests:
708 # Make sure networking is disabled.
709 if self.network_enabled:
710 logging.info('Disabling network, as requested by %s',
711 exclusive_tests)
712 self.connection_manager.DisableNetworking()
713 self.network_enabled = False
714 else:
715 # Make sure networking is enabled.
716 if not self.network_enabled:
717 logging.info('Re-enabling network')
718 self.connection_manager.EnableNetworking()
719 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800720
cychiang21886742012-07-05 15:16:32 +0800721 def check_for_updates(self):
722 '''
723 Schedules an asynchronous check for updates if necessary.
724 '''
725 if not self.test_list.options.update_period_secs:
726 # Not enabled.
727 return
728
729 now = time.time()
730 if self.last_update_check and (
731 now - self.last_update_check <
732 self.test_list.options.update_period_secs):
733 # Not yet time for another check.
734 return
735
736 self.last_update_check = now
737
738 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
739 if reached_shopfloor:
740 new_update_md5sum = md5sum if needs_update else None
741 if system.SystemInfo.update_md5sum != new_update_md5sum:
742 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
743 system.SystemInfo.update_md5sum = new_update_md5sum
744 self.run_queue.put(self.update_system_info)
745
746 updater.CheckForUpdateAsync(
747 handle_check_for_update,
748 self.test_list.options.shopfloor_timeout_secs)
749
Jon Salza6711d72012-07-18 14:33:03 +0800750 def cancel_pending_tests(self):
751 '''Cancels any tests in the run queue.'''
752 self.run_tests([])
753
Jon Salz0697cbf2012-07-04 15:14:04 +0800754 def run_tests(self, subtrees, untested_only=False):
755 '''
756 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800757
Jon Salz0697cbf2012-07-04 15:14:04 +0800758 The tests are run in order unless one fails (then stops).
759 Backgroundable tests are run simultaneously; when a foreground test is
760 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800761
Jon Salz0697cbf2012-07-04 15:14:04 +0800762 @param subtrees: Node or nodes containing tests to run (may either be
763 a single test or a list). Duplicates will be ignored.
764 '''
765 if type(subtrees) != list:
766 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800767
Jon Salz0697cbf2012-07-04 15:14:04 +0800768 # Nodes we've seen so far, to avoid duplicates.
769 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800770
Jon Salz0697cbf2012-07-04 15:14:04 +0800771 self.tests_to_run = deque()
772 for subtree in subtrees:
773 for test in subtree.walk():
774 if test in seen:
775 continue
776 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800777
Jon Salz0697cbf2012-07-04 15:14:04 +0800778 if not test.is_leaf():
779 continue
780 if (untested_only and
781 test.get_state().status != TestState.UNTESTED):
782 continue
783 self.tests_to_run.append(test)
784 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800785
Jon Salz0697cbf2012-07-04 15:14:04 +0800786 def reap_completed_tests(self):
787 '''
788 Removes completed tests from the set of active tests.
789
790 Also updates the visible test if it was reaped.
791 '''
792 for t, v in dict(self.invocations).iteritems():
793 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800794 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800795 del self.invocations[t]
796
Jon Salz1acc8742012-07-17 17:45:55 +0800797 if new_state.iterations_left and new_state.status == TestState.PASSED:
798 # Play it again, Sam!
799 self._run_test(t)
800
Jon Salz0697cbf2012-07-04 15:14:04 +0800801 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800802 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800803 self.set_visible_test(None)
804 # Make the first running test, if any, the visible test
805 for t in self.test_list.walk():
806 if t in self.invocations:
807 self.set_visible_test(t)
808 break
809
Jon Salz85a39882012-07-05 16:45:04 +0800810 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800811 '''
812 Kills and waits for all active tests.
813
Jon Salz85a39882012-07-05 16:45:04 +0800814 Args:
815 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800816 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800817 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800818 '''
819 self.reap_completed_tests()
820 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800821 if root and not test.has_ancestor(root):
822 continue
823
Jon Salz0697cbf2012-07-04 15:14:04 +0800824 factory.console.info('Killing active test %s...' % test.path)
825 invoc.abort_and_join()
826 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800827 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800828 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800829
Jon Salz0697cbf2012-07-04 15:14:04 +0800830 if not abort:
831 test.update_state(status=TestState.UNTESTED)
832 self.reap_completed_tests()
833
Jon Salz85a39882012-07-05 16:45:04 +0800834 def stop(self, root=None, fail=False):
835 self.kill_active_tests(fail, root)
836 # Remove any tests in the run queue under the root.
837 self.tests_to_run = deque([x for x in self.tests_to_run
838 if root and not x.has_ancestor(root)])
839 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800840
841 def abort_active_tests(self):
842 self.kill_active_tests(True)
843
844 def main(self):
845 try:
846 self.init()
847 self.event_log.Log('goofy_init',
848 success=True)
849 except:
850 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800851 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800852 self.event_log.Log('goofy_init',
853 success=False,
854 trace=traceback.format_exc())
855 except: # pylint: disable=W0702
856 pass
857 raise
858
859 self.run()
860
861 def update_system_info(self):
862 '''Updates system info.'''
863 system_info = system.SystemInfo()
864 self.state_instance.set_shared_data('system_info', system_info.__dict__)
865 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
866 system_info=system_info.__dict__))
867 logging.info('System info: %r', system_info.__dict__)
868
Jon Salzeb42f0d2012-07-27 19:14:04 +0800869 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
870 '''Commences updating factory software.
871
872 Args:
873 auto_run_on_restart: Auto-run when the machine comes back up.
874 post_update_hook: Code to call after update but immediately before
875 restart.
876
877 Returns:
878 Never if the update was successful (we just reboot).
879 False if the update was unnecessary (no update available).
880 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800881 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800882 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800883
Jon Salz5c344f62012-07-13 14:31:16 +0800884 def pre_update_hook():
885 if auto_run_on_restart:
886 self.state_instance.set_shared_data('tests_after_shutdown',
887 FORCE_AUTO_RUN)
888 self.state_instance.close()
889
Jon Salzeb42f0d2012-07-27 19:14:04 +0800890 if updater.TryUpdate(pre_update_hook=pre_update_hook):
891 if post_update_hook:
892 post_update_hook()
893 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800894
Jon Salzcef132a2012-08-30 04:58:08 +0800895 def handle_sigint(self, dummy_signum, dummy_frame):
Jon Salz77c151e2012-08-28 07:20:37 +0800896 logging.error('Received SIGINT')
897 self.run_queue.put(None)
898 raise KeyboardInterrupt()
899
Jon Salz0697cbf2012-07-04 15:14:04 +0800900 def init(self, args=None, env=None):
901 '''Initializes Goofy.
902
903 Args:
904 args: A list of command-line arguments. Uses sys.argv if
905 args is None.
906 env: An Environment instance to use (or None to choose
907 FakeChrootEnvironment or DUTEnvironment as appropriate).
908 '''
Jon Salz77c151e2012-08-28 07:20:37 +0800909 signal.signal(signal.SIGINT, self.handle_sigint)
910
Jon Salz0697cbf2012-07-04 15:14:04 +0800911 parser = OptionParser()
912 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800913 action='store_true',
914 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800915 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800916 metavar='FILE',
917 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800918 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800919 action='store_true',
920 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800921 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800922 choices=['none', 'gtk', 'chrome'],
923 default=('chrome' if utils.in_chroot() else 'gtk'),
924 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800925 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800926 type='int', default=1,
927 help=('Factor by which to scale UI '
928 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800929 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800930 metavar='FILE',
931 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800932 (self.options, self.args) = parser.parse_args(args)
933
Jon Salz46b89562012-07-05 11:49:22 +0800934 # Make sure factory directories exist.
935 factory.get_log_root()
936 factory.get_state_root()
937 factory.get_test_data_root()
938
Jon Salz0697cbf2012-07-04 15:14:04 +0800939 global _inited_logging # pylint: disable=W0603
940 if not _inited_logging:
941 factory.init_logging('goofy', verbose=self.options.verbose)
942 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800943
Jon Salzee85d522012-07-17 14:34:46 +0800944 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +0800945 self.event_log = EventLog('goofy')
946
947 if (not suppress_chroot_warning and
948 factory.in_chroot() and
949 self.options.ui == 'gtk' and
950 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
951 # That's not going to work! Tell the user how to run
952 # this way.
953 logging.warn(GOOFY_IN_CHROOT_WARNING)
954 time.sleep(1)
955
956 if env:
957 self.env = env
958 elif factory.in_chroot():
959 self.env = test_environment.FakeChrootEnvironment()
960 logging.warn(
961 'Using chroot environment: will not actually run autotests')
962 else:
963 self.env = test_environment.DUTEnvironment()
964 self.env.goofy = self
965
966 if self.options.restart:
967 state.clear_state()
968
969 if self.options.print_test_list:
Jon Salzeb42f0d2012-07-27 19:14:04 +0800970 print factory.read_test_list(
971 self.options.print_test_list).__repr__(recursive=True)
Jon Salz0697cbf2012-07-04 15:14:04 +0800972 return
973
974 if self.options.ui_scale_factor != 1 and utils.in_qemu():
975 logging.warn(
976 'In QEMU; ignoring ui_scale_factor argument')
977 self.options.ui_scale_factor = 1
978
979 logging.info('Started')
980
981 self.start_state_server()
982 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
983 self.state_instance.set_shared_data('ui_scale_factor',
984 self.options.ui_scale_factor)
985 self.last_shutdown_time = (
986 self.state_instance.get_shared_data('shutdown_time', optional=True))
987 self.state_instance.del_shared_data('shutdown_time', optional=True)
988
989 if not self.options.test_list:
990 self.options.test_list = find_test_list()
991 if not self.options.test_list:
992 logging.error('No test list. Aborting.')
993 sys.exit(1)
994 logging.info('Using test list %s', self.options.test_list)
995
996 self.test_list = factory.read_test_list(
997 self.options.test_list,
Jon Salzeb42f0d2012-07-27 19:14:04 +0800998 self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800999 if not self.state_instance.has_shared_data('ui_lang'):
1000 self.state_instance.set_shared_data('ui_lang',
1001 self.test_list.options.ui_lang)
1002 self.state_instance.set_shared_data(
1003 'test_list_options',
1004 self.test_list.options.__dict__)
1005 self.state_instance.test_list = self.test_list
1006
Jon Salz8fa8e832012-07-13 19:04:09 +08001007 if self.test_list.options.time_sanitizer:
1008 self.time_sanitizer = time_sanitizer.TimeSanitizer(
1009 base_time=time_sanitizer.GetBaseTimeFromFile(
1010 # lsb-factory is written by the factory install shim during
1011 # installation, so it should have a good time obtained from
1012 # the mini-Omaha server.
1013 '/usr/local/etc/lsb-factory'))
1014 self.time_sanitizer.RunOnce()
1015
Jon Salz0697cbf2012-07-04 15:14:04 +08001016 self.init_states()
1017 self.start_event_server()
1018 self.connection_manager = self.env.create_connection_manager(
1019 self.test_list.options.wlans)
1020 # Note that we create a log watcher even if
1021 # sync_event_log_period_secs isn't set (no background
1022 # syncing), since we may use it to flush event logs as well.
1023 self.log_watcher = EventLogWatcher(
1024 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +08001025 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001026 if self.test_list.options.sync_event_log_period_secs:
1027 self.log_watcher.StartWatchThread()
1028
1029 self.update_system_info()
1030
Vic Yang4953fc12012-07-26 16:19:53 +08001031 assert ((self.test_list.options.min_charge_pct is None) ==
1032 (self.test_list.options.max_charge_pct is None))
1033 if (self.test_list.options.min_charge_pct and
1034 self.test_list.options.max_charge_pct):
1035 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1036 self.test_list.options.max_charge_pct)
1037
Jon Salz0697cbf2012-07-04 15:14:04 +08001038 os.environ['CROS_FACTORY'] = '1'
1039 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1040
1041 # Set CROS_UI since some behaviors in ui.py depend on the
1042 # particular UI in use. TODO(jsalz): Remove this (and all
1043 # places it is used) when the GTK UI is removed.
1044 os.environ['CROS_UI'] = self.options.ui
1045
1046 if self.options.ui == 'chrome':
1047 self.env.launch_chrome()
1048 logging.info('Waiting for a web socket connection')
1049 self.web_socket_manager.wait()
1050
1051 # Wait for the test widget size to be set; this is done in
1052 # an asynchronous RPC so there is a small chance that the
1053 # web socket might be opened first.
1054 for _ in range(100): # 10 s
1055 try:
1056 if self.state_instance.get_shared_data('test_widget_size'):
1057 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001058 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001059 pass # Retry
1060 time.sleep(0.1) # 100 ms
1061 else:
1062 logging.warn('Never received test_widget_size from UI')
1063 elif self.options.ui == 'gtk':
1064 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001065
Jon Salz0697cbf2012-07-04 15:14:04 +08001066 def state_change_callback(test, test_state):
1067 self.event_client.post_event(
1068 Event(Event.Type.STATE_CHANGE,
1069 path=test.path, state=test_state))
1070 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001071
Jon Salza6711d72012-07-18 14:33:03 +08001072 for handler in self.on_ui_startup:
1073 handler()
1074
1075 self.prespawner = Prespawner()
1076 self.prespawner.start()
1077
Jon Salz0697cbf2012-07-04 15:14:04 +08001078 try:
1079 tests_after_shutdown = self.state_instance.get_shared_data(
1080 'tests_after_shutdown')
1081 except KeyError:
1082 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001083
Jon Salz5c344f62012-07-13 14:31:16 +08001084 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1085 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001086 logging.info('Resuming tests after shutdown: %s',
1087 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001088 self.tests_to_run.extend(
1089 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1090 self.run_queue.put(self.run_next_test)
1091 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001092 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001093 self.run_queue.put(
1094 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001095 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001096
Jon Salz0697cbf2012-07-04 15:14:04 +08001097 def run(self):
1098 '''Runs Goofy.'''
1099 # Process events forever.
1100 while self.run_once(True):
1101 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001102
Jon Salz0697cbf2012-07-04 15:14:04 +08001103 def run_once(self, block=False):
1104 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001105
Jon Salz0697cbf2012-07-04 15:14:04 +08001106 Args:
1107 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001108
Jon Salz0697cbf2012-07-04 15:14:04 +08001109 Returns:
1110 True to keep going or False to shut down.
1111 '''
1112 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001113 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001114 # Nothing on the run queue.
1115 self._run_queue_idle()
1116 if block:
1117 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001118 try:
1119 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1120 except Queue.Empty:
1121 # Keep going (calling _run_queue_idle() again at the top of
1122 # the loop)
1123 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001124 # ...and grab anything else that showed up at the same
1125 # time.
1126 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001127 else:
1128 break
Jon Salz51528e12012-07-02 18:54:45 +08001129
Jon Salz0697cbf2012-07-04 15:14:04 +08001130 for event in events:
1131 if not event:
1132 # Shutdown request.
1133 self.run_queue.task_done()
1134 return False
Jon Salz51528e12012-07-02 18:54:45 +08001135
Jon Salz0697cbf2012-07-04 15:14:04 +08001136 try:
1137 event()
Jon Salz85a39882012-07-05 16:45:04 +08001138 except: # pylint: disable=W0702
1139 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001140 self.record_exception(traceback.format_exception_only(
1141 *sys.exc_info()[:2]))
1142 # But keep going
1143 finally:
1144 self.run_queue.task_done()
1145 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001146
Jon Salz8fa8e832012-07-13 19:04:09 +08001147 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001148 '''Writes out current time and tries to sync with shopfloor server.'''
1149 if not self.time_sanitizer:
1150 return
1151
1152 # Write out the current time.
1153 self.time_sanitizer.SaveTime()
1154
Jon Salz8fa8e832012-07-13 19:04:09 +08001155 if ((not self.test_list.options.sync_time_period_secs) or
1156 (not self.time_sanitizer) or
1157 self.time_synced or
1158 factory.in_chroot()):
1159 # Not enabled or already succeeded.
1160 return
1161
1162 now = time.time()
1163 if self.last_sync_time and (
1164 now - self.last_sync_time <
1165 self.test_list.options.sync_time_period_secs):
1166 # Not yet time for another check.
1167 return
1168 self.last_sync_time = now
1169
1170 def target():
1171 try:
1172 self.time_sanitizer.SyncWithShopfloor()
1173 self.time_synced = True
1174 except: # pylint: disable=W0702
1175 # Oh well. Log an error (but no trace)
1176 logging.info(
1177 'Unable to get time from shopfloor server: %s',
1178 utils.FormatExceptionOnly())
1179
1180 thread = threading.Thread(target=target)
1181 thread.daemon = True
1182 thread.start()
1183
Jon Salz0697cbf2012-07-04 15:14:04 +08001184 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001185 '''Invoked when the run queue has no events.
1186
1187 This method must not raise exception.
1188 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001189 now = time.time()
1190 if (self.last_idle and
1191 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1192 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1193 # 1) seconds.
1194 return
1195
1196 self.last_idle = now
1197
Jon Salz0697cbf2012-07-04 15:14:04 +08001198 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001199 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001200 self.sync_time_in_background()
Vic Yang4953fc12012-07-26 16:19:53 +08001201 if self.charge_manager:
1202 self.charge_manager.AdjustChargeState()
Jon Salz57717ca2012-04-04 16:47:25 +08001203
Jon Salz16d10542012-07-23 12:18:45 +08001204 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001205 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001206
Jon Salz0697cbf2012-07-04 15:14:04 +08001207 Attempts to upload the event logs to the shopfloor server.
1208 '''
1209 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1210 start_time = time.time()
1211 logging.info('Syncing %s', description)
1212 shopfloor_client = shopfloor.get_instance(
1213 detect=True,
1214 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzb10cf512012-08-09 17:29:21 +08001215 shopfloor_client.UploadEvent(log_name, Binary(chunk))
Jon Salz0697cbf2012-07-04 15:14:04 +08001216 logging.info(
1217 'Successfully synced %s in %.03f s',
1218 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001219
Jon Salz0697cbf2012-07-04 15:14:04 +08001220 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1221 root=None):
1222 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001223
Jon Salz0697cbf2012-07-04 15:14:04 +08001224 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001225
Jon Salz0697cbf2012-07-04 15:14:04 +08001226 Args:
1227 starting_at: If provided, only auto-runs tests beginning with
1228 this test.
1229 '''
1230 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001231
Jon Salz0697cbf2012-07-04 15:14:04 +08001232 if starting_at:
1233 # Make sure they passed a test, not a string.
1234 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001235
Jon Salz0697cbf2012-07-04 15:14:04 +08001236 tests_to_reset = []
1237 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001238
Jon Salz0697cbf2012-07-04 15:14:04 +08001239 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001240
Jon Salz0697cbf2012-07-04 15:14:04 +08001241 for test in root.get_top_level_tests():
1242 if starting_at:
1243 if test == starting_at:
1244 # We've found starting_at; do auto-run on all
1245 # subsequent tests.
1246 found_starting_at = True
1247 if not found_starting_at:
1248 # Don't start this guy yet
1249 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001250
Jon Salz0697cbf2012-07-04 15:14:04 +08001251 status = test.get_state().status
1252 if status == TestState.ACTIVE or status in statuses_to_run:
1253 # Reset the test (later; we will need to abort
1254 # all active tests first).
1255 tests_to_reset.append(test)
1256 if status in statuses_to_run:
1257 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001258
Jon Salz0697cbf2012-07-04 15:14:04 +08001259 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001260
Jon Salz0697cbf2012-07-04 15:14:04 +08001261 # Reset all statuses of the tests to run (in case any tests were active;
1262 # we want them to be run again).
1263 for test_to_reset in tests_to_reset:
1264 for test in test_to_reset.walk():
1265 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001266
Jon Salz0697cbf2012-07-04 15:14:04 +08001267 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001268
Jon Salz0697cbf2012-07-04 15:14:04 +08001269 def restart_tests(self, root=None):
1270 '''Restarts all tests.'''
1271 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001272
Jon Salz0697cbf2012-07-04 15:14:04 +08001273 self.abort_active_tests()
1274 for test in root.walk():
1275 test.update_state(status=TestState.UNTESTED)
1276 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001277
Jon Salz0697cbf2012-07-04 15:14:04 +08001278 def auto_run(self, starting_at=None, root=None):
1279 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001280
Jon Salz0697cbf2012-07-04 15:14:04 +08001281 Args:
1282 starting_at: If provide, only auto-runs tests beginning with
1283 this test.
1284 '''
1285 root = root or self.test_list
1286 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1287 starting_at=starting_at,
1288 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001289
Jon Salz0697cbf2012-07-04 15:14:04 +08001290 def re_run_failed(self, root=None):
1291 '''Re-runs failed tests.'''
1292 root = root or self.test_list
1293 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001294
Jon Salz0697cbf2012-07-04 15:14:04 +08001295 def show_review_information(self):
1296 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001297
Jon Salz0697cbf2012-07-04 15:14:04 +08001298 The information screene is rendered by main UI program (ui.py), so in
1299 goofy we only need to kill all active tests, set them as untested, and
1300 clear remaining tests.
1301 '''
1302 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001303 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001304
Jon Salz0697cbf2012-07-04 15:14:04 +08001305 def handle_switch_test(self, event):
1306 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001307
Jon Salz0697cbf2012-07-04 15:14:04 +08001308 @param event: The SWITCH_TEST event.
1309 '''
1310 test = self.test_list.lookup_path(event.path)
1311 if not test:
1312 logging.error('Unknown test %r', event.key)
1313 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001314
Jon Salz0697cbf2012-07-04 15:14:04 +08001315 invoc = self.invocations.get(test)
1316 if invoc and test.backgroundable:
1317 # Already running: just bring to the front if it
1318 # has a UI.
1319 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001320 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001321 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001322
Jon Salz0697cbf2012-07-04 15:14:04 +08001323 self.abort_active_tests()
1324 for t in test.walk():
1325 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001326
Jon Salz0697cbf2012-07-04 15:14:04 +08001327 if self.test_list.options.auto_run_on_keypress:
1328 self.auto_run(starting_at=test)
1329 else:
1330 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001331
Jon Salz0697cbf2012-07-04 15:14:04 +08001332 def wait(self):
1333 '''Waits for all pending invocations.
1334
1335 Useful for testing.
1336 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001337 while self.invocations:
1338 for k, v in self.invocations.iteritems():
1339 logging.info('Waiting for %s to complete...', k)
1340 v.thread.join()
1341 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001342
1343 def check_exceptions(self):
1344 '''Raises an error if any exceptions have occurred in
1345 invocation threads.'''
1346 if self.exceptions:
1347 raise RuntimeError('Exception in invocation thread: %r' %
1348 self.exceptions)
1349
1350 def record_exception(self, msg):
1351 '''Records an exception in an invocation thread.
1352
1353 An exception with the given message will be rethrown when
1354 Goofy is destroyed.'''
1355 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001356
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001357
1358if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001359 goofy = Goofy()
1360 try:
1361 goofy.main()
1362 finally:
1363 goofy.destroy()