blob: d2b786e82ad7502b38110ba790e27f8072960996 [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
Jon Salzb10cf512012-08-09 17:29:21 +080020from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080021from collections import deque
22from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080023
Jon Salz0697cbf2012-07-04 15:14:04 +080024import factory_common # pylint: disable=W0611
jcliangcd688182012-08-20 21:01:26 +080025from cros.factory import event_log
26from cros.factory import system
27from cros.factory.event_log import EventLog
28from cros.factory.goofy import test_environment
29from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080030from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080031from cros.factory.goofy.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080032from cros.factory.goofy.goofy_rpc import GoofyRPC
33from cros.factory.goofy.invocation import TestInvocation
34from cros.factory.goofy.prespawner import Prespawner
35from cros.factory.goofy.web_socket_manager import WebSocketManager
36from cros.factory.system.charge_manager import ChargeManager
37from cros.factory.test import factory
38from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080039from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080040from cros.factory.test import utils
41from cros.factory.test.event import Event
42from cros.factory.test.event import EventClient
43from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080044from cros.factory.test.factory import TestState
Jon Salz78c32392012-07-25 14:18:29 +080045from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080046
47
Jon Salz2f757d42012-06-27 17:06:42 +080048DEFAULT_TEST_LISTS_DIR = os.path.join(factory.FACTORY_PATH, 'test_lists')
49CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080050HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
51
Jon Salz8796e362012-05-24 11:39:09 +080052# File that suppresses reboot if present (e.g., for development).
53NO_REBOOT_FILE = '/var/log/factory.noreboot'
54
Jon Salz5c344f62012-07-13 14:31:16 +080055# Value for tests_after_shutdown that forces auto-run (e.g., after
56# a factory update, when the available set of tests might change).
57FORCE_AUTO_RUN = 'force_auto_run'
58
cychiang21886742012-07-05 15:16:32 +080059RUN_QUEUE_TIMEOUT_SECS = 10
60
Jon Salz758e6cc2012-04-03 15:47:07 +080061GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
62You are running Goofy inside the chroot. Autotests are not supported.
63
64To use Goofy in the chroot, first install an Xvnc server:
65
Jon Salz0697cbf2012-07-04 15:14:04 +080066 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080067
68...and then start a VNC X server outside the chroot:
69
Jon Salz0697cbf2012-07-04 15:14:04 +080070 vncserver :10 &
71 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080072
73...and run Goofy as follows:
74
Jon Salz0697cbf2012-07-04 15:14:04 +080075 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080076''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080077suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080078
79def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080080 '''
81 Returns the HWID config tag, or an empty string if none can be found.
82 '''
83 if 'CROS_HWID' in os.environ:
84 return os.environ['CROS_HWID']
85 if os.path.exists(HWID_CFG_PATH):
86 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
87 return hwid_cfg_handle.read().strip()
88 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080089
90
91def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +080092 '''
93 Returns the path to the active test list, based on the HWID config tag.
94 '''
95 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +080096
Jon Salz0697cbf2012-07-04 15:14:04 +080097 search_dirs = [CUSTOM_DIR, DEFAULT_TEST_LISTS_DIR]
Jon Salz2f757d42012-06-27 17:06:42 +080098
Jon Salz0697cbf2012-07-04 15:14:04 +080099 # Try in order: test_list_${hwid_cfg}, test_list, test_list.all
100 search_files = ['test_list', 'test_list.all']
101 if hwid_cfg:
102 search_files.insert(0, hwid_cfg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800103
Jon Salz0697cbf2012-07-04 15:14:04 +0800104 for d in search_dirs:
105 for f in search_files:
106 test_list = os.path.join(d, f)
107 if os.path.exists(test_list):
108 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800109
Jon Salz0697cbf2012-07-04 15:14:04 +0800110 logging.warn('Cannot find test lists named any of %s in any of %s',
111 search_files, search_dirs)
112 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800113
Jon Salz73e0fd02012-04-04 11:46:38 +0800114_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800115
116class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800117 '''
118 The main factory flow.
119
120 Note that all methods in this class must be invoked from the main
121 (event) thread. Other threads, such as callbacks and TestInvocation
122 methods, should instead post events on the run queue.
123
124 TODO: Unit tests. (chrome-os-partner:7409)
125
126 Properties:
127 uuid: A unique UUID for this invocation of Goofy.
128 state_instance: An instance of FactoryState.
129 state_server: The FactoryState XML/RPC server.
130 state_server_thread: A thread running state_server.
131 event_server: The EventServer socket server.
132 event_server_thread: A thread running event_server.
133 event_client: A client to the event server.
134 connection_manager: The connection_manager object.
135 network_enabled: Whether the connection_manager is currently
136 enabling connections.
137 ui_process: The factory ui process object.
138 run_queue: A queue of callbacks to invoke from the main thread.
139 invocations: A map from FactoryTest objects to the corresponding
140 TestInvocations objects representing active tests.
141 tests_to_run: A deque of tests that should be run when the current
142 test(s) complete.
143 options: Command-line options.
144 args: Command-line args.
145 test_list: The test list.
146 event_handlers: Map of Event.Type to the method used to handle that
147 event. If the method has an 'event' argument, the event is passed
148 to the handler.
149 exceptions: Exceptions encountered in invocation threads.
150 '''
151 def __init__(self):
152 self.uuid = str(uuid.uuid4())
153 self.state_instance = None
154 self.state_server = None
155 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800156 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800157 self.event_server = None
158 self.event_server_thread = None
159 self.event_client = None
160 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800161 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800162 self.time_sanitizer = None
163 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800164 self.log_watcher = None
165 self.network_enabled = True
166 self.event_log = None
167 self.prespawner = None
168 self.ui_process = None
169 self.run_queue = Queue.Queue()
170 self.invocations = {}
171 self.tests_to_run = deque()
172 self.visible_test = None
173 self.chrome = None
174
175 self.options = None
176 self.args = None
177 self.test_list = None
178 self.on_ui_startup = []
179 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800180 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800181 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800182 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800183 self.last_sync_time = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800184
Jon Salz85a39882012-07-05 16:45:04 +0800185 def test_or_root(event, parent_or_group=True):
186 '''Returns the test affected by a particular event.
187
188 Args:
189 event: The event containing an optional 'path' attribute.
190 parent_on_group: If True, returns the top-level parent for a test (the
191 root node of the tests that need to be run together if the given test
192 path is to be run).
193 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800194 try:
195 path = event.path
196 except AttributeError:
197 path = None
198
199 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800200 test = self.test_list.lookup_path(path)
201 if parent_or_group:
202 test = test.get_top_level_parent_or_group()
203 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800204 else:
205 return self.test_list
206
207 self.event_handlers = {
208 Event.Type.SWITCH_TEST: self.handle_switch_test,
209 Event.Type.SHOW_NEXT_ACTIVE_TEST:
210 lambda event: self.show_next_active_test(),
211 Event.Type.RESTART_TESTS:
212 lambda event: self.restart_tests(root=test_or_root(event)),
213 Event.Type.AUTO_RUN:
214 lambda event: self.auto_run(root=test_or_root(event)),
215 Event.Type.RE_RUN_FAILED:
216 lambda event: self.re_run_failed(root=test_or_root(event)),
217 Event.Type.RUN_TESTS_WITH_STATUS:
218 lambda event: self.run_tests_with_status(
219 event.status,
220 root=test_or_root(event)),
221 Event.Type.REVIEW:
222 lambda event: self.show_review_information(),
223 Event.Type.UPDATE_SYSTEM_INFO:
224 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800225 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800226 lambda event: self.stop(root=test_or_root(event, False),
227 fail=getattr(event, 'fail', False)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800228 Event.Type.SET_VISIBLE_TEST:
229 lambda event: self.set_visible_test(
230 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800231 }
232
233 self.exceptions = []
234 self.web_socket_manager = None
235
236 def destroy(self):
237 if self.chrome:
238 self.chrome.kill()
239 self.chrome = None
240 if self.ui_process:
241 utils.kill_process_tree(self.ui_process, 'ui')
242 self.ui_process = None
243 if self.web_socket_manager:
244 logging.info('Stopping web sockets')
245 self.web_socket_manager.close()
246 self.web_socket_manager = None
247 if self.state_server_thread:
248 logging.info('Stopping state server')
249 self.state_server.shutdown()
250 self.state_server_thread.join()
251 self.state_server.server_close()
252 self.state_server_thread = None
253 if self.state_instance:
254 self.state_instance.close()
255 if self.event_server_thread:
256 logging.info('Stopping event server')
257 self.event_server.shutdown() # pylint: disable=E1101
258 self.event_server_thread.join()
259 self.event_server.server_close()
260 self.event_server_thread = None
261 if self.log_watcher:
262 if self.log_watcher.IsThreadStarted():
263 self.log_watcher.StopWatchThread()
264 self.log_watcher = None
265 if self.prespawner:
266 logging.info('Stopping prespawner')
267 self.prespawner.stop()
268 self.prespawner = None
269 if self.event_client:
270 logging.info('Closing event client')
271 self.event_client.close()
272 self.event_client = None
273 if self.event_log:
274 self.event_log.Close()
275 self.event_log = None
276 self.check_exceptions()
277 logging.info('Done destroying Goofy')
278
279 def start_state_server(self):
280 self.state_instance, self.state_server = (
281 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800282 self.goofy_rpc = GoofyRPC(self)
283 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800284 logging.info('Starting state server')
285 self.state_server_thread = threading.Thread(
286 target=self.state_server.serve_forever,
287 name='StateServer')
288 self.state_server_thread.start()
289
290 def start_event_server(self):
291 self.event_server = EventServer()
292 logging.info('Starting factory event server')
293 self.event_server_thread = threading.Thread(
294 target=self.event_server.serve_forever,
295 name='EventServer') # pylint: disable=E1101
296 self.event_server_thread.start()
297
298 self.event_client = EventClient(
299 callback=self.handle_event, event_loop=self.run_queue)
300
301 self.web_socket_manager = WebSocketManager(self.uuid)
302 self.state_server.add_handler("/event",
303 self.web_socket_manager.handle_web_socket)
304
305 def start_ui(self):
306 ui_proc_args = [
307 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
308 self.options.test_list]
309 if self.options.verbose:
310 ui_proc_args.append('-v')
311 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800312 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800313 logging.info('Waiting for UI to come up...')
314 self.event_client.wait(
315 lambda event: event.type == Event.Type.UI_READY)
316 logging.info('UI has started')
317
318 def set_visible_test(self, test):
319 if self.visible_test == test:
320 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800321 if test and not test.has_ui:
322 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800323
324 if test:
325 test.update_state(visible=True)
326 if self.visible_test:
327 self.visible_test.update_state(visible=False)
328 self.visible_test = test
329
330 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800331 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800332 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800333
Jon Salz0697cbf2012-07-04 15:14:04 +0800334 @param test: The ShutdownStep.
335 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800336 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800337 test_state = test.update_state(increment_shutdown_count=1)
338 logging.info('Detected shutdown (%d of %d)',
339 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800340
Jon Salz0697cbf2012-07-04 15:14:04 +0800341 def log_and_update_state(status, error_msg, **kw):
342 self.event_log.Log('rebooted',
343 status=status, error_msg=error_msg, **kw)
344 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800345
Jon Salz0697cbf2012-07-04 15:14:04 +0800346 if not self.last_shutdown_time:
347 log_and_update_state(status=TestState.FAILED,
348 error_msg='Unable to read shutdown_time')
349 return
Jon Salz258a40c2012-04-19 12:34:01 +0800350
Jon Salz0697cbf2012-07-04 15:14:04 +0800351 now = time.time()
352 logging.info('%.03f s passed since reboot',
353 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800354
Jon Salz0697cbf2012-07-04 15:14:04 +0800355 if self.last_shutdown_time > now:
356 test.update_state(status=TestState.FAILED,
357 error_msg='Time moved backward during reboot')
358 elif (isinstance(test, factory.RebootStep) and
359 self.test_list.options.max_reboot_time_secs and
360 (now - self.last_shutdown_time >
361 self.test_list.options.max_reboot_time_secs)):
362 # A reboot took too long; fail. (We don't check this for
363 # HaltSteps, because the machine could be halted for a
364 # very long time, and even unplugged with battery backup,
365 # thus hosing the clock.)
366 log_and_update_state(
367 status=TestState.FAILED,
368 error_msg=('More than %d s elapsed during reboot '
369 '(%.03f s, from %s to %s)' % (
370 self.test_list.options.max_reboot_time_secs,
371 now - self.last_shutdown_time,
372 utils.TimeString(self.last_shutdown_time),
373 utils.TimeString(now))),
374 duration=(now-self.last_shutdown_time))
375 elif test_state.shutdown_count == test.iterations:
376 # Good!
377 log_and_update_state(status=TestState.PASSED,
378 duration=(now - self.last_shutdown_time),
379 error_msg='')
380 elif test_state.shutdown_count > test.iterations:
381 # Shut down too many times
382 log_and_update_state(status=TestState.FAILED,
383 error_msg='Too many shutdowns')
384 elif utils.are_shift_keys_depressed():
385 logging.info('Shift keys are depressed; cancelling restarts')
386 # Abort shutdown
387 log_and_update_state(
388 status=TestState.FAILED,
389 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800390 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800391 else:
392 def handler():
393 if self._prompt_cancel_shutdown(
394 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800395 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800396 log_and_update_state(
397 status=TestState.FAILED,
398 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800399 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800400 return
Jon Salz0405ab52012-03-16 15:26:52 +0800401
Jon Salz0697cbf2012-07-04 15:14:04 +0800402 # Time to shutdown again
403 log_and_update_state(
404 status=TestState.ACTIVE,
405 error_msg='',
406 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800407
Jon Salz0697cbf2012-07-04 15:14:04 +0800408 self.event_log.Log('shutdown', operation='reboot')
409 self.state_instance.set_shared_data('shutdown_time',
410 time.time())
411 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800412
Jon Salz0697cbf2012-07-04 15:14:04 +0800413 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800414
Jon Salz0697cbf2012-07-04 15:14:04 +0800415 def _prompt_cancel_shutdown(self, test, iteration):
416 if self.options.ui != 'chrome':
417 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800418
Jon Salz0697cbf2012-07-04 15:14:04 +0800419 pending_shutdown_data = {
420 'delay_secs': test.delay_secs,
421 'time': time.time() + test.delay_secs,
422 'operation': test.operation,
423 'iteration': iteration,
424 'iterations': test.iterations,
425 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800426
Jon Salz0697cbf2012-07-04 15:14:04 +0800427 # Create a new (threaded) event client since we
428 # don't want to use the event loop for this.
429 with EventClient() as event_client:
430 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
431 **pending_shutdown_data))
432 aborted = event_client.wait(
433 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
434 timeout=test.delay_secs) is not None
435 if aborted:
436 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
437 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800438
Jon Salz0697cbf2012-07-04 15:14:04 +0800439 def init_states(self):
440 '''
441 Initializes all states on startup.
442 '''
443 for test in self.test_list.get_all_tests():
444 # Make sure the state server knows about all the tests,
445 # defaulting to an untested state.
446 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800447
Jon Salz0697cbf2012-07-04 15:14:04 +0800448 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800449 mosys_log = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800450
Jon Salz0697cbf2012-07-04 15:14:04 +0800451 # Any 'active' tests should be marked as failed now.
452 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800453 if not test.is_leaf():
454 # Don't bother with parents; they will be updated when their
455 # children are updated.
456 continue
457
Jon Salz0697cbf2012-07-04 15:14:04 +0800458 test_state = test.get_state()
459 if test_state.status != TestState.ACTIVE:
460 continue
461 if isinstance(test, factory.ShutdownStep):
462 # Shutdown while the test was active - that's good.
463 self.handle_shutdown_complete(test, test_state)
464 else:
465 # Unexpected shutdown. Grab /var/log/messages for context.
466 if var_log_messages is None:
467 try:
468 var_log_messages = (
469 utils.var_log_messages_before_reboot())
470 # Write it to the log, to make it easier to
471 # correlate with /var/log/messages.
472 logging.info(
473 'Unexpected shutdown. '
474 'Tail of /var/log/messages before last reboot:\n'
475 '%s', ('\n'.join(
476 ' ' + x for x in var_log_messages)))
477 except: # pylint: disable=W0702
478 logging.exception('Unable to grok /var/log/messages')
479 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800480
Vic Yanga9c32212012-08-16 20:07:54 +0800481 if mosys_log is None:
482 mosys_log = utils.Spawn(['mosys', 'eventlog', 'list'],
483 read_stdout=True, log_stderr_on_error=True).stdout_data
484 # Write it to the log also.
485 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
486
Jon Salz0697cbf2012-07-04 15:14:04 +0800487 error_msg = 'Unexpected shutdown while test was running'
488 self.event_log.Log('end_test',
489 path=test.path,
490 status=TestState.FAILED,
491 invocation=test.get_state().invocation,
492 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800493 var_log_messages='\n'.join(var_log_messages),
494 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800495 test.update_state(
496 status=TestState.FAILED,
497 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800498
Jon Salz50efe942012-07-26 11:54:10 +0800499 if not test.never_fails:
500 # For "never_fails" tests (such as "Start"), don't cancel
501 # pending tests, since reboot is expected.
502 factory.console.info('Unexpected shutdown while test %s '
503 'running; cancelling any pending tests',
504 test.path)
505 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800506
Jon Salz0697cbf2012-07-04 15:14:04 +0800507 def show_next_active_test(self):
508 '''
509 Rotates to the next visible active test.
510 '''
511 self.reap_completed_tests()
512 active_tests = [
513 t for t in self.test_list.walk()
514 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
515 if not active_tests:
516 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800517
Jon Salz0697cbf2012-07-04 15:14:04 +0800518 try:
519 next_test = active_tests[
520 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
521 except ValueError: # visible_test not present in active_tests
522 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800523
Jon Salz0697cbf2012-07-04 15:14:04 +0800524 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800525
Jon Salz0697cbf2012-07-04 15:14:04 +0800526 def handle_event(self, event):
527 '''
528 Handles an event from the event server.
529 '''
530 handler = self.event_handlers.get(event.type)
531 if handler:
532 handler(event)
533 else:
534 # We don't register handlers for all event types - just ignore
535 # this event.
536 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800537
Jon Salz0697cbf2012-07-04 15:14:04 +0800538 def run_next_test(self):
539 '''
540 Runs the next eligible test (or tests) in self.tests_to_run.
541 '''
542 self.reap_completed_tests()
543 while self.tests_to_run:
544 logging.debug('Tests to run: %s',
545 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800546
Jon Salz0697cbf2012-07-04 15:14:04 +0800547 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800548
Jon Salz0697cbf2012-07-04 15:14:04 +0800549 if test in self.invocations:
550 logging.info('Next test %s is already running', test.path)
551 self.tests_to_run.popleft()
552 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800553
Jon Salza1412922012-07-23 16:04:17 +0800554 for requirement in test.require_run:
555 for i in requirement.test.walk():
556 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800557 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800558 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800559 return
560
Jon Salz0697cbf2012-07-04 15:14:04 +0800561 if self.invocations and not (test.backgroundable and all(
562 [x.backgroundable for x in self.invocations])):
563 logging.debug('Waiting for non-backgroundable tests to '
564 'complete before running %s', test.path)
565 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800566
Jon Salz0697cbf2012-07-04 15:14:04 +0800567 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800568
Jon Salz304a75d2012-07-06 11:14:15 +0800569 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800570 for requirement in test.require_run:
571 for i in requirement.test.walk():
572 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800573 # We've hit this test itself; stop checking
574 break
Jon Salza1412922012-07-23 16:04:17 +0800575 if ((i.get_state().status == TestState.UNTESTED) or
576 (requirement.passed and i.get_state().status !=
577 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800578 # Found an untested test; move on to the next
579 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800580 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800581 break
582
583 if untested:
584 untested_paths = ', '.join(sorted([x.path for x in untested]))
585 if self.state_instance.get_shared_data('engineering_mode',
586 optional=True):
587 # In engineering mode, we'll let it go.
588 factory.console.warn('In engineering mode; running '
589 '%s even though required tests '
590 '[%s] have not completed',
591 test.path, untested_paths)
592 else:
593 # Not in engineering mode; mark it failed.
594 error_msg = ('Required tests [%s] have not been run yet'
595 % untested_paths)
596 factory.console.error('Not running %s: %s',
597 test.path, error_msg)
598 test.update_state(status=TestState.FAILED,
599 error_msg=error_msg)
600 continue
601
Jon Salz0697cbf2012-07-04 15:14:04 +0800602 if isinstance(test, factory.ShutdownStep):
603 if os.path.exists(NO_REBOOT_FILE):
604 test.update_state(
605 status=TestState.FAILED, increment_count=1,
606 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800607 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800608 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800609
Jon Salz0697cbf2012-07-04 15:14:04 +0800610 test.update_state(status=TestState.ACTIVE, increment_count=1,
611 error_msg='', shutdown_count=0)
612 if self._prompt_cancel_shutdown(test, 1):
613 self.event_log.Log('reboot_cancelled')
614 test.update_state(
615 status=TestState.FAILED, increment_count=1,
616 error_msg='Shutdown aborted by operator',
617 shutdown_count=0)
618 return
Jon Salz2f757d42012-06-27 17:06:42 +0800619
Jon Salz0697cbf2012-07-04 15:14:04 +0800620 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800621 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800622 'tests_after_shutdown',
623 [t.path for t in self.tests_to_run])
624 # Save shutdown time
625 self.state_instance.set_shared_data('shutdown_time',
626 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800627
Jon Salz0697cbf2012-07-04 15:14:04 +0800628 with self.env.lock:
629 self.event_log.Log('shutdown', operation=test.operation)
630 shutdown_result = self.env.shutdown(test.operation)
631 if shutdown_result:
632 # That's all, folks!
633 self.run_queue.put(None)
634 return
635 else:
636 # Just pass (e.g., in the chroot).
637 test.update_state(status=TestState.PASSED)
638 self.state_instance.set_shared_data(
639 'tests_after_shutdown', None)
640 # Send event with no fields to indicate that there is no
641 # longer a pending shutdown.
642 self.event_client.post_event(Event(
643 Event.Type.PENDING_SHUTDOWN))
644 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800645
Jon Salz1acc8742012-07-17 17:45:55 +0800646 self._run_test(test, test.iterations)
647
648 def _run_test(self, test, iterations_left=None):
649 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
650 new_state = test.update_state(
651 status=TestState.ACTIVE, increment_count=1, error_msg='',
652 invocation=invoc.uuid, iterations_left=iterations_left)
653 invoc.count = new_state.count
654
655 self.invocations[test] = invoc
656 if self.visible_test is None and test.has_ui:
657 self.set_visible_test(test)
658 self.check_connection_manager()
659 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800660
Jon Salz0697cbf2012-07-04 15:14:04 +0800661 def check_connection_manager(self):
662 exclusive_tests = [
663 test.path
664 for test in self.invocations
665 if test.is_exclusive(
666 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
667 if exclusive_tests:
668 # Make sure networking is disabled.
669 if self.network_enabled:
670 logging.info('Disabling network, as requested by %s',
671 exclusive_tests)
672 self.connection_manager.DisableNetworking()
673 self.network_enabled = False
674 else:
675 # Make sure networking is enabled.
676 if not self.network_enabled:
677 logging.info('Re-enabling network')
678 self.connection_manager.EnableNetworking()
679 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800680
cychiang21886742012-07-05 15:16:32 +0800681 def check_for_updates(self):
682 '''
683 Schedules an asynchronous check for updates if necessary.
684 '''
685 if not self.test_list.options.update_period_secs:
686 # Not enabled.
687 return
688
689 now = time.time()
690 if self.last_update_check and (
691 now - self.last_update_check <
692 self.test_list.options.update_period_secs):
693 # Not yet time for another check.
694 return
695
696 self.last_update_check = now
697
698 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
699 if reached_shopfloor:
700 new_update_md5sum = md5sum if needs_update else None
701 if system.SystemInfo.update_md5sum != new_update_md5sum:
702 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
703 system.SystemInfo.update_md5sum = new_update_md5sum
704 self.run_queue.put(self.update_system_info)
705
706 updater.CheckForUpdateAsync(
707 handle_check_for_update,
708 self.test_list.options.shopfloor_timeout_secs)
709
Jon Salza6711d72012-07-18 14:33:03 +0800710 def cancel_pending_tests(self):
711 '''Cancels any tests in the run queue.'''
712 self.run_tests([])
713
Jon Salz0697cbf2012-07-04 15:14:04 +0800714 def run_tests(self, subtrees, untested_only=False):
715 '''
716 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800717
Jon Salz0697cbf2012-07-04 15:14:04 +0800718 The tests are run in order unless one fails (then stops).
719 Backgroundable tests are run simultaneously; when a foreground test is
720 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800721
Jon Salz0697cbf2012-07-04 15:14:04 +0800722 @param subtrees: Node or nodes containing tests to run (may either be
723 a single test or a list). Duplicates will be ignored.
724 '''
725 if type(subtrees) != list:
726 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800727
Jon Salz0697cbf2012-07-04 15:14:04 +0800728 # Nodes we've seen so far, to avoid duplicates.
729 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800730
Jon Salz0697cbf2012-07-04 15:14:04 +0800731 self.tests_to_run = deque()
732 for subtree in subtrees:
733 for test in subtree.walk():
734 if test in seen:
735 continue
736 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800737
Jon Salz0697cbf2012-07-04 15:14:04 +0800738 if not test.is_leaf():
739 continue
740 if (untested_only and
741 test.get_state().status != TestState.UNTESTED):
742 continue
743 self.tests_to_run.append(test)
744 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800745
Jon Salz0697cbf2012-07-04 15:14:04 +0800746 def reap_completed_tests(self):
747 '''
748 Removes completed tests from the set of active tests.
749
750 Also updates the visible test if it was reaped.
751 '''
752 for t, v in dict(self.invocations).iteritems():
753 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800754 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800755 del self.invocations[t]
756
Jon Salz1acc8742012-07-17 17:45:55 +0800757 if new_state.iterations_left and new_state.status == TestState.PASSED:
758 # Play it again, Sam!
759 self._run_test(t)
760
Jon Salz0697cbf2012-07-04 15:14:04 +0800761 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800762 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800763 self.set_visible_test(None)
764 # Make the first running test, if any, the visible test
765 for t in self.test_list.walk():
766 if t in self.invocations:
767 self.set_visible_test(t)
768 break
769
Jon Salz85a39882012-07-05 16:45:04 +0800770 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800771 '''
772 Kills and waits for all active tests.
773
Jon Salz85a39882012-07-05 16:45:04 +0800774 Args:
775 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800776 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800777 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800778 '''
779 self.reap_completed_tests()
780 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800781 if root and not test.has_ancestor(root):
782 continue
783
Jon Salz0697cbf2012-07-04 15:14:04 +0800784 factory.console.info('Killing active test %s...' % test.path)
785 invoc.abort_and_join()
786 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800787 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800788 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800789
Jon Salz0697cbf2012-07-04 15:14:04 +0800790 if not abort:
791 test.update_state(status=TestState.UNTESTED)
792 self.reap_completed_tests()
793
Jon Salz85a39882012-07-05 16:45:04 +0800794 def stop(self, root=None, fail=False):
795 self.kill_active_tests(fail, root)
796 # Remove any tests in the run queue under the root.
797 self.tests_to_run = deque([x for x in self.tests_to_run
798 if root and not x.has_ancestor(root)])
799 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800800
801 def abort_active_tests(self):
802 self.kill_active_tests(True)
803
804 def main(self):
805 try:
806 self.init()
807 self.event_log.Log('goofy_init',
808 success=True)
809 except:
810 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800811 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800812 self.event_log.Log('goofy_init',
813 success=False,
814 trace=traceback.format_exc())
815 except: # pylint: disable=W0702
816 pass
817 raise
818
819 self.run()
820
821 def update_system_info(self):
822 '''Updates system info.'''
823 system_info = system.SystemInfo()
824 self.state_instance.set_shared_data('system_info', system_info.__dict__)
825 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
826 system_info=system_info.__dict__))
827 logging.info('System info: %r', system_info.__dict__)
828
Jon Salzeb42f0d2012-07-27 19:14:04 +0800829 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
830 '''Commences updating factory software.
831
832 Args:
833 auto_run_on_restart: Auto-run when the machine comes back up.
834 post_update_hook: Code to call after update but immediately before
835 restart.
836
837 Returns:
838 Never if the update was successful (we just reboot).
839 False if the update was unnecessary (no update available).
840 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800841 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800842 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800843
Jon Salz5c344f62012-07-13 14:31:16 +0800844 def pre_update_hook():
845 if auto_run_on_restart:
846 self.state_instance.set_shared_data('tests_after_shutdown',
847 FORCE_AUTO_RUN)
848 self.state_instance.close()
849
Jon Salzeb42f0d2012-07-27 19:14:04 +0800850 if updater.TryUpdate(pre_update_hook=pre_update_hook):
851 if post_update_hook:
852 post_update_hook()
853 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800854
855 def init(self, args=None, env=None):
856 '''Initializes Goofy.
857
858 Args:
859 args: A list of command-line arguments. Uses sys.argv if
860 args is None.
861 env: An Environment instance to use (or None to choose
862 FakeChrootEnvironment or DUTEnvironment as appropriate).
863 '''
864 parser = OptionParser()
865 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800866 action='store_true',
867 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800868 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800869 metavar='FILE',
870 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800871 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800872 action='store_true',
873 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800874 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800875 choices=['none', 'gtk', 'chrome'],
876 default=('chrome' if utils.in_chroot() else 'gtk'),
877 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800878 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800879 type='int', default=1,
880 help=('Factor by which to scale UI '
881 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800882 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800883 metavar='FILE',
884 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800885 (self.options, self.args) = parser.parse_args(args)
886
Jon Salz46b89562012-07-05 11:49:22 +0800887 # Make sure factory directories exist.
888 factory.get_log_root()
889 factory.get_state_root()
890 factory.get_test_data_root()
891
Jon Salz0697cbf2012-07-04 15:14:04 +0800892 global _inited_logging # pylint: disable=W0603
893 if not _inited_logging:
894 factory.init_logging('goofy', verbose=self.options.verbose)
895 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800896
Jon Salzee85d522012-07-17 14:34:46 +0800897 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +0800898 self.event_log = EventLog('goofy')
899
900 if (not suppress_chroot_warning and
901 factory.in_chroot() and
902 self.options.ui == 'gtk' and
903 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
904 # That's not going to work! Tell the user how to run
905 # this way.
906 logging.warn(GOOFY_IN_CHROOT_WARNING)
907 time.sleep(1)
908
909 if env:
910 self.env = env
911 elif factory.in_chroot():
912 self.env = test_environment.FakeChrootEnvironment()
913 logging.warn(
914 'Using chroot environment: will not actually run autotests')
915 else:
916 self.env = test_environment.DUTEnvironment()
917 self.env.goofy = self
918
919 if self.options.restart:
920 state.clear_state()
921
922 if self.options.print_test_list:
Jon Salzeb42f0d2012-07-27 19:14:04 +0800923 print factory.read_test_list(
924 self.options.print_test_list).__repr__(recursive=True)
Jon Salz0697cbf2012-07-04 15:14:04 +0800925 return
926
927 if self.options.ui_scale_factor != 1 and utils.in_qemu():
928 logging.warn(
929 'In QEMU; ignoring ui_scale_factor argument')
930 self.options.ui_scale_factor = 1
931
932 logging.info('Started')
933
934 self.start_state_server()
935 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
936 self.state_instance.set_shared_data('ui_scale_factor',
937 self.options.ui_scale_factor)
938 self.last_shutdown_time = (
939 self.state_instance.get_shared_data('shutdown_time', optional=True))
940 self.state_instance.del_shared_data('shutdown_time', optional=True)
941
942 if not self.options.test_list:
943 self.options.test_list = find_test_list()
944 if not self.options.test_list:
945 logging.error('No test list. Aborting.')
946 sys.exit(1)
947 logging.info('Using test list %s', self.options.test_list)
948
949 self.test_list = factory.read_test_list(
950 self.options.test_list,
Jon Salzeb42f0d2012-07-27 19:14:04 +0800951 self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800952 if not self.state_instance.has_shared_data('ui_lang'):
953 self.state_instance.set_shared_data('ui_lang',
954 self.test_list.options.ui_lang)
955 self.state_instance.set_shared_data(
956 'test_list_options',
957 self.test_list.options.__dict__)
958 self.state_instance.test_list = self.test_list
959
Jon Salz8fa8e832012-07-13 19:04:09 +0800960 if self.test_list.options.time_sanitizer:
961 self.time_sanitizer = time_sanitizer.TimeSanitizer(
962 base_time=time_sanitizer.GetBaseTimeFromFile(
963 # lsb-factory is written by the factory install shim during
964 # installation, so it should have a good time obtained from
965 # the mini-Omaha server.
966 '/usr/local/etc/lsb-factory'))
967 self.time_sanitizer.RunOnce()
968
Jon Salz0697cbf2012-07-04 15:14:04 +0800969 self.init_states()
970 self.start_event_server()
971 self.connection_manager = self.env.create_connection_manager(
972 self.test_list.options.wlans)
973 # Note that we create a log watcher even if
974 # sync_event_log_period_secs isn't set (no background
975 # syncing), since we may use it to flush event logs as well.
976 self.log_watcher = EventLogWatcher(
977 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +0800978 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +0800979 if self.test_list.options.sync_event_log_period_secs:
980 self.log_watcher.StartWatchThread()
981
982 self.update_system_info()
983
Vic Yang4953fc12012-07-26 16:19:53 +0800984 assert ((self.test_list.options.min_charge_pct is None) ==
985 (self.test_list.options.max_charge_pct is None))
986 if (self.test_list.options.min_charge_pct and
987 self.test_list.options.max_charge_pct):
988 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
989 self.test_list.options.max_charge_pct)
990
Jon Salz0697cbf2012-07-04 15:14:04 +0800991 os.environ['CROS_FACTORY'] = '1'
992 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
993
994 # Set CROS_UI since some behaviors in ui.py depend on the
995 # particular UI in use. TODO(jsalz): Remove this (and all
996 # places it is used) when the GTK UI is removed.
997 os.environ['CROS_UI'] = self.options.ui
998
999 if self.options.ui == 'chrome':
1000 self.env.launch_chrome()
1001 logging.info('Waiting for a web socket connection')
1002 self.web_socket_manager.wait()
1003
1004 # Wait for the test widget size to be set; this is done in
1005 # an asynchronous RPC so there is a small chance that the
1006 # web socket might be opened first.
1007 for _ in range(100): # 10 s
1008 try:
1009 if self.state_instance.get_shared_data('test_widget_size'):
1010 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001011 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001012 pass # Retry
1013 time.sleep(0.1) # 100 ms
1014 else:
1015 logging.warn('Never received test_widget_size from UI')
1016 elif self.options.ui == 'gtk':
1017 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001018
Jon Salz0697cbf2012-07-04 15:14:04 +08001019 def state_change_callback(test, test_state):
1020 self.event_client.post_event(
1021 Event(Event.Type.STATE_CHANGE,
1022 path=test.path, state=test_state))
1023 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001024
Jon Salza6711d72012-07-18 14:33:03 +08001025 for handler in self.on_ui_startup:
1026 handler()
1027
1028 self.prespawner = Prespawner()
1029 self.prespawner.start()
1030
Jon Salz0697cbf2012-07-04 15:14:04 +08001031 try:
1032 tests_after_shutdown = self.state_instance.get_shared_data(
1033 'tests_after_shutdown')
1034 except KeyError:
1035 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001036
Jon Salz5c344f62012-07-13 14:31:16 +08001037 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1038 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001039 logging.info('Resuming tests after shutdown: %s',
1040 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001041 self.tests_to_run.extend(
1042 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1043 self.run_queue.put(self.run_next_test)
1044 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001045 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001046 self.run_queue.put(
1047 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001048 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001049
Jon Salz0697cbf2012-07-04 15:14:04 +08001050 def run(self):
1051 '''Runs Goofy.'''
1052 # Process events forever.
1053 while self.run_once(True):
1054 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001055
Jon Salz0697cbf2012-07-04 15:14:04 +08001056 def run_once(self, block=False):
1057 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001058
Jon Salz0697cbf2012-07-04 15:14:04 +08001059 Args:
1060 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001061
Jon Salz0697cbf2012-07-04 15:14:04 +08001062 Returns:
1063 True to keep going or False to shut down.
1064 '''
1065 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001066 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001067 # Nothing on the run queue.
1068 self._run_queue_idle()
1069 if block:
1070 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001071 try:
1072 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1073 except Queue.Empty:
1074 # Keep going (calling _run_queue_idle() again at the top of
1075 # the loop)
1076 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001077 # ...and grab anything else that showed up at the same
1078 # time.
1079 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001080 else:
1081 break
Jon Salz51528e12012-07-02 18:54:45 +08001082
Jon Salz0697cbf2012-07-04 15:14:04 +08001083 for event in events:
1084 if not event:
1085 # Shutdown request.
1086 self.run_queue.task_done()
1087 return False
Jon Salz51528e12012-07-02 18:54:45 +08001088
Jon Salz0697cbf2012-07-04 15:14:04 +08001089 try:
1090 event()
Jon Salz85a39882012-07-05 16:45:04 +08001091 except: # pylint: disable=W0702
1092 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001093 self.record_exception(traceback.format_exception_only(
1094 *sys.exc_info()[:2]))
1095 # But keep going
1096 finally:
1097 self.run_queue.task_done()
1098 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001099
Jon Salz8fa8e832012-07-13 19:04:09 +08001100 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001101 '''Writes out current time and tries to sync with shopfloor server.'''
1102 if not self.time_sanitizer:
1103 return
1104
1105 # Write out the current time.
1106 self.time_sanitizer.SaveTime()
1107
Jon Salz8fa8e832012-07-13 19:04:09 +08001108 if ((not self.test_list.options.sync_time_period_secs) or
1109 (not self.time_sanitizer) or
1110 self.time_synced or
1111 factory.in_chroot()):
1112 # Not enabled or already succeeded.
1113 return
1114
1115 now = time.time()
1116 if self.last_sync_time and (
1117 now - self.last_sync_time <
1118 self.test_list.options.sync_time_period_secs):
1119 # Not yet time for another check.
1120 return
1121 self.last_sync_time = now
1122
1123 def target():
1124 try:
1125 self.time_sanitizer.SyncWithShopfloor()
1126 self.time_synced = True
1127 except: # pylint: disable=W0702
1128 # Oh well. Log an error (but no trace)
1129 logging.info(
1130 'Unable to get time from shopfloor server: %s',
1131 utils.FormatExceptionOnly())
1132
1133 thread = threading.Thread(target=target)
1134 thread.daemon = True
1135 thread.start()
1136
Jon Salz0697cbf2012-07-04 15:14:04 +08001137 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001138 '''Invoked when the run queue has no events.
1139
1140 This method must not raise exception.
1141 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001142 now = time.time()
1143 if (self.last_idle and
1144 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1145 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1146 # 1) seconds.
1147 return
1148
1149 self.last_idle = now
1150
Jon Salz0697cbf2012-07-04 15:14:04 +08001151 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001152 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001153 self.sync_time_in_background()
Vic Yang4953fc12012-07-26 16:19:53 +08001154 if self.charge_manager:
1155 self.charge_manager.AdjustChargeState()
Jon Salz57717ca2012-04-04 16:47:25 +08001156
Jon Salz16d10542012-07-23 12:18:45 +08001157 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001158 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001159
Jon Salz0697cbf2012-07-04 15:14:04 +08001160 Attempts to upload the event logs to the shopfloor server.
1161 '''
1162 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1163 start_time = time.time()
1164 logging.info('Syncing %s', description)
1165 shopfloor_client = shopfloor.get_instance(
1166 detect=True,
1167 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzb10cf512012-08-09 17:29:21 +08001168 shopfloor_client.UploadEvent(log_name, Binary(chunk))
Jon Salz0697cbf2012-07-04 15:14:04 +08001169 logging.info(
1170 'Successfully synced %s in %.03f s',
1171 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001172
Jon Salz0697cbf2012-07-04 15:14:04 +08001173 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1174 root=None):
1175 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001176
Jon Salz0697cbf2012-07-04 15:14:04 +08001177 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001178
Jon Salz0697cbf2012-07-04 15:14:04 +08001179 Args:
1180 starting_at: If provided, only auto-runs tests beginning with
1181 this test.
1182 '''
1183 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001184
Jon Salz0697cbf2012-07-04 15:14:04 +08001185 if starting_at:
1186 # Make sure they passed a test, not a string.
1187 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001188
Jon Salz0697cbf2012-07-04 15:14:04 +08001189 tests_to_reset = []
1190 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001191
Jon Salz0697cbf2012-07-04 15:14:04 +08001192 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001193
Jon Salz0697cbf2012-07-04 15:14:04 +08001194 for test in root.get_top_level_tests():
1195 if starting_at:
1196 if test == starting_at:
1197 # We've found starting_at; do auto-run on all
1198 # subsequent tests.
1199 found_starting_at = True
1200 if not found_starting_at:
1201 # Don't start this guy yet
1202 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001203
Jon Salz0697cbf2012-07-04 15:14:04 +08001204 status = test.get_state().status
1205 if status == TestState.ACTIVE or status in statuses_to_run:
1206 # Reset the test (later; we will need to abort
1207 # all active tests first).
1208 tests_to_reset.append(test)
1209 if status in statuses_to_run:
1210 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001211
Jon Salz0697cbf2012-07-04 15:14:04 +08001212 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001213
Jon Salz0697cbf2012-07-04 15:14:04 +08001214 # Reset all statuses of the tests to run (in case any tests were active;
1215 # we want them to be run again).
1216 for test_to_reset in tests_to_reset:
1217 for test in test_to_reset.walk():
1218 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001219
Jon Salz0697cbf2012-07-04 15:14:04 +08001220 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001221
Jon Salz0697cbf2012-07-04 15:14:04 +08001222 def restart_tests(self, root=None):
1223 '''Restarts all tests.'''
1224 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001225
Jon Salz0697cbf2012-07-04 15:14:04 +08001226 self.abort_active_tests()
1227 for test in root.walk():
1228 test.update_state(status=TestState.UNTESTED)
1229 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001230
Jon Salz0697cbf2012-07-04 15:14:04 +08001231 def auto_run(self, starting_at=None, root=None):
1232 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001233
Jon Salz0697cbf2012-07-04 15:14:04 +08001234 Args:
1235 starting_at: If provide, only auto-runs tests beginning with
1236 this test.
1237 '''
1238 root = root or self.test_list
1239 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1240 starting_at=starting_at,
1241 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001242
Jon Salz0697cbf2012-07-04 15:14:04 +08001243 def re_run_failed(self, root=None):
1244 '''Re-runs failed tests.'''
1245 root = root or self.test_list
1246 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001247
Jon Salz0697cbf2012-07-04 15:14:04 +08001248 def show_review_information(self):
1249 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001250
Jon Salz0697cbf2012-07-04 15:14:04 +08001251 The information screene is rendered by main UI program (ui.py), so in
1252 goofy we only need to kill all active tests, set them as untested, and
1253 clear remaining tests.
1254 '''
1255 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001256 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001257
Jon Salz0697cbf2012-07-04 15:14:04 +08001258 def handle_switch_test(self, event):
1259 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001260
Jon Salz0697cbf2012-07-04 15:14:04 +08001261 @param event: The SWITCH_TEST event.
1262 '''
1263 test = self.test_list.lookup_path(event.path)
1264 if not test:
1265 logging.error('Unknown test %r', event.key)
1266 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001267
Jon Salz0697cbf2012-07-04 15:14:04 +08001268 invoc = self.invocations.get(test)
1269 if invoc and test.backgroundable:
1270 # Already running: just bring to the front if it
1271 # has a UI.
1272 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001273 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001274 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001275
Jon Salz0697cbf2012-07-04 15:14:04 +08001276 self.abort_active_tests()
1277 for t in test.walk():
1278 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001279
Jon Salz0697cbf2012-07-04 15:14:04 +08001280 if self.test_list.options.auto_run_on_keypress:
1281 self.auto_run(starting_at=test)
1282 else:
1283 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001284
Jon Salz0697cbf2012-07-04 15:14:04 +08001285 def wait(self):
1286 '''Waits for all pending invocations.
1287
1288 Useful for testing.
1289 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001290 while self.invocations:
1291 for k, v in self.invocations.iteritems():
1292 logging.info('Waiting for %s to complete...', k)
1293 v.thread.join()
1294 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001295
1296 def check_exceptions(self):
1297 '''Raises an error if any exceptions have occurred in
1298 invocation threads.'''
1299 if self.exceptions:
1300 raise RuntimeError('Exception in invocation thread: %r' %
1301 self.exceptions)
1302
1303 def record_exception(self, msg):
1304 '''Records an exception in an invocation thread.
1305
1306 An exception with the given message will be rethrown when
1307 Goofy is destroyed.'''
1308 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001309
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001310
1311if __name__ == '__main__':
Jon Salz0697cbf2012-07-04 15:14:04 +08001312 Goofy().main()