blob: 0aea16932fb007a1a7c6c5ee6cf948d03b7210b0 [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
Jon Salzb22d1172012-08-06 10:38:57 +0800179 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800180 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800181 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800182 self.last_sync_time = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800183
Jon Salz85a39882012-07-05 16:45:04 +0800184 def test_or_root(event, parent_or_group=True):
185 '''Returns the test affected by a particular event.
186
187 Args:
188 event: The event containing an optional 'path' attribute.
189 parent_on_group: If True, returns the top-level parent for a test (the
190 root node of the tests that need to be run together if the given test
191 path is to be run).
192 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800193 try:
194 path = event.path
195 except AttributeError:
196 path = None
197
198 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800199 test = self.test_list.lookup_path(path)
200 if parent_or_group:
201 test = test.get_top_level_parent_or_group()
202 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800203 else:
204 return self.test_list
205
206 self.event_handlers = {
207 Event.Type.SWITCH_TEST: self.handle_switch_test,
208 Event.Type.SHOW_NEXT_ACTIVE_TEST:
209 lambda event: self.show_next_active_test(),
210 Event.Type.RESTART_TESTS:
211 lambda event: self.restart_tests(root=test_or_root(event)),
212 Event.Type.AUTO_RUN:
213 lambda event: self.auto_run(root=test_or_root(event)),
214 Event.Type.RE_RUN_FAILED:
215 lambda event: self.re_run_failed(root=test_or_root(event)),
216 Event.Type.RUN_TESTS_WITH_STATUS:
217 lambda event: self.run_tests_with_status(
218 event.status,
219 root=test_or_root(event)),
220 Event.Type.REVIEW:
221 lambda event: self.show_review_information(),
222 Event.Type.UPDATE_SYSTEM_INFO:
223 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800224 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800225 lambda event: self.stop(root=test_or_root(event, False),
226 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800227 Event.Type.SET_VISIBLE_TEST:
228 lambda event: self.set_visible_test(
229 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800230 }
231
232 self.exceptions = []
233 self.web_socket_manager = None
234
235 def destroy(self):
236 if self.chrome:
237 self.chrome.kill()
238 self.chrome = None
239 if self.ui_process:
240 utils.kill_process_tree(self.ui_process, 'ui')
241 self.ui_process = None
242 if self.web_socket_manager:
243 logging.info('Stopping web sockets')
244 self.web_socket_manager.close()
245 self.web_socket_manager = None
246 if self.state_server_thread:
247 logging.info('Stopping state server')
248 self.state_server.shutdown()
249 self.state_server_thread.join()
250 self.state_server.server_close()
251 self.state_server_thread = None
252 if self.state_instance:
253 self.state_instance.close()
254 if self.event_server_thread:
255 logging.info('Stopping event server')
256 self.event_server.shutdown() # pylint: disable=E1101
257 self.event_server_thread.join()
258 self.event_server.server_close()
259 self.event_server_thread = None
260 if self.log_watcher:
261 if self.log_watcher.IsThreadStarted():
262 self.log_watcher.StopWatchThread()
263 self.log_watcher = None
264 if self.prespawner:
265 logging.info('Stopping prespawner')
266 self.prespawner.stop()
267 self.prespawner = None
268 if self.event_client:
269 logging.info('Closing event client')
270 self.event_client.close()
271 self.event_client = None
272 if self.event_log:
273 self.event_log.Close()
274 self.event_log = None
275 self.check_exceptions()
276 logging.info('Done destroying Goofy')
277
278 def start_state_server(self):
279 self.state_instance, self.state_server = (
280 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800281 self.goofy_rpc = GoofyRPC(self)
282 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800283 logging.info('Starting state server')
284 self.state_server_thread = threading.Thread(
285 target=self.state_server.serve_forever,
286 name='StateServer')
287 self.state_server_thread.start()
288
289 def start_event_server(self):
290 self.event_server = EventServer()
291 logging.info('Starting factory event server')
292 self.event_server_thread = threading.Thread(
293 target=self.event_server.serve_forever,
294 name='EventServer') # pylint: disable=E1101
295 self.event_server_thread.start()
296
297 self.event_client = EventClient(
298 callback=self.handle_event, event_loop=self.run_queue)
299
300 self.web_socket_manager = WebSocketManager(self.uuid)
301 self.state_server.add_handler("/event",
302 self.web_socket_manager.handle_web_socket)
303
304 def start_ui(self):
305 ui_proc_args = [
306 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
307 self.options.test_list]
308 if self.options.verbose:
309 ui_proc_args.append('-v')
310 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800311 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800312 logging.info('Waiting for UI to come up...')
313 self.event_client.wait(
314 lambda event: event.type == Event.Type.UI_READY)
315 logging.info('UI has started')
316
317 def set_visible_test(self, test):
318 if self.visible_test == test:
319 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800320 if test and not test.has_ui:
321 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800322
323 if test:
324 test.update_state(visible=True)
325 if self.visible_test:
326 self.visible_test.update_state(visible=False)
327 self.visible_test = test
328
329 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800330 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800331 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800332
Jon Salz0697cbf2012-07-04 15:14:04 +0800333 @param test: The ShutdownStep.
334 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800335 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800336 test_state = test.update_state(increment_shutdown_count=1)
337 logging.info('Detected shutdown (%d of %d)',
338 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800339
Jon Salz0697cbf2012-07-04 15:14:04 +0800340 def log_and_update_state(status, error_msg, **kw):
341 self.event_log.Log('rebooted',
342 status=status, error_msg=error_msg, **kw)
343 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800344
Jon Salz0697cbf2012-07-04 15:14:04 +0800345 if not self.last_shutdown_time:
346 log_and_update_state(status=TestState.FAILED,
347 error_msg='Unable to read shutdown_time')
348 return
Jon Salz258a40c2012-04-19 12:34:01 +0800349
Jon Salz0697cbf2012-07-04 15:14:04 +0800350 now = time.time()
351 logging.info('%.03f s passed since reboot',
352 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800353
Jon Salz0697cbf2012-07-04 15:14:04 +0800354 if self.last_shutdown_time > now:
355 test.update_state(status=TestState.FAILED,
356 error_msg='Time moved backward during reboot')
357 elif (isinstance(test, factory.RebootStep) and
358 self.test_list.options.max_reboot_time_secs and
359 (now - self.last_shutdown_time >
360 self.test_list.options.max_reboot_time_secs)):
361 # A reboot took too long; fail. (We don't check this for
362 # HaltSteps, because the machine could be halted for a
363 # very long time, and even unplugged with battery backup,
364 # thus hosing the clock.)
365 log_and_update_state(
366 status=TestState.FAILED,
367 error_msg=('More than %d s elapsed during reboot '
368 '(%.03f s, from %s to %s)' % (
369 self.test_list.options.max_reboot_time_secs,
370 now - self.last_shutdown_time,
371 utils.TimeString(self.last_shutdown_time),
372 utils.TimeString(now))),
373 duration=(now-self.last_shutdown_time))
374 elif test_state.shutdown_count == test.iterations:
375 # Good!
376 log_and_update_state(status=TestState.PASSED,
377 duration=(now - self.last_shutdown_time),
378 error_msg='')
379 elif test_state.shutdown_count > test.iterations:
380 # Shut down too many times
381 log_and_update_state(status=TestState.FAILED,
382 error_msg='Too many shutdowns')
383 elif utils.are_shift_keys_depressed():
384 logging.info('Shift keys are depressed; cancelling restarts')
385 # Abort shutdown
386 log_and_update_state(
387 status=TestState.FAILED,
388 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800389 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800390 else:
391 def handler():
392 if self._prompt_cancel_shutdown(
393 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800394 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800395 log_and_update_state(
396 status=TestState.FAILED,
397 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800398 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800399 return
Jon Salz0405ab52012-03-16 15:26:52 +0800400
Jon Salz0697cbf2012-07-04 15:14:04 +0800401 # Time to shutdown again
402 log_and_update_state(
403 status=TestState.ACTIVE,
404 error_msg='',
405 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800406
Jon Salz0697cbf2012-07-04 15:14:04 +0800407 self.event_log.Log('shutdown', operation='reboot')
408 self.state_instance.set_shared_data('shutdown_time',
409 time.time())
410 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800411
Jon Salz0697cbf2012-07-04 15:14:04 +0800412 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800413
Jon Salz0697cbf2012-07-04 15:14:04 +0800414 def _prompt_cancel_shutdown(self, test, iteration):
415 if self.options.ui != 'chrome':
416 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800417
Jon Salz0697cbf2012-07-04 15:14:04 +0800418 pending_shutdown_data = {
419 'delay_secs': test.delay_secs,
420 'time': time.time() + test.delay_secs,
421 'operation': test.operation,
422 'iteration': iteration,
423 'iterations': test.iterations,
424 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800425
Jon Salz0697cbf2012-07-04 15:14:04 +0800426 # Create a new (threaded) event client since we
427 # don't want to use the event loop for this.
428 with EventClient() as event_client:
429 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
430 **pending_shutdown_data))
431 aborted = event_client.wait(
432 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
433 timeout=test.delay_secs) is not None
434 if aborted:
435 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
436 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800437
Jon Salz0697cbf2012-07-04 15:14:04 +0800438 def init_states(self):
439 '''
440 Initializes all states on startup.
441 '''
442 for test in self.test_list.get_all_tests():
443 # Make sure the state server knows about all the tests,
444 # defaulting to an untested state.
445 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800446
Jon Salz0697cbf2012-07-04 15:14:04 +0800447 var_log_messages = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800448
Jon Salz0697cbf2012-07-04 15:14:04 +0800449 # Any 'active' tests should be marked as failed now.
450 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800451 if not test.is_leaf():
452 # Don't bother with parents; they will be updated when their
453 # children are updated.
454 continue
455
Jon Salz0697cbf2012-07-04 15:14:04 +0800456 test_state = test.get_state()
457 if test_state.status != TestState.ACTIVE:
458 continue
459 if isinstance(test, factory.ShutdownStep):
460 # Shutdown while the test was active - that's good.
461 self.handle_shutdown_complete(test, test_state)
462 else:
463 # Unexpected shutdown. Grab /var/log/messages for context.
464 if var_log_messages is None:
465 try:
466 var_log_messages = (
467 utils.var_log_messages_before_reboot())
468 # Write it to the log, to make it easier to
469 # correlate with /var/log/messages.
470 logging.info(
471 'Unexpected shutdown. '
472 'Tail of /var/log/messages before last reboot:\n'
473 '%s', ('\n'.join(
474 ' ' + x for x in var_log_messages)))
475 except: # pylint: disable=W0702
476 logging.exception('Unable to grok /var/log/messages')
477 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800478
Jon Salz0697cbf2012-07-04 15:14:04 +0800479 error_msg = 'Unexpected shutdown while test was running'
480 self.event_log.Log('end_test',
481 path=test.path,
482 status=TestState.FAILED,
483 invocation=test.get_state().invocation,
484 error_msg=error_msg,
485 var_log_messages='\n'.join(var_log_messages))
486 test.update_state(
487 status=TestState.FAILED,
488 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800489
Jon Salz50efe942012-07-26 11:54:10 +0800490 if not test.never_fails:
491 # For "never_fails" tests (such as "Start"), don't cancel
492 # pending tests, since reboot is expected.
493 factory.console.info('Unexpected shutdown while test %s '
494 'running; cancelling any pending tests',
495 test.path)
496 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800497
Jon Salz0697cbf2012-07-04 15:14:04 +0800498 def show_next_active_test(self):
499 '''
500 Rotates to the next visible active test.
501 '''
502 self.reap_completed_tests()
503 active_tests = [
504 t for t in self.test_list.walk()
505 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
506 if not active_tests:
507 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800508
Jon Salz0697cbf2012-07-04 15:14:04 +0800509 try:
510 next_test = active_tests[
511 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
512 except ValueError: # visible_test not present in active_tests
513 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800514
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800516
Jon Salz0697cbf2012-07-04 15:14:04 +0800517 def handle_event(self, event):
518 '''
519 Handles an event from the event server.
520 '''
521 handler = self.event_handlers.get(event.type)
522 if handler:
523 handler(event)
524 else:
525 # We don't register handlers for all event types - just ignore
526 # this event.
527 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800528
Jon Salz0697cbf2012-07-04 15:14:04 +0800529 def run_next_test(self):
530 '''
531 Runs the next eligible test (or tests) in self.tests_to_run.
532 '''
533 self.reap_completed_tests()
534 while self.tests_to_run:
535 logging.debug('Tests to run: %s',
536 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800537
Jon Salz0697cbf2012-07-04 15:14:04 +0800538 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800539
Jon Salz0697cbf2012-07-04 15:14:04 +0800540 if test in self.invocations:
541 logging.info('Next test %s is already running', test.path)
542 self.tests_to_run.popleft()
543 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800544
Jon Salza1412922012-07-23 16:04:17 +0800545 for requirement in test.require_run:
546 for i in requirement.test.walk():
547 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800548 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800549 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800550 return
551
Jon Salz0697cbf2012-07-04 15:14:04 +0800552 if self.invocations and not (test.backgroundable and all(
553 [x.backgroundable for x in self.invocations])):
554 logging.debug('Waiting for non-backgroundable tests to '
555 'complete before running %s', test.path)
556 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800557
Jon Salz0697cbf2012-07-04 15:14:04 +0800558 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800559
Jon Salz304a75d2012-07-06 11:14:15 +0800560 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800561 for requirement in test.require_run:
562 for i in requirement.test.walk():
563 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800564 # We've hit this test itself; stop checking
565 break
Jon Salza1412922012-07-23 16:04:17 +0800566 if ((i.get_state().status == TestState.UNTESTED) or
567 (requirement.passed and i.get_state().status !=
568 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800569 # Found an untested test; move on to the next
570 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800571 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800572 break
573
574 if untested:
575 untested_paths = ', '.join(sorted([x.path for x in untested]))
576 if self.state_instance.get_shared_data('engineering_mode',
577 optional=True):
578 # In engineering mode, we'll let it go.
579 factory.console.warn('In engineering mode; running '
580 '%s even though required tests '
581 '[%s] have not completed',
582 test.path, untested_paths)
583 else:
584 # Not in engineering mode; mark it failed.
585 error_msg = ('Required tests [%s] have not been run yet'
586 % untested_paths)
587 factory.console.error('Not running %s: %s',
588 test.path, error_msg)
589 test.update_state(status=TestState.FAILED,
590 error_msg=error_msg)
591 continue
592
Jon Salz0697cbf2012-07-04 15:14:04 +0800593 if isinstance(test, factory.ShutdownStep):
594 if os.path.exists(NO_REBOOT_FILE):
595 test.update_state(
596 status=TestState.FAILED, increment_count=1,
597 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800598 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800599 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800600
Jon Salz0697cbf2012-07-04 15:14:04 +0800601 test.update_state(status=TestState.ACTIVE, increment_count=1,
602 error_msg='', shutdown_count=0)
603 if self._prompt_cancel_shutdown(test, 1):
604 self.event_log.Log('reboot_cancelled')
605 test.update_state(
606 status=TestState.FAILED, increment_count=1,
607 error_msg='Shutdown aborted by operator',
608 shutdown_count=0)
609 return
Jon Salz2f757d42012-06-27 17:06:42 +0800610
Jon Salz0697cbf2012-07-04 15:14:04 +0800611 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800612 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800613 'tests_after_shutdown',
614 [t.path for t in self.tests_to_run])
615 # Save shutdown time
616 self.state_instance.set_shared_data('shutdown_time',
617 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800618
Jon Salz0697cbf2012-07-04 15:14:04 +0800619 with self.env.lock:
620 self.event_log.Log('shutdown', operation=test.operation)
621 shutdown_result = self.env.shutdown(test.operation)
622 if shutdown_result:
623 # That's all, folks!
624 self.run_queue.put(None)
625 return
626 else:
627 # Just pass (e.g., in the chroot).
628 test.update_state(status=TestState.PASSED)
629 self.state_instance.set_shared_data(
630 'tests_after_shutdown', None)
631 # Send event with no fields to indicate that there is no
632 # longer a pending shutdown.
633 self.event_client.post_event(Event(
634 Event.Type.PENDING_SHUTDOWN))
635 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800636
Jon Salz1acc8742012-07-17 17:45:55 +0800637 self._run_test(test, test.iterations)
638
639 def _run_test(self, test, iterations_left=None):
640 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
641 new_state = test.update_state(
642 status=TestState.ACTIVE, increment_count=1, error_msg='',
643 invocation=invoc.uuid, iterations_left=iterations_left)
644 invoc.count = new_state.count
645
646 self.invocations[test] = invoc
647 if self.visible_test is None and test.has_ui:
648 self.set_visible_test(test)
649 self.check_connection_manager()
650 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800651
Jon Salz0697cbf2012-07-04 15:14:04 +0800652 def check_connection_manager(self):
653 exclusive_tests = [
654 test.path
655 for test in self.invocations
656 if test.is_exclusive(
657 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
658 if exclusive_tests:
659 # Make sure networking is disabled.
660 if self.network_enabled:
661 logging.info('Disabling network, as requested by %s',
662 exclusive_tests)
663 self.connection_manager.DisableNetworking()
664 self.network_enabled = False
665 else:
666 # Make sure networking is enabled.
667 if not self.network_enabled:
668 logging.info('Re-enabling network')
669 self.connection_manager.EnableNetworking()
670 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800671
cychiang21886742012-07-05 15:16:32 +0800672 def check_for_updates(self):
673 '''
674 Schedules an asynchronous check for updates if necessary.
675 '''
676 if not self.test_list.options.update_period_secs:
677 # Not enabled.
678 return
679
680 now = time.time()
681 if self.last_update_check and (
682 now - self.last_update_check <
683 self.test_list.options.update_period_secs):
684 # Not yet time for another check.
685 return
686
687 self.last_update_check = now
688
689 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
690 if reached_shopfloor:
691 new_update_md5sum = md5sum if needs_update else None
692 if system.SystemInfo.update_md5sum != new_update_md5sum:
693 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
694 system.SystemInfo.update_md5sum = new_update_md5sum
695 self.run_queue.put(self.update_system_info)
696
697 updater.CheckForUpdateAsync(
698 handle_check_for_update,
699 self.test_list.options.shopfloor_timeout_secs)
700
Jon Salza6711d72012-07-18 14:33:03 +0800701 def cancel_pending_tests(self):
702 '''Cancels any tests in the run queue.'''
703 self.run_tests([])
704
Jon Salz0697cbf2012-07-04 15:14:04 +0800705 def run_tests(self, subtrees, untested_only=False):
706 '''
707 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800708
Jon Salz0697cbf2012-07-04 15:14:04 +0800709 The tests are run in order unless one fails (then stops).
710 Backgroundable tests are run simultaneously; when a foreground test is
711 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800712
Jon Salz0697cbf2012-07-04 15:14:04 +0800713 @param subtrees: Node or nodes containing tests to run (may either be
714 a single test or a list). Duplicates will be ignored.
715 '''
716 if type(subtrees) != list:
717 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800718
Jon Salz0697cbf2012-07-04 15:14:04 +0800719 # Nodes we've seen so far, to avoid duplicates.
720 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800721
Jon Salz0697cbf2012-07-04 15:14:04 +0800722 self.tests_to_run = deque()
723 for subtree in subtrees:
724 for test in subtree.walk():
725 if test in seen:
726 continue
727 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800728
Jon Salz0697cbf2012-07-04 15:14:04 +0800729 if not test.is_leaf():
730 continue
731 if (untested_only and
732 test.get_state().status != TestState.UNTESTED):
733 continue
734 self.tests_to_run.append(test)
735 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800736
Jon Salz0697cbf2012-07-04 15:14:04 +0800737 def reap_completed_tests(self):
738 '''
739 Removes completed tests from the set of active tests.
740
741 Also updates the visible test if it was reaped.
742 '''
743 for t, v in dict(self.invocations).iteritems():
744 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800745 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800746 del self.invocations[t]
747
Jon Salz1acc8742012-07-17 17:45:55 +0800748 if new_state.iterations_left and new_state.status == TestState.PASSED:
749 # Play it again, Sam!
750 self._run_test(t)
751
Jon Salz0697cbf2012-07-04 15:14:04 +0800752 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800753 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800754 self.set_visible_test(None)
755 # Make the first running test, if any, the visible test
756 for t in self.test_list.walk():
757 if t in self.invocations:
758 self.set_visible_test(t)
759 break
760
Jon Salz85a39882012-07-05 16:45:04 +0800761 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800762 '''
763 Kills and waits for all active tests.
764
Jon Salz85a39882012-07-05 16:45:04 +0800765 Args:
766 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800767 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800768 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800769 '''
770 self.reap_completed_tests()
771 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800772 if root and not test.has_ancestor(root):
773 continue
774
Jon Salz0697cbf2012-07-04 15:14:04 +0800775 factory.console.info('Killing active test %s...' % test.path)
776 invoc.abort_and_join()
777 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800778 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800779 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800780
Jon Salz0697cbf2012-07-04 15:14:04 +0800781 if not abort:
782 test.update_state(status=TestState.UNTESTED)
783 self.reap_completed_tests()
784
Jon Salz85a39882012-07-05 16:45:04 +0800785 def stop(self, root=None, fail=False):
786 self.kill_active_tests(fail, root)
787 # Remove any tests in the run queue under the root.
788 self.tests_to_run = deque([x for x in self.tests_to_run
789 if root and not x.has_ancestor(root)])
790 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800791
792 def abort_active_tests(self):
793 self.kill_active_tests(True)
794
795 def main(self):
796 try:
797 self.init()
798 self.event_log.Log('goofy_init',
799 success=True)
800 except:
801 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800802 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800803 self.event_log.Log('goofy_init',
804 success=False,
805 trace=traceback.format_exc())
806 except: # pylint: disable=W0702
807 pass
808 raise
809
810 self.run()
811
812 def update_system_info(self):
813 '''Updates system info.'''
814 system_info = system.SystemInfo()
815 self.state_instance.set_shared_data('system_info', system_info.__dict__)
816 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
817 system_info=system_info.__dict__))
818 logging.info('System info: %r', system_info.__dict__)
819
Jon Salzeb42f0d2012-07-27 19:14:04 +0800820 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
821 '''Commences updating factory software.
822
823 Args:
824 auto_run_on_restart: Auto-run when the machine comes back up.
825 post_update_hook: Code to call after update but immediately before
826 restart.
827
828 Returns:
829 Never if the update was successful (we just reboot).
830 False if the update was unnecessary (no update available).
831 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800832 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800833 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800834
Jon Salz5c344f62012-07-13 14:31:16 +0800835 def pre_update_hook():
836 if auto_run_on_restart:
837 self.state_instance.set_shared_data('tests_after_shutdown',
838 FORCE_AUTO_RUN)
839 self.state_instance.close()
840
Jon Salzeb42f0d2012-07-27 19:14:04 +0800841 if updater.TryUpdate(pre_update_hook=pre_update_hook):
842 if post_update_hook:
843 post_update_hook()
844 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800845
846 def init(self, args=None, env=None):
847 '''Initializes Goofy.
848
849 Args:
850 args: A list of command-line arguments. Uses sys.argv if
851 args is None.
852 env: An Environment instance to use (or None to choose
853 FakeChrootEnvironment or DUTEnvironment as appropriate).
854 '''
855 parser = OptionParser()
856 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800857 action='store_true',
858 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800859 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800860 metavar='FILE',
861 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800862 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800863 action='store_true',
864 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800865 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800866 choices=['none', 'gtk', 'chrome'],
867 default=('chrome' if utils.in_chroot() else 'gtk'),
868 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800869 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800870 type='int', default=1,
871 help=('Factor by which to scale UI '
872 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800873 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800874 metavar='FILE',
875 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800876 (self.options, self.args) = parser.parse_args(args)
877
Jon Salz46b89562012-07-05 11:49:22 +0800878 # Make sure factory directories exist.
879 factory.get_log_root()
880 factory.get_state_root()
881 factory.get_test_data_root()
882
Jon Salz0697cbf2012-07-04 15:14:04 +0800883 global _inited_logging # pylint: disable=W0603
884 if not _inited_logging:
885 factory.init_logging('goofy', verbose=self.options.verbose)
886 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800887
Jon Salzee85d522012-07-17 14:34:46 +0800888 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +0800889 self.event_log = EventLog('goofy')
890
891 if (not suppress_chroot_warning and
892 factory.in_chroot() and
893 self.options.ui == 'gtk' and
894 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
895 # That's not going to work! Tell the user how to run
896 # this way.
897 logging.warn(GOOFY_IN_CHROOT_WARNING)
898 time.sleep(1)
899
900 if env:
901 self.env = env
902 elif factory.in_chroot():
903 self.env = test_environment.FakeChrootEnvironment()
904 logging.warn(
905 'Using chroot environment: will not actually run autotests')
906 else:
907 self.env = test_environment.DUTEnvironment()
908 self.env.goofy = self
909
910 if self.options.restart:
911 state.clear_state()
912
913 if self.options.print_test_list:
Jon Salzeb42f0d2012-07-27 19:14:04 +0800914 print factory.read_test_list(
915 self.options.print_test_list).__repr__(recursive=True)
Jon Salz0697cbf2012-07-04 15:14:04 +0800916 return
917
918 if self.options.ui_scale_factor != 1 and utils.in_qemu():
919 logging.warn(
920 'In QEMU; ignoring ui_scale_factor argument')
921 self.options.ui_scale_factor = 1
922
923 logging.info('Started')
924
925 self.start_state_server()
926 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
927 self.state_instance.set_shared_data('ui_scale_factor',
928 self.options.ui_scale_factor)
929 self.last_shutdown_time = (
930 self.state_instance.get_shared_data('shutdown_time', optional=True))
931 self.state_instance.del_shared_data('shutdown_time', optional=True)
932
933 if not self.options.test_list:
934 self.options.test_list = find_test_list()
935 if not self.options.test_list:
936 logging.error('No test list. Aborting.')
937 sys.exit(1)
938 logging.info('Using test list %s', self.options.test_list)
939
940 self.test_list = factory.read_test_list(
941 self.options.test_list,
Jon Salzeb42f0d2012-07-27 19:14:04 +0800942 self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800943 if not self.state_instance.has_shared_data('ui_lang'):
944 self.state_instance.set_shared_data('ui_lang',
945 self.test_list.options.ui_lang)
946 self.state_instance.set_shared_data(
947 'test_list_options',
948 self.test_list.options.__dict__)
949 self.state_instance.test_list = self.test_list
950
Jon Salz8fa8e832012-07-13 19:04:09 +0800951 if self.test_list.options.time_sanitizer:
952 self.time_sanitizer = time_sanitizer.TimeSanitizer(
953 base_time=time_sanitizer.GetBaseTimeFromFile(
954 # lsb-factory is written by the factory install shim during
955 # installation, so it should have a good time obtained from
956 # the mini-Omaha server.
957 '/usr/local/etc/lsb-factory'))
958 self.time_sanitizer.RunOnce()
959
Jon Salz0697cbf2012-07-04 15:14:04 +0800960 self.init_states()
961 self.start_event_server()
962 self.connection_manager = self.env.create_connection_manager(
963 self.test_list.options.wlans)
964 # Note that we create a log watcher even if
965 # sync_event_log_period_secs isn't set (no background
966 # syncing), since we may use it to flush event logs as well.
967 self.log_watcher = EventLogWatcher(
968 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +0800969 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +0800970 if self.test_list.options.sync_event_log_period_secs:
971 self.log_watcher.StartWatchThread()
972
973 self.update_system_info()
974
Vic Yang4953fc12012-07-26 16:19:53 +0800975 assert ((self.test_list.options.min_charge_pct is None) ==
976 (self.test_list.options.max_charge_pct is None))
977 if (self.test_list.options.min_charge_pct and
978 self.test_list.options.max_charge_pct):
979 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
980 self.test_list.options.max_charge_pct)
981
Jon Salz0697cbf2012-07-04 15:14:04 +0800982 os.environ['CROS_FACTORY'] = '1'
983 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
984
985 # Set CROS_UI since some behaviors in ui.py depend on the
986 # particular UI in use. TODO(jsalz): Remove this (and all
987 # places it is used) when the GTK UI is removed.
988 os.environ['CROS_UI'] = self.options.ui
989
990 if self.options.ui == 'chrome':
991 self.env.launch_chrome()
992 logging.info('Waiting for a web socket connection')
993 self.web_socket_manager.wait()
994
995 # Wait for the test widget size to be set; this is done in
996 # an asynchronous RPC so there is a small chance that the
997 # web socket might be opened first.
998 for _ in range(100): # 10 s
999 try:
1000 if self.state_instance.get_shared_data('test_widget_size'):
1001 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001002 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001003 pass # Retry
1004 time.sleep(0.1) # 100 ms
1005 else:
1006 logging.warn('Never received test_widget_size from UI')
1007 elif self.options.ui == 'gtk':
1008 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001009
Jon Salz0697cbf2012-07-04 15:14:04 +08001010 def state_change_callback(test, test_state):
1011 self.event_client.post_event(
1012 Event(Event.Type.STATE_CHANGE,
1013 path=test.path, state=test_state))
1014 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001015
Jon Salza6711d72012-07-18 14:33:03 +08001016 for handler in self.on_ui_startup:
1017 handler()
1018
1019 self.prespawner = Prespawner()
1020 self.prespawner.start()
1021
Jon Salz0697cbf2012-07-04 15:14:04 +08001022 try:
1023 tests_after_shutdown = self.state_instance.get_shared_data(
1024 'tests_after_shutdown')
1025 except KeyError:
1026 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001027
Jon Salz5c344f62012-07-13 14:31:16 +08001028 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1029 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001030 logging.info('Resuming tests after shutdown: %s',
1031 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001032 self.tests_to_run.extend(
1033 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1034 self.run_queue.put(self.run_next_test)
1035 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001036 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001037 self.run_queue.put(
1038 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001039 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001040
Jon Salz0697cbf2012-07-04 15:14:04 +08001041 def run(self):
1042 '''Runs Goofy.'''
1043 # Process events forever.
1044 while self.run_once(True):
1045 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001046
Jon Salz0697cbf2012-07-04 15:14:04 +08001047 def run_once(self, block=False):
1048 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001049
Jon Salz0697cbf2012-07-04 15:14:04 +08001050 Args:
1051 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001052
Jon Salz0697cbf2012-07-04 15:14:04 +08001053 Returns:
1054 True to keep going or False to shut down.
1055 '''
1056 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001057 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001058 # Nothing on the run queue.
1059 self._run_queue_idle()
1060 if block:
1061 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001062 try:
1063 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1064 except Queue.Empty:
1065 # Keep going (calling _run_queue_idle() again at the top of
1066 # the loop)
1067 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001068 # ...and grab anything else that showed up at the same
1069 # time.
1070 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001071 else:
1072 break
Jon Salz51528e12012-07-02 18:54:45 +08001073
Jon Salz0697cbf2012-07-04 15:14:04 +08001074 for event in events:
1075 if not event:
1076 # Shutdown request.
1077 self.run_queue.task_done()
1078 return False
Jon Salz51528e12012-07-02 18:54:45 +08001079
Jon Salz0697cbf2012-07-04 15:14:04 +08001080 try:
1081 event()
Jon Salz85a39882012-07-05 16:45:04 +08001082 except: # pylint: disable=W0702
1083 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001084 self.record_exception(traceback.format_exception_only(
1085 *sys.exc_info()[:2]))
1086 # But keep going
1087 finally:
1088 self.run_queue.task_done()
1089 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001090
Jon Salz8fa8e832012-07-13 19:04:09 +08001091 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001092 '''Writes out current time and tries to sync with shopfloor server.'''
1093 if not self.time_sanitizer:
1094 return
1095
1096 # Write out the current time.
1097 self.time_sanitizer.SaveTime()
1098
Jon Salz8fa8e832012-07-13 19:04:09 +08001099 if ((not self.test_list.options.sync_time_period_secs) or
1100 (not self.time_sanitizer) or
1101 self.time_synced or
1102 factory.in_chroot()):
1103 # Not enabled or already succeeded.
1104 return
1105
1106 now = time.time()
1107 if self.last_sync_time and (
1108 now - self.last_sync_time <
1109 self.test_list.options.sync_time_period_secs):
1110 # Not yet time for another check.
1111 return
1112 self.last_sync_time = now
1113
1114 def target():
1115 try:
1116 self.time_sanitizer.SyncWithShopfloor()
1117 self.time_synced = True
1118 except: # pylint: disable=W0702
1119 # Oh well. Log an error (but no trace)
1120 logging.info(
1121 'Unable to get time from shopfloor server: %s',
1122 utils.FormatExceptionOnly())
1123
1124 thread = threading.Thread(target=target)
1125 thread.daemon = True
1126 thread.start()
1127
Jon Salz0697cbf2012-07-04 15:14:04 +08001128 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001129 '''Invoked when the run queue has no events.
1130
1131 This method must not raise exception.
1132 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001133 now = time.time()
1134 if (self.last_idle and
1135 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1136 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1137 # 1) seconds.
1138 return
1139
1140 self.last_idle = now
1141
Jon Salz0697cbf2012-07-04 15:14:04 +08001142 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001143 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001144 self.sync_time_in_background()
Vic Yang4953fc12012-07-26 16:19:53 +08001145 if self.charge_manager:
1146 self.charge_manager.AdjustChargeState()
Jon Salz57717ca2012-04-04 16:47:25 +08001147
Jon Salz16d10542012-07-23 12:18:45 +08001148 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001149 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001150
Jon Salz0697cbf2012-07-04 15:14:04 +08001151 Attempts to upload the event logs to the shopfloor server.
1152 '''
1153 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1154 start_time = time.time()
1155 logging.info('Syncing %s', description)
1156 shopfloor_client = shopfloor.get_instance(
1157 detect=True,
1158 timeout=self.test_list.options.shopfloor_timeout_secs)
1159 shopfloor_client.UploadEvent(log_name, chunk)
1160 logging.info(
1161 'Successfully synced %s in %.03f s',
1162 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001163
Jon Salz0697cbf2012-07-04 15:14:04 +08001164 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1165 root=None):
1166 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001167
Jon Salz0697cbf2012-07-04 15:14:04 +08001168 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001169
Jon Salz0697cbf2012-07-04 15:14:04 +08001170 Args:
1171 starting_at: If provided, only auto-runs tests beginning with
1172 this test.
1173 '''
1174 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001175
Jon Salz0697cbf2012-07-04 15:14:04 +08001176 if starting_at:
1177 # Make sure they passed a test, not a string.
1178 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001179
Jon Salz0697cbf2012-07-04 15:14:04 +08001180 tests_to_reset = []
1181 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001182
Jon Salz0697cbf2012-07-04 15:14:04 +08001183 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001184
Jon Salz0697cbf2012-07-04 15:14:04 +08001185 for test in root.get_top_level_tests():
1186 if starting_at:
1187 if test == starting_at:
1188 # We've found starting_at; do auto-run on all
1189 # subsequent tests.
1190 found_starting_at = True
1191 if not found_starting_at:
1192 # Don't start this guy yet
1193 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001194
Jon Salz0697cbf2012-07-04 15:14:04 +08001195 status = test.get_state().status
1196 if status == TestState.ACTIVE or status in statuses_to_run:
1197 # Reset the test (later; we will need to abort
1198 # all active tests first).
1199 tests_to_reset.append(test)
1200 if status in statuses_to_run:
1201 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001202
Jon Salz0697cbf2012-07-04 15:14:04 +08001203 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001204
Jon Salz0697cbf2012-07-04 15:14:04 +08001205 # Reset all statuses of the tests to run (in case any tests were active;
1206 # we want them to be run again).
1207 for test_to_reset in tests_to_reset:
1208 for test in test_to_reset.walk():
1209 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001210
Jon Salz0697cbf2012-07-04 15:14:04 +08001211 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001212
Jon Salz0697cbf2012-07-04 15:14:04 +08001213 def restart_tests(self, root=None):
1214 '''Restarts all tests.'''
1215 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001216
Jon Salz0697cbf2012-07-04 15:14:04 +08001217 self.abort_active_tests()
1218 for test in root.walk():
1219 test.update_state(status=TestState.UNTESTED)
1220 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001221
Jon Salz0697cbf2012-07-04 15:14:04 +08001222 def auto_run(self, starting_at=None, root=None):
1223 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001224
Jon Salz0697cbf2012-07-04 15:14:04 +08001225 Args:
1226 starting_at: If provide, only auto-runs tests beginning with
1227 this test.
1228 '''
1229 root = root or self.test_list
1230 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1231 starting_at=starting_at,
1232 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001233
Jon Salz0697cbf2012-07-04 15:14:04 +08001234 def re_run_failed(self, root=None):
1235 '''Re-runs failed tests.'''
1236 root = root or self.test_list
1237 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001238
Jon Salz0697cbf2012-07-04 15:14:04 +08001239 def show_review_information(self):
1240 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001241
Jon Salz0697cbf2012-07-04 15:14:04 +08001242 The information screene is rendered by main UI program (ui.py), so in
1243 goofy we only need to kill all active tests, set them as untested, and
1244 clear remaining tests.
1245 '''
1246 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001247 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001248
Jon Salz0697cbf2012-07-04 15:14:04 +08001249 def handle_switch_test(self, event):
1250 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001251
Jon Salz0697cbf2012-07-04 15:14:04 +08001252 @param event: The SWITCH_TEST event.
1253 '''
1254 test = self.test_list.lookup_path(event.path)
1255 if not test:
1256 logging.error('Unknown test %r', event.key)
1257 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001258
Jon Salz0697cbf2012-07-04 15:14:04 +08001259 invoc = self.invocations.get(test)
1260 if invoc and test.backgroundable:
1261 # Already running: just bring to the front if it
1262 # has a UI.
1263 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001264 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001265 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001266
Jon Salz0697cbf2012-07-04 15:14:04 +08001267 self.abort_active_tests()
1268 for t in test.walk():
1269 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001270
Jon Salz0697cbf2012-07-04 15:14:04 +08001271 if self.test_list.options.auto_run_on_keypress:
1272 self.auto_run(starting_at=test)
1273 else:
1274 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001275
Jon Salz0697cbf2012-07-04 15:14:04 +08001276 def wait(self):
1277 '''Waits for all pending invocations.
1278
1279 Useful for testing.
1280 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001281 while self.invocations:
1282 for k, v in self.invocations.iteritems():
1283 logging.info('Waiting for %s to complete...', k)
1284 v.thread.join()
1285 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001286
1287 def check_exceptions(self):
1288 '''Raises an error if any exceptions have occurred in
1289 invocation threads.'''
1290 if self.exceptions:
1291 raise RuntimeError('Exception in invocation thread: %r' %
1292 self.exceptions)
1293
1294 def record_exception(self, msg):
1295 '''Records an exception in an invocation thread.
1296
1297 An exception with the given message will be rethrown when
1298 Goofy is destroyed.'''
1299 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001300
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001301
1302if __name__ == '__main__':
Jon Salz0697cbf2012-07-04 15:14:04 +08001303 Goofy().main()