blob: e559d79e16a85e839ddc6e6b09b933bc755132dd [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 Salz0405ab52012-03-16 15:26:52 +080015import sys
Jon Salz0405ab52012-03-16 15:26:52 +080016import threading
17import time
18import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080019import uuid
Hung-Te Linf2f78f72012-02-08 19:27:11 +080020from collections import deque
21from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080022
Jon Salz0697cbf2012-07-04 15:14:04 +080023import factory_common # pylint: disable=W0611
Jon Salz83591782012-06-26 11:09:58 +080024from cros.factory.goofy.prespawner import Prespawner
25from cros.factory.test import factory
26from cros.factory.test import state
27from cros.factory.test.factory import TestState
28from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080029from cros.factory.goofy.event_log_watcher import EventLogWatcher
30from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080031from cros.factory.test import utils
32from cros.factory.test.event import Event
33from cros.factory.test.event import EventClient
34from cros.factory.test.event import EventServer
Jon Salzee85d522012-07-17 14:34:46 +080035from cros.factory import event_log
Jon Salz83591782012-06-26 11:09:58 +080036from cros.factory.event_log import EventLog
37from cros.factory.goofy.invocation import TestInvocation
Jon Salz16d10542012-07-23 12:18:45 +080038from cros.factory.goofy.goofy_rpc import GoofyRPC
Jon Salz83591782012-06-26 11:09:58 +080039from cros.factory.goofy import system
40from cros.factory.goofy import test_environment
Jon Salz8fa8e832012-07-13 19:04:09 +080041from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080042from cros.factory.goofy.web_socket_manager import WebSocketManager
Vic Yang4953fc12012-07-26 16:19:53 +080043from cros.factory.goofy.charge_manager import ChargeManager
Jon Salz78c32392012-07-25 14:18:29 +080044from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080045
46
Jon Salz2f757d42012-06-27 17:06:42 +080047DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
48CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080049HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
50
Jon Salz8796e362012-05-24 11:39:09 +080051# File that suppresses reboot if present (e.g., for development).
52NO_REBOOT_FILE = '/var/log/factory.noreboot'
53
Jon Salz5c344f62012-07-13 14:31:16 +080054# Value for tests_after_shutdown that forces auto-run (e.g., after
55# a factory update, when the available set of tests might change).
56FORCE_AUTO_RUN = 'force_auto_run'
57
cychiang21886742012-07-05 15:16:32 +080058RUN_QUEUE_TIMEOUT_SECS = 10
59
Jon Salz758e6cc2012-04-03 15:47:07 +080060GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
61You are running Goofy inside the chroot. Autotests are not supported.
62
63To use Goofy in the chroot, first install an Xvnc server:
64
Jon Salz0697cbf2012-07-04 15:14:04 +080065 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080066
67...and then start a VNC X server outside the chroot:
68
Jon Salz0697cbf2012-07-04 15:14:04 +080069 vncserver :10 &
70 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080071
72...and run Goofy as follows:
73
Jon Salz0697cbf2012-07-04 15:14:04 +080074 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080075''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080076suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080077
78def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080079 '''
80 Returns the HWID config tag, or an empty string if none can be found.
81 '''
82 if 'CROS_HWID' in os.environ:
83 return os.environ['CROS_HWID']
84 if os.path.exists(HWID_CFG_PATH):
85 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
86 return hwid_cfg_handle.read().strip()
87 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080088
89
90def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080091 '''
92 Returns the path to the active test list, based on the HWID config tag.
93 '''
94 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +080095
Jon Salz0697cbf2012-07-04 15:14:04 +080096 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
Jon Salz2f757d42012-06-27 17:06:42 +080097
Jon Salz0697cbf2012-07-04 15:14:04 +080098 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
99 search_files = ['test_list', 'test_list.all']
100 if hwid_cfg:
101 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800102
Jon Salz0697cbf2012-07-04 15:14:04 +0800103 for d in search_dirs:
104 for f in search_files:
105 test_list = os.path.join(d, f)
106 if os.path.exists(test_list):
107 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800108
Jon Salz0697cbf2012-07-04 15:14:04 +0800109 logging.warn('Cannot find test lists named any of %s in any of %s',
110 search_files, search_dirs)
111 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800112
Jon Salz73e0fd02012-04-04 11:46:38 +0800113_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800114
115class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800116 '''
117 The main factory flow.
118
119 Note that all methods in this class must be invoked from the main
120 (event) thread. Other threads, such as callbacks and TestInvocation
121 methods, should instead post events on the run queue.
122
123 TODO: Unit tests. (chrome-os-partner:7409)
124
125 Properties:
126 uuid: A unique UUID for this invocation of Goofy.
127 state_instance: An instance of FactoryState.
128 state_server: The FactoryState XML/RPC server.
129 state_server_thread: A thread running state_server.
130 event_server: The EventServer socket server.
131 event_server_thread: A thread running event_server.
132 event_client: A client to the event server.
133 connection_manager: The connection_manager object.
134 network_enabled: Whether the connection_manager is currently
135 enabling connections.
136 ui_process: The factory ui process object.
137 run_queue: A queue of callbacks to invoke from the main thread.
138 invocations: A map from FactoryTest objects to the corresponding
139 TestInvocations objects representing active tests.
140 tests_to_run: A deque of tests that should be run when the current
141 test(s) complete.
142 options: Command-line options.
143 args: Command-line args.
144 test_list: The test list.
145 event_handlers: Map of Event.Type to the method used to handle that
146 event. If the method has an 'event' argument, the event is passed
147 to the handler.
148 exceptions: Exceptions encountered in invocation threads.
149 '''
150 def __init__(self):
151 self.uuid = str(uuid.uuid4())
152 self.state_instance = None
153 self.state_server = None
154 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800155 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800156 self.event_server = None
157 self.event_server_thread = None
158 self.event_client = None
159 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800160 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800161 self.time_sanitizer = None
162 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800163 self.log_watcher = None
164 self.network_enabled = True
165 self.event_log = None
166 self.prespawner = None
167 self.ui_process = None
168 self.run_queue = Queue.Queue()
169 self.invocations = {}
170 self.tests_to_run = deque()
171 self.visible_test = None
172 self.chrome = None
173
174 self.options = None
175 self.args = None
176 self.test_list = None
177 self.on_ui_startup = []
178 self.env = None
179 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800180 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800181 self.last_sync_time = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800182
Jon Salz85a39882012-07-05 16:45:04 +0800183 def test_or_root(event, parent_or_group=True):
184 '''Returns the test affected by a particular event.
185
186 Args:
187 event: The event containing an optional 'path' attribute.
188 parent_on_group: If True, returns the top-level parent for a test (the
189 root node of the tests that need to be run together if the given test
190 path is to be run).
191 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800192 try:
193 path = event.path
194 except AttributeError:
195 path = None
196
197 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800198 test = self.test_list.lookup_path(path)
199 if parent_or_group:
200 test = test.get_top_level_parent_or_group()
201 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800202 else:
203 return self.test_list
204
205 self.event_handlers = {
206 Event.Type.SWITCH_TEST: self.handle_switch_test,
207 Event.Type.SHOW_NEXT_ACTIVE_TEST:
208 lambda event: self.show_next_active_test(),
209 Event.Type.RESTART_TESTS:
210 lambda event: self.restart_tests(root=test_or_root(event)),
211 Event.Type.AUTO_RUN:
212 lambda event: self.auto_run(root=test_or_root(event)),
213 Event.Type.RE_RUN_FAILED:
214 lambda event: self.re_run_failed(root=test_or_root(event)),
215 Event.Type.RUN_TESTS_WITH_STATUS:
216 lambda event: self.run_tests_with_status(
217 event.status,
218 root=test_or_root(event)),
219 Event.Type.REVIEW:
220 lambda event: self.show_review_information(),
221 Event.Type.UPDATE_SYSTEM_INFO:
222 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800223 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800224 lambda event: self.stop(root=test_or_root(event, False),
225 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800226 Event.Type.SET_VISIBLE_TEST:
227 lambda event: self.set_visible_test(
228 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800229 }
230
231 self.exceptions = []
232 self.web_socket_manager = None
233
234 def destroy(self):
235 if self.chrome:
236 self.chrome.kill()
237 self.chrome = None
238 if self.ui_process:
239 utils.kill_process_tree(self.ui_process, 'ui')
240 self.ui_process = None
241 if self.web_socket_manager:
242 logging.info('Stopping web sockets')
243 self.web_socket_manager.close()
244 self.web_socket_manager = None
245 if self.state_server_thread:
246 logging.info('Stopping state server')
247 self.state_server.shutdown()
248 self.state_server_thread.join()
249 self.state_server.server_close()
250 self.state_server_thread = None
251 if self.state_instance:
252 self.state_instance.close()
253 if self.event_server_thread:
254 logging.info('Stopping event server')
255 self.event_server.shutdown() # pylint: disable=E1101
256 self.event_server_thread.join()
257 self.event_server.server_close()
258 self.event_server_thread = None
259 if self.log_watcher:
260 if self.log_watcher.IsThreadStarted():
261 self.log_watcher.StopWatchThread()
262 self.log_watcher = None
263 if self.prespawner:
264 logging.info('Stopping prespawner')
265 self.prespawner.stop()
266 self.prespawner = None
267 if self.event_client:
268 logging.info('Closing event client')
269 self.event_client.close()
270 self.event_client = None
271 if self.event_log:
272 self.event_log.Close()
273 self.event_log = None
274 self.check_exceptions()
275 logging.info('Done destroying Goofy')
276
277 def start_state_server(self):
278 self.state_instance, self.state_server = (
279 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800280 self.goofy_rpc = GoofyRPC(self)
281 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800282 logging.info('Starting state server')
283 self.state_server_thread = threading.Thread(
284 target=self.state_server.serve_forever,
285 name='StateServer')
286 self.state_server_thread.start()
287
288 def start_event_server(self):
289 self.event_server = EventServer()
290 logging.info('Starting factory event server')
291 self.event_server_thread = threading.Thread(
292 target=self.event_server.serve_forever,
293 name='EventServer') # pylint: disable=E1101
294 self.event_server_thread.start()
295
296 self.event_client = EventClient(
297 callback=self.handle_event, event_loop=self.run_queue)
298
299 self.web_socket_manager = WebSocketManager(self.uuid)
300 self.state_server.add_handler("/event",
301 self.web_socket_manager.handle_web_socket)
302
303 def start_ui(self):
304 ui_proc_args = [
305 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
306 self.options.test_list]
307 if self.options.verbose:
308 ui_proc_args.append('-v')
309 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800310 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800311 logging.info('Waiting for UI to come up...')
312 self.event_client.wait(
313 lambda event: event.type == Event.Type.UI_READY)
314 logging.info('UI has started')
315
316 def set_visible_test(self, test):
317 if self.visible_test == test:
318 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800319 if test and not test.has_ui:
320 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800321
322 if test:
323 test.update_state(visible=True)
324 if self.visible_test:
325 self.visible_test.update_state(visible=False)
326 self.visible_test = test
327
328 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800329 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800330 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800331
Jon Salz0697cbf2012-07-04 15:14:04 +0800332 @param test: The ShutdownStep.
333 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800334 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800335 test_state = test.update_state(increment_shutdown_count=1)
336 logging.info('Detected shutdown (%d of %d)',
337 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800338
Jon Salz0697cbf2012-07-04 15:14:04 +0800339 def log_and_update_state(status, error_msg, **kw):
340 self.event_log.Log('rebooted',
341 status=status, error_msg=error_msg, **kw)
342 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800343
Jon Salz0697cbf2012-07-04 15:14:04 +0800344 if not self.last_shutdown_time:
345 log_and_update_state(status=TestState.FAILED,
346 error_msg='Unable to read shutdown_time')
347 return
Jon Salz258a40c2012-04-19 12:34:01 +0800348
Jon Salz0697cbf2012-07-04 15:14:04 +0800349 now = time.time()
350 logging.info('%.03f s passed since reboot',
351 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800352
Jon Salz0697cbf2012-07-04 15:14:04 +0800353 if self.last_shutdown_time > now:
354 test.update_state(status=TestState.FAILED,
355 error_msg='Time moved backward during reboot')
356 elif (isinstance(test, factory.RebootStep) and
357 self.test_list.options.max_reboot_time_secs and
358 (now - self.last_shutdown_time >
359 self.test_list.options.max_reboot_time_secs)):
360 # A reboot took too long; fail. (We don't check this for
361 # HaltSteps, because the machine could be halted for a
362 # very long time, and even unplugged with battery backup,
363 # thus hosing the clock.)
364 log_and_update_state(
365 status=TestState.FAILED,
366 error_msg=('More than %d s elapsed during reboot '
367 '(%.03f s, from %s to %s)' % (
368 self.test_list.options.max_reboot_time_secs,
369 now - self.last_shutdown_time,
370 utils.TimeString(self.last_shutdown_time),
371 utils.TimeString(now))),
372 duration=(now-self.last_shutdown_time))
373 elif test_state.shutdown_count == test.iterations:
374 # Good!
375 log_and_update_state(status=TestState.PASSED,
376 duration=(now - self.last_shutdown_time),
377 error_msg='')
378 elif test_state.shutdown_count > test.iterations:
379 # Shut down too many times
380 log_and_update_state(status=TestState.FAILED,
381 error_msg='Too many shutdowns')
382 elif utils.are_shift_keys_depressed():
383 logging.info('Shift keys are depressed; cancelling restarts')
384 # Abort shutdown
385 log_and_update_state(
386 status=TestState.FAILED,
387 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800388 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800389 else:
390 def handler():
391 if self._prompt_cancel_shutdown(
392 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800393 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800394 log_and_update_state(
395 status=TestState.FAILED,
396 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800397 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800398 return
Jon Salz0405ab52012-03-16 15:26:52 +0800399
Jon Salz0697cbf2012-07-04 15:14:04 +0800400 # Time to shutdown again
401 log_and_update_state(
402 status=TestState.ACTIVE,
403 error_msg='',
404 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800405
Jon Salz0697cbf2012-07-04 15:14:04 +0800406 self.event_log.Log('shutdown', operation='reboot')
407 self.state_instance.set_shared_data('shutdown_time',
408 time.time())
409 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800410
Jon Salz0697cbf2012-07-04 15:14:04 +0800411 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800412
Jon Salz0697cbf2012-07-04 15:14:04 +0800413 def _prompt_cancel_shutdown(self, test, iteration):
414 if self.options.ui != 'chrome':
415 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800416
Jon Salz0697cbf2012-07-04 15:14:04 +0800417 pending_shutdown_data = {
418 'delay_secs': test.delay_secs,
419 'time': time.time() + test.delay_secs,
420 'operation': test.operation,
421 'iteration': iteration,
422 'iterations': test.iterations,
423 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800424
Jon Salz0697cbf2012-07-04 15:14:04 +0800425 # Create a new (threaded) event client since we
426 # don't want to use the event loop for this.
427 with EventClient() as event_client:
428 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
429 **pending_shutdown_data))
430 aborted = event_client.wait(
431 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
432 timeout=test.delay_secs) is not None
433 if aborted:
434 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
435 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800436
Jon Salz0697cbf2012-07-04 15:14:04 +0800437 def init_states(self):
438 '''
439 Initializes all states on startup.
440 '''
441 for test in self.test_list.get_all_tests():
442 # Make sure the state server knows about all the tests,
443 # defaulting to an untested state.
444 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800445
Jon Salz0697cbf2012-07-04 15:14:04 +0800446 var_log_messages = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800447
Jon Salz0697cbf2012-07-04 15:14:04 +0800448 # Any 'active' tests should be marked as failed now.
449 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800450 if not test.is_leaf():
451 # Don't bother with parents; they will be updated when their
452 # children are updated.
453 continue
454
Jon Salz0697cbf2012-07-04 15:14:04 +0800455 test_state = test.get_state()
456 if test_state.status != TestState.ACTIVE:
457 continue
458 if isinstance(test, factory.ShutdownStep):
459 # Shutdown while the test was active - that's good.
460 self.handle_shutdown_complete(test, test_state)
461 else:
462 # Unexpected shutdown. Grab /var/log/messages for context.
463 if var_log_messages is None:
464 try:
465 var_log_messages = (
466 utils.var_log_messages_before_reboot())
467 # Write it to the log, to make it easier to
468 # correlate with /var/log/messages.
469 logging.info(
470 'Unexpected shutdown. '
471 'Tail of /var/log/messages before last reboot:\n'
472 '%s', ('\n'.join(
473 ' ' + x for x in var_log_messages)))
474 except: # pylint: disable=W0702
475 logging.exception('Unable to grok /var/log/messages')
476 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800477
Jon Salz0697cbf2012-07-04 15:14:04 +0800478 error_msg = 'Unexpected shutdown while test was running'
479 self.event_log.Log('end_test',
480 path=test.path,
481 status=TestState.FAILED,
482 invocation=test.get_state().invocation,
483 error_msg=error_msg,
484 var_log_messages='\n'.join(var_log_messages))
485 test.update_state(
486 status=TestState.FAILED,
487 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800488
Jon Salz50efe942012-07-26 11:54:10 +0800489 if not test.never_fails:
490 # For "never_fails" tests (such as "Start"), don't cancel
491 # pending tests, since reboot is expected.
492 factory.console.info('Unexpected shutdown while test %s '
493 'running; cancelling any pending tests',
494 test.path)
495 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800496
Jon Salz0697cbf2012-07-04 15:14:04 +0800497 def show_next_active_test(self):
498 '''
499 Rotates to the next visible active test.
500 '''
501 self.reap_completed_tests()
502 active_tests = [
503 t for t in self.test_list.walk()
504 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
505 if not active_tests:
506 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800507
Jon Salz0697cbf2012-07-04 15:14:04 +0800508 try:
509 next_test = active_tests[
510 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
511 except ValueError: # visible_test not present in active_tests
512 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800513
Jon Salz0697cbf2012-07-04 15:14:04 +0800514 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800515
Jon Salz0697cbf2012-07-04 15:14:04 +0800516 def handle_event(self, event):
517 '''
518 Handles an event from the event server.
519 '''
520 handler = self.event_handlers.get(event.type)
521 if handler:
522 handler(event)
523 else:
524 # We don't register handlers for all event types - just ignore
525 # this event.
526 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800527
Jon Salz0697cbf2012-07-04 15:14:04 +0800528 def run_next_test(self):
529 '''
530 Runs the next eligible test (or tests) in self.tests_to_run.
531 '''
532 self.reap_completed_tests()
533 while self.tests_to_run:
534 logging.debug('Tests to run: %s',
535 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800536
Jon Salz0697cbf2012-07-04 15:14:04 +0800537 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800538
Jon Salz0697cbf2012-07-04 15:14:04 +0800539 if test in self.invocations:
540 logging.info('Next test %s is already running', test.path)
541 self.tests_to_run.popleft()
542 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800543
Jon Salza1412922012-07-23 16:04:17 +0800544 for requirement in test.require_run:
545 for i in requirement.test.walk():
546 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800547 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800548 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800549 return
550
Jon Salz0697cbf2012-07-04 15:14:04 +0800551 if self.invocations and not (test.backgroundable and all(
552 [x.backgroundable for x in self.invocations])):
553 logging.debug('Waiting for non-backgroundable tests to '
554 'complete before running %s', test.path)
555 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800556
Jon Salz0697cbf2012-07-04 15:14:04 +0800557 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800558
Jon Salz304a75d2012-07-06 11:14:15 +0800559 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800560 for requirement in test.require_run:
561 for i in requirement.test.walk():
562 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800563 # We've hit this test itself; stop checking
564 break
Jon Salza1412922012-07-23 16:04:17 +0800565 if ((i.get_state().status == TestState.UNTESTED) or
566 (requirement.passed and i.get_state().status !=
567 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800568 # Found an untested test; move on to the next
569 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800570 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800571 break
572
573 if untested:
574 untested_paths = ', '.join(sorted([x.path for x in untested]))
575 if self.state_instance.get_shared_data('engineering_mode',
576 optional=True):
577 # In engineering mode, we'll let it go.
578 factory.console.warn('In engineering mode; running '
579 '%s even though required tests '
580 '[%s] have not completed',
581 test.path, untested_paths)
582 else:
583 # Not in engineering mode; mark it failed.
584 error_msg = ('Required tests [%s] have not been run yet'
585 % untested_paths)
586 factory.console.error('Not running %s: %s',
587 test.path, error_msg)
588 test.update_state(status=TestState.FAILED,
589 error_msg=error_msg)
590 continue
591
Jon Salz0697cbf2012-07-04 15:14:04 +0800592 if isinstance(test, factory.ShutdownStep):
593 if os.path.exists(NO_REBOOT_FILE):
594 test.update_state(
595 status=TestState.FAILED, increment_count=1,
596 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800597 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800598 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800599
Jon Salz0697cbf2012-07-04 15:14:04 +0800600 test.update_state(status=TestState.ACTIVE, increment_count=1,
601 error_msg='', shutdown_count=0)
602 if self._prompt_cancel_shutdown(test, 1):
603 self.event_log.Log('reboot_cancelled')
604 test.update_state(
605 status=TestState.FAILED, increment_count=1,
606 error_msg='Shutdown aborted by operator',
607 shutdown_count=0)
608 return
Jon Salz2f757d42012-06-27 17:06:42 +0800609
Jon Salz0697cbf2012-07-04 15:14:04 +0800610 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800611 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800612 'tests_after_shutdown',
613 [t.path for t in self.tests_to_run])
614 # Save shutdown time
615 self.state_instance.set_shared_data('shutdown_time',
616 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800617
Jon Salz0697cbf2012-07-04 15:14:04 +0800618 with self.env.lock:
619 self.event_log.Log('shutdown', operation=test.operation)
620 shutdown_result = self.env.shutdown(test.operation)
621 if shutdown_result:
622 # That's all, folks!
623 self.run_queue.put(None)
624 return
625 else:
626 # Just pass (e.g., in the chroot).
627 test.update_state(status=TestState.PASSED)
628 self.state_instance.set_shared_data(
629 'tests_after_shutdown', None)
630 # Send event with no fields to indicate that there is no
631 # longer a pending shutdown.
632 self.event_client.post_event(Event(
633 Event.Type.PENDING_SHUTDOWN))
634 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800635
Jon Salz1acc8742012-07-17 17:45:55 +0800636 self._run_test(test, test.iterations)
637
638 def _run_test(self, test, iterations_left=None):
639 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
640 new_state = test.update_state(
641 status=TestState.ACTIVE, increment_count=1, error_msg='',
642 invocation=invoc.uuid, iterations_left=iterations_left)
643 invoc.count = new_state.count
644
645 self.invocations[test] = invoc
646 if self.visible_test is None and test.has_ui:
647 self.set_visible_test(test)
648 self.check_connection_manager()
649 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800650
Jon Salz0697cbf2012-07-04 15:14:04 +0800651 def check_connection_manager(self):
652 exclusive_tests = [
653 test.path
654 for test in self.invocations
655 if test.is_exclusive(
656 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
657 if exclusive_tests:
658 # Make sure networking is disabled.
659 if self.network_enabled:
660 logging.info('Disabling network, as requested by %s',
661 exclusive_tests)
662 self.connection_manager.DisableNetworking()
663 self.network_enabled = False
664 else:
665 # Make sure networking is enabled.
666 if not self.network_enabled:
667 logging.info('Re-enabling network')
668 self.connection_manager.EnableNetworking()
669 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800670
cychiang21886742012-07-05 15:16:32 +0800671 def check_for_updates(self):
672 '''
673 Schedules an asynchronous check for updates if necessary.
674 '''
675 if not self.test_list.options.update_period_secs:
676 # Not enabled.
677 return
678
679 now = time.time()
680 if self.last_update_check and (
681 now - self.last_update_check <
682 self.test_list.options.update_period_secs):
683 # Not yet time for another check.
684 return
685
686 self.last_update_check = now
687
688 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
689 if reached_shopfloor:
690 new_update_md5sum = md5sum if needs_update else None
691 if system.SystemInfo.update_md5sum != new_update_md5sum:
692 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
693 system.SystemInfo.update_md5sum = new_update_md5sum
694 self.run_queue.put(self.update_system_info)
695
696 updater.CheckForUpdateAsync(
697 handle_check_for_update,
698 self.test_list.options.shopfloor_timeout_secs)
699
Jon Salza6711d72012-07-18 14:33:03 +0800700 def cancel_pending_tests(self):
701 '''Cancels any tests in the run queue.'''
702 self.run_tests([])
703
Jon Salz0697cbf2012-07-04 15:14:04 +0800704 def run_tests(self, subtrees, untested_only=False):
705 '''
706 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800707
Jon Salz0697cbf2012-07-04 15:14:04 +0800708 The tests are run in order unless one fails (then stops).
709 Backgroundable tests are run simultaneously; when a foreground test is
710 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800711
Jon Salz0697cbf2012-07-04 15:14:04 +0800712 @param subtrees: Node or nodes containing tests to run (may either be
713 a single test or a list). Duplicates will be ignored.
714 '''
715 if type(subtrees) != list:
716 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800717
Jon Salz0697cbf2012-07-04 15:14:04 +0800718 # Nodes we've seen so far, to avoid duplicates.
719 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800720
Jon Salz0697cbf2012-07-04 15:14:04 +0800721 self.tests_to_run = deque()
722 for subtree in subtrees:
723 for test in subtree.walk():
724 if test in seen:
725 continue
726 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800727
Jon Salz0697cbf2012-07-04 15:14:04 +0800728 if not test.is_leaf():
729 continue
730 if (untested_only and
731 test.get_state().status != TestState.UNTESTED):
732 continue
733 self.tests_to_run.append(test)
734 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800735
Jon Salz0697cbf2012-07-04 15:14:04 +0800736 def reap_completed_tests(self):
737 '''
738 Removes completed tests from the set of active tests.
739
740 Also updates the visible test if it was reaped.
741 '''
742 for t, v in dict(self.invocations).iteritems():
743 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800744 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800745 del self.invocations[t]
746
Jon Salz1acc8742012-07-17 17:45:55 +0800747 if new_state.iterations_left and new_state.status == TestState.PASSED:
748 # Play it again, Sam!
749 self._run_test(t)
750
Jon Salz0697cbf2012-07-04 15:14:04 +0800751 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800752 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800753 self.set_visible_test(None)
754 # Make the first running test, if any, the visible test
755 for t in self.test_list.walk():
756 if t in self.invocations:
757 self.set_visible_test(t)
758 break
759
Jon Salz85a39882012-07-05 16:45:04 +0800760 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800761 '''
762 Kills and waits for all active tests.
763
Jon Salz85a39882012-07-05 16:45:04 +0800764 Args:
765 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800766 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800767 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800768 '''
769 self.reap_completed_tests()
770 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800771 if root and not test.has_ancestor(root):
772 continue
773
Jon Salz0697cbf2012-07-04 15:14:04 +0800774 factory.console.info('Killing active test %s...' % test.path)
775 invoc.abort_and_join()
776 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800777 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800778 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800779
Jon Salz0697cbf2012-07-04 15:14:04 +0800780 if not abort:
781 test.update_state(status=TestState.UNTESTED)
782 self.reap_completed_tests()
783
Jon Salz85a39882012-07-05 16:45:04 +0800784 def stop(self, root=None, fail=False):
785 self.kill_active_tests(fail, root)
786 # Remove any tests in the run queue under the root.
787 self.tests_to_run = deque([x for x in self.tests_to_run
788 if root and not x.has_ancestor(root)])
789 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800790
791 def abort_active_tests(self):
792 self.kill_active_tests(True)
793
794 def main(self):
795 try:
796 self.init()
797 self.event_log.Log('goofy_init',
798 success=True)
799 except:
800 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800801 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800802 self.event_log.Log('goofy_init',
803 success=False,
804 trace=traceback.format_exc())
805 except: # pylint: disable=W0702
806 pass
807 raise
808
809 self.run()
810
811 def update_system_info(self):
812 '''Updates system info.'''
813 system_info = system.SystemInfo()
814 self.state_instance.set_shared_data('system_info', system_info.__dict__)
815 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
816 system_info=system_info.__dict__))
817 logging.info('System info: %r', system_info.__dict__)
818
Jon Salzeb42f0d2012-07-27 19:14:04 +0800819 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
820 '''Commences updating factory software.
821
822 Args:
823 auto_run_on_restart: Auto-run when the machine comes back up.
824 post_update_hook: Code to call after update but immediately before
825 restart.
826
827 Returns:
828 Never if the update was successful (we just reboot).
829 False if the update was unnecessary (no update available).
830 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800831 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800832 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800833
Jon Salz5c344f62012-07-13 14:31:16 +0800834 def pre_update_hook():
835 if auto_run_on_restart:
836 self.state_instance.set_shared_data('tests_after_shutdown',
837 FORCE_AUTO_RUN)
838 self.state_instance.close()
839
Jon Salzeb42f0d2012-07-27 19:14:04 +0800840 if updater.TryUpdate(pre_update_hook=pre_update_hook):
841 if post_update_hook:
842 post_update_hook()
843 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800844
845 def init(self, args=None, env=None):
846 '''Initializes Goofy.
847
848 Args:
849 args: A list of command-line arguments. Uses sys.argv if
850 args is None.
851 env: An Environment instance to use (or None to choose
852 FakeChrootEnvironment or DUTEnvironment as appropriate).
853 '''
854 parser = OptionParser()
855 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800856 action='store_true',
857 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800858 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800859 metavar='FILE',
860 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800861 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800862 action='store_true',
863 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800864 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800865 choices=['none', 'gtk', 'chrome'],
866 default=('chrome' if utils.in_chroot() else 'gtk'),
867 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800868 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800869 type='int', default=1,
870 help=('Factor by which to scale UI '
871 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800872 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800873 metavar='FILE',
874 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800875 (self.options, self.args) = parser.parse_args(args)
876
Jon Salz46b89562012-07-05 11:49:22 +0800877 # Make sure factory directories exist.
878 factory.get_log_root()
879 factory.get_state_root()
880 factory.get_test_data_root()
881
Jon Salz0697cbf2012-07-04 15:14:04 +0800882 global _inited_logging # pylint: disable=W0603
883 if not _inited_logging:
884 factory.init_logging('goofy', verbose=self.options.verbose)
885 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800886
Jon Salzee85d522012-07-17 14:34:46 +0800887 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +0800888 self.event_log = EventLog('goofy')
889
890 if (not suppress_chroot_warning and
891 factory.in_chroot() and
892 self.options.ui == 'gtk' and
893 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
894 # That's not going to work! Tell the user how to run
895 # this way.
896 logging.warn(GOOFY_IN_CHROOT_WARNING)
897 time.sleep(1)
898
899 if env:
900 self.env = env
901 elif factory.in_chroot():
902 self.env = test_environment.FakeChrootEnvironment()
903 logging.warn(
904 'Using chroot environment: will not actually run autotests')
905 else:
906 self.env = test_environment.DUTEnvironment()
907 self.env.goofy = self
908
909 if self.options.restart:
910 state.clear_state()
911
912 if self.options.print_test_list:
Jon Salzeb42f0d2012-07-27 19:14:04 +0800913 print factory.read_test_list(
914 self.options.print_test_list).__repr__(recursive=True)
Jon Salz0697cbf2012-07-04 15:14:04 +0800915 return
916
917 if self.options.ui_scale_factor != 1 and utils.in_qemu():
918 logging.warn(
919 'In QEMU; ignoring ui_scale_factor argument')
920 self.options.ui_scale_factor = 1
921
922 logging.info('Started')
923
924 self.start_state_server()
925 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
926 self.state_instance.set_shared_data('ui_scale_factor',
927 self.options.ui_scale_factor)
928 self.last_shutdown_time = (
929 self.state_instance.get_shared_data('shutdown_time', optional=True))
930 self.state_instance.del_shared_data('shutdown_time', optional=True)
931
932 if not self.options.test_list:
933 self.options.test_list = find_test_list()
934 if not self.options.test_list:
935 logging.error('No test list. Aborting.')
936 sys.exit(1)
937 logging.info('Using test list %s', self.options.test_list)
938
939 self.test_list = factory.read_test_list(
940 self.options.test_list,
Jon Salzeb42f0d2012-07-27 19:14:04 +0800941 self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800942 if not self.state_instance.has_shared_data('ui_lang'):
943 self.state_instance.set_shared_data('ui_lang',
944 self.test_list.options.ui_lang)
945 self.state_instance.set_shared_data(
946 'test_list_options',
947 self.test_list.options.__dict__)
948 self.state_instance.test_list = self.test_list
949
Jon Salz8fa8e832012-07-13 19:04:09 +0800950 if self.test_list.options.time_sanitizer:
951 self.time_sanitizer = time_sanitizer.TimeSanitizer(
952 base_time=time_sanitizer.GetBaseTimeFromFile(
953 # lsb-factory is written by the factory install shim during
954 # installation, so it should have a good time obtained from
955 # the mini-Omaha server.
956 '/usr/local/etc/lsb-factory'))
957 self.time_sanitizer.RunOnce()
958
Jon Salz0697cbf2012-07-04 15:14:04 +0800959 self.init_states()
960 self.start_event_server()
961 self.connection_manager = self.env.create_connection_manager(
962 self.test_list.options.wlans)
963 # Note that we create a log watcher even if
964 # sync_event_log_period_secs isn't set (no background
965 # syncing), since we may use it to flush event logs as well.
966 self.log_watcher = EventLogWatcher(
967 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +0800968 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +0800969 if self.test_list.options.sync_event_log_period_secs:
970 self.log_watcher.StartWatchThread()
971
972 self.update_system_info()
973
Vic Yang4953fc12012-07-26 16:19:53 +0800974 assert ((self.test_list.options.min_charge_pct is None) ==
975 (self.test_list.options.max_charge_pct is None))
976 if (self.test_list.options.min_charge_pct and
977 self.test_list.options.max_charge_pct):
978 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
979 self.test_list.options.max_charge_pct)
980
Jon Salz0697cbf2012-07-04 15:14:04 +0800981 os.environ['CROS_FACTORY'] = '1'
982 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
983
984 # Set CROS_UI since some behaviors in ui.py depend on the
985 # particular UI in use. TODO(jsalz): Remove this (and all
986 # places it is used) when the GTK UI is removed.
987 os.environ['CROS_UI'] = self.options.ui
988
989 if self.options.ui == 'chrome':
990 self.env.launch_chrome()
991 logging.info('Waiting for a web socket connection')
992 self.web_socket_manager.wait()
993
994 # Wait for the test widget size to be set; this is done in
995 # an asynchronous RPC so there is a small chance that the
996 # web socket might be opened first.
997 for _ in range(100): # 10 s
998 try:
999 if self.state_instance.get_shared_data('test_widget_size'):
1000 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001001 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001002 pass # Retry
1003 time.sleep(0.1) # 100 ms
1004 else:
1005 logging.warn('Never received test_widget_size from UI')
1006 elif self.options.ui == 'gtk':
1007 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001008
Jon Salz0697cbf2012-07-04 15:14:04 +08001009 def state_change_callback(test, test_state):
1010 self.event_client.post_event(
1011 Event(Event.Type.STATE_CHANGE,
1012 path=test.path, state=test_state))
1013 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001014
Jon Salza6711d72012-07-18 14:33:03 +08001015 for handler in self.on_ui_startup:
1016 handler()
1017
1018 self.prespawner = Prespawner()
1019 self.prespawner.start()
1020
Jon Salz0697cbf2012-07-04 15:14:04 +08001021 try:
1022 tests_after_shutdown = self.state_instance.get_shared_data(
1023 'tests_after_shutdown')
1024 except KeyError:
1025 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001026
Jon Salz5c344f62012-07-13 14:31:16 +08001027 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1028 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001029 logging.info('Resuming tests after shutdown: %s',
1030 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001031 self.tests_to_run.extend(
1032 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1033 self.run_queue.put(self.run_next_test)
1034 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001035 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001036 self.run_queue.put(
1037 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001038 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001039
Jon Salz0697cbf2012-07-04 15:14:04 +08001040 def run(self):
1041 '''Runs Goofy.'''
1042 # Process events forever.
1043 while self.run_once(True):
1044 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001045
Jon Salz0697cbf2012-07-04 15:14:04 +08001046 def run_once(self, block=False):
1047 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001048
Jon Salz0697cbf2012-07-04 15:14:04 +08001049 Args:
1050 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001051
Jon Salz0697cbf2012-07-04 15:14:04 +08001052 Returns:
1053 True to keep going or False to shut down.
1054 '''
1055 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001056 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001057 # Nothing on the run queue.
1058 self._run_queue_idle()
1059 if block:
1060 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001061 try:
1062 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1063 except Queue.Empty:
1064 # Keep going (calling _run_queue_idle() again at the top of
1065 # the loop)
1066 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001067 # ...and grab anything else that showed up at the same
1068 # time.
1069 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001070 else:
1071 break
Jon Salz51528e12012-07-02 18:54:45 +08001072
Jon Salz0697cbf2012-07-04 15:14:04 +08001073 for event in events:
1074 if not event:
1075 # Shutdown request.
1076 self.run_queue.task_done()
1077 return False
Jon Salz51528e12012-07-02 18:54:45 +08001078
Jon Salz0697cbf2012-07-04 15:14:04 +08001079 try:
1080 event()
Jon Salz85a39882012-07-05 16:45:04 +08001081 except: # pylint: disable=W0702
1082 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001083 self.record_exception(traceback.format_exception_only(
1084 *sys.exc_info()[:2]))
1085 # But keep going
1086 finally:
1087 self.run_queue.task_done()
1088 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001089
Jon Salz8fa8e832012-07-13 19:04:09 +08001090 def sync_time_in_background(self):
1091 '''Attempts to sync time with the shopfloor server.'''
1092 if ((not self.test_list.options.sync_time_period_secs) or
1093 (not self.time_sanitizer) or
1094 self.time_synced or
1095 factory.in_chroot()):
1096 # Not enabled or already succeeded.
1097 return
1098
1099 now = time.time()
1100 if self.last_sync_time and (
1101 now - self.last_sync_time <
1102 self.test_list.options.sync_time_period_secs):
1103 # Not yet time for another check.
1104 return
1105 self.last_sync_time = now
1106
1107 def target():
1108 try:
1109 self.time_sanitizer.SyncWithShopfloor()
1110 self.time_synced = True
1111 except: # pylint: disable=W0702
1112 # Oh well. Log an error (but no trace)
1113 logging.info(
1114 'Unable to get time from shopfloor server: %s',
1115 utils.FormatExceptionOnly())
1116
1117 thread = threading.Thread(target=target)
1118 thread.daemon = True
1119 thread.start()
1120
Jon Salz0697cbf2012-07-04 15:14:04 +08001121 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001122 '''Invoked when the run queue has no events.
1123
1124 This method must not raise exception.
1125 '''
Jon Salz0697cbf2012-07-04 15:14:04 +08001126 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001127 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001128 self.sync_time_in_background()
Vic Yang4953fc12012-07-26 16:19:53 +08001129 if self.charge_manager:
1130 self.charge_manager.AdjustChargeState()
Jon Salz57717ca2012-04-04 16:47:25 +08001131
Jon Salz16d10542012-07-23 12:18:45 +08001132 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001133 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001134
Jon Salz0697cbf2012-07-04 15:14:04 +08001135 Attempts to upload the event logs to the shopfloor server.
1136 '''
1137 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1138 start_time = time.time()
1139 logging.info('Syncing %s', description)
1140 shopfloor_client = shopfloor.get_instance(
1141 detect=True,
1142 timeout=self.test_list.options.shopfloor_timeout_secs)
1143 shopfloor_client.UploadEvent(log_name, chunk)
1144 logging.info(
1145 'Successfully synced %s in %.03f s',
1146 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001147
Jon Salz0697cbf2012-07-04 15:14:04 +08001148 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1149 root=None):
1150 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001151
Jon Salz0697cbf2012-07-04 15:14:04 +08001152 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001153
Jon Salz0697cbf2012-07-04 15:14:04 +08001154 Args:
1155 starting_at: If provided, only auto-runs tests beginning with
1156 this test.
1157 '''
1158 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001159
Jon Salz0697cbf2012-07-04 15:14:04 +08001160 if starting_at:
1161 # Make sure they passed a test, not a string.
1162 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001163
Jon Salz0697cbf2012-07-04 15:14:04 +08001164 tests_to_reset = []
1165 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001166
Jon Salz0697cbf2012-07-04 15:14:04 +08001167 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001168
Jon Salz0697cbf2012-07-04 15:14:04 +08001169 for test in root.get_top_level_tests():
1170 if starting_at:
1171 if test == starting_at:
1172 # We've found starting_at; do auto-run on all
1173 # subsequent tests.
1174 found_starting_at = True
1175 if not found_starting_at:
1176 # Don't start this guy yet
1177 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001178
Jon Salz0697cbf2012-07-04 15:14:04 +08001179 status = test.get_state().status
1180 if status == TestState.ACTIVE or status in statuses_to_run:
1181 # Reset the test (later; we will need to abort
1182 # all active tests first).
1183 tests_to_reset.append(test)
1184 if status in statuses_to_run:
1185 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001186
Jon Salz0697cbf2012-07-04 15:14:04 +08001187 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001188
Jon Salz0697cbf2012-07-04 15:14:04 +08001189 # Reset all statuses of the tests to run (in case any tests were active;
1190 # we want them to be run again).
1191 for test_to_reset in tests_to_reset:
1192 for test in test_to_reset.walk():
1193 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001194
Jon Salz0697cbf2012-07-04 15:14:04 +08001195 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001196
Jon Salz0697cbf2012-07-04 15:14:04 +08001197 def restart_tests(self, root=None):
1198 '''Restarts all tests.'''
1199 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001200
Jon Salz0697cbf2012-07-04 15:14:04 +08001201 self.abort_active_tests()
1202 for test in root.walk():
1203 test.update_state(status=TestState.UNTESTED)
1204 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001205
Jon Salz0697cbf2012-07-04 15:14:04 +08001206 def auto_run(self, starting_at=None, root=None):
1207 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001208
Jon Salz0697cbf2012-07-04 15:14:04 +08001209 Args:
1210 starting_at: If provide, only auto-runs tests beginning with
1211 this test.
1212 '''
1213 root = root or self.test_list
1214 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1215 starting_at=starting_at,
1216 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001217
Jon Salz0697cbf2012-07-04 15:14:04 +08001218 def re_run_failed(self, root=None):
1219 '''Re-runs failed tests.'''
1220 root = root or self.test_list
1221 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001222
Jon Salz0697cbf2012-07-04 15:14:04 +08001223 def show_review_information(self):
1224 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001225
Jon Salz0697cbf2012-07-04 15:14:04 +08001226 The information screene is rendered by main UI program (ui.py), so in
1227 goofy we only need to kill all active tests, set them as untested, and
1228 clear remaining tests.
1229 '''
1230 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001231 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001232
Jon Salz0697cbf2012-07-04 15:14:04 +08001233 def handle_switch_test(self, event):
1234 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001235
Jon Salz0697cbf2012-07-04 15:14:04 +08001236 @param event: The SWITCH_TEST event.
1237 '''
1238 test = self.test_list.lookup_path(event.path)
1239 if not test:
1240 logging.error('Unknown test %r', event.key)
1241 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001242
Jon Salz0697cbf2012-07-04 15:14:04 +08001243 invoc = self.invocations.get(test)
1244 if invoc and test.backgroundable:
1245 # Already running: just bring to the front if it
1246 # has a UI.
1247 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001248 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001249 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001250
Jon Salz0697cbf2012-07-04 15:14:04 +08001251 self.abort_active_tests()
1252 for t in test.walk():
1253 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001254
Jon Salz0697cbf2012-07-04 15:14:04 +08001255 if self.test_list.options.auto_run_on_keypress:
1256 self.auto_run(starting_at=test)
1257 else:
1258 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001259
Jon Salz0697cbf2012-07-04 15:14:04 +08001260 def wait(self):
1261 '''Waits for all pending invocations.
1262
1263 Useful for testing.
1264 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001265 while self.invocations:
1266 for k, v in self.invocations.iteritems():
1267 logging.info('Waiting for %s to complete...', k)
1268 v.thread.join()
1269 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001270
1271 def check_exceptions(self):
1272 '''Raises an error if any exceptions have occurred in
1273 invocation threads.'''
1274 if self.exceptions:
1275 raise RuntimeError('Exception in invocation thread: %r' %
1276 self.exceptions)
1277
1278 def record_exception(self, msg):
1279 '''Records an exception in an invocation thread.
1280
1281 An exception with the given message will be rethrown when
1282 Goofy is destroyed.'''
1283 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001284
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001285
1286if __name__ == '__main__':
Jon Salz0697cbf2012-07-04 15:14:04 +08001287 Goofy().main()