blob: 96ff8cf7a30262758c194ce31cbdb07d61252826 [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
Jon Salz83591782012-06-26 11:09:58 +080025from cros.factory.goofy.prespawner import Prespawner
26from cros.factory.test import factory
27from cros.factory.test import state
28from cros.factory.test.factory import TestState
29from cros.factory.goofy import updater
Jon Salz51528e12012-07-02 18:54:45 +080030from cros.factory.goofy.event_log_watcher import EventLogWatcher
31from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080032from cros.factory.test import utils
33from cros.factory.test.event import Event
34from cros.factory.test.event import EventClient
35from cros.factory.test.event import EventServer
Jon Salzee85d522012-07-17 14:34:46 +080036from cros.factory import event_log
Jon Salz83591782012-06-26 11:09:58 +080037from cros.factory.event_log import EventLog
38from cros.factory.goofy.invocation import TestInvocation
Jon Salz16d10542012-07-23 12:18:45 +080039from cros.factory.goofy.goofy_rpc import GoofyRPC
Jon Salz83591782012-06-26 11:09:58 +080040from cros.factory.goofy import system
41from cros.factory.goofy import test_environment
Jon Salz8fa8e832012-07-13 19:04:09 +080042from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080043from cros.factory.goofy.web_socket_manager import WebSocketManager
Vic Yang4953fc12012-07-26 16:19:53 +080044from cros.factory.goofy.charge_manager import ChargeManager
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
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800449
Jon Salz0697cbf2012-07-04 15:14:04 +0800450 # Any 'active' tests should be marked as failed now.
451 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800452 if not test.is_leaf():
453 # Don't bother with parents; they will be updated when their
454 # children are updated.
455 continue
456
Jon Salz0697cbf2012-07-04 15:14:04 +0800457 test_state = test.get_state()
458 if test_state.status != TestState.ACTIVE:
459 continue
460 if isinstance(test, factory.ShutdownStep):
461 # Shutdown while the test was active - that's good.
462 self.handle_shutdown_complete(test, test_state)
463 else:
464 # Unexpected shutdown. Grab /var/log/messages for context.
465 if var_log_messages is None:
466 try:
467 var_log_messages = (
468 utils.var_log_messages_before_reboot())
469 # Write it to the log, to make it easier to
470 # correlate with /var/log/messages.
471 logging.info(
472 'Unexpected shutdown. '
473 'Tail of /var/log/messages before last reboot:\n'
474 '%s', ('\n'.join(
475 ' ' + x for x in var_log_messages)))
476 except: # pylint: disable=W0702
477 logging.exception('Unable to grok /var/log/messages')
478 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800479
Jon Salz0697cbf2012-07-04 15:14:04 +0800480 error_msg = 'Unexpected shutdown while test was running'
481 self.event_log.Log('end_test',
482 path=test.path,
483 status=TestState.FAILED,
484 invocation=test.get_state().invocation,
485 error_msg=error_msg,
486 var_log_messages='\n'.join(var_log_messages))
487 test.update_state(
488 status=TestState.FAILED,
489 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800490
Jon Salz50efe942012-07-26 11:54:10 +0800491 if not test.never_fails:
492 # For "never_fails" tests (such as "Start"), don't cancel
493 # pending tests, since reboot is expected.
494 factory.console.info('Unexpected shutdown while test %s '
495 'running; cancelling any pending tests',
496 test.path)
497 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800498
Jon Salz0697cbf2012-07-04 15:14:04 +0800499 def show_next_active_test(self):
500 '''
501 Rotates to the next visible active test.
502 '''
503 self.reap_completed_tests()
504 active_tests = [
505 t for t in self.test_list.walk()
506 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
507 if not active_tests:
508 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800509
Jon Salz0697cbf2012-07-04 15:14:04 +0800510 try:
511 next_test = active_tests[
512 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
513 except ValueError: # visible_test not present in active_tests
514 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800515
Jon Salz0697cbf2012-07-04 15:14:04 +0800516 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800517
Jon Salz0697cbf2012-07-04 15:14:04 +0800518 def handle_event(self, event):
519 '''
520 Handles an event from the event server.
521 '''
522 handler = self.event_handlers.get(event.type)
523 if handler:
524 handler(event)
525 else:
526 # We don't register handlers for all event types - just ignore
527 # this event.
528 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800529
Jon Salz0697cbf2012-07-04 15:14:04 +0800530 def run_next_test(self):
531 '''
532 Runs the next eligible test (or tests) in self.tests_to_run.
533 '''
534 self.reap_completed_tests()
535 while self.tests_to_run:
536 logging.debug('Tests to run: %s',
537 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800538
Jon Salz0697cbf2012-07-04 15:14:04 +0800539 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800540
Jon Salz0697cbf2012-07-04 15:14:04 +0800541 if test in self.invocations:
542 logging.info('Next test %s is already running', test.path)
543 self.tests_to_run.popleft()
544 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800545
Jon Salza1412922012-07-23 16:04:17 +0800546 for requirement in test.require_run:
547 for i in requirement.test.walk():
548 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800549 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800550 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800551 return
552
Jon Salz0697cbf2012-07-04 15:14:04 +0800553 if self.invocations and not (test.backgroundable and all(
554 [x.backgroundable for x in self.invocations])):
555 logging.debug('Waiting for non-backgroundable tests to '
556 'complete before running %s', test.path)
557 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800558
Jon Salz0697cbf2012-07-04 15:14:04 +0800559 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800560
Jon Salz304a75d2012-07-06 11:14:15 +0800561 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800562 for requirement in test.require_run:
563 for i in requirement.test.walk():
564 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800565 # We've hit this test itself; stop checking
566 break
Jon Salza1412922012-07-23 16:04:17 +0800567 if ((i.get_state().status == TestState.UNTESTED) or
568 (requirement.passed and i.get_state().status !=
569 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800570 # Found an untested test; move on to the next
571 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800572 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800573 break
574
575 if untested:
576 untested_paths = ', '.join(sorted([x.path for x in untested]))
577 if self.state_instance.get_shared_data('engineering_mode',
578 optional=True):
579 # In engineering mode, we'll let it go.
580 factory.console.warn('In engineering mode; running '
581 '%s even though required tests '
582 '[%s] have not completed',
583 test.path, untested_paths)
584 else:
585 # Not in engineering mode; mark it failed.
586 error_msg = ('Required tests [%s] have not been run yet'
587 % untested_paths)
588 factory.console.error('Not running %s: %s',
589 test.path, error_msg)
590 test.update_state(status=TestState.FAILED,
591 error_msg=error_msg)
592 continue
593
Jon Salz0697cbf2012-07-04 15:14:04 +0800594 if isinstance(test, factory.ShutdownStep):
595 if os.path.exists(NO_REBOOT_FILE):
596 test.update_state(
597 status=TestState.FAILED, increment_count=1,
598 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800599 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800600 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800601
Jon Salz0697cbf2012-07-04 15:14:04 +0800602 test.update_state(status=TestState.ACTIVE, increment_count=1,
603 error_msg='', shutdown_count=0)
604 if self._prompt_cancel_shutdown(test, 1):
605 self.event_log.Log('reboot_cancelled')
606 test.update_state(
607 status=TestState.FAILED, increment_count=1,
608 error_msg='Shutdown aborted by operator',
609 shutdown_count=0)
610 return
Jon Salz2f757d42012-06-27 17:06:42 +0800611
Jon Salz0697cbf2012-07-04 15:14:04 +0800612 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800613 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800614 'tests_after_shutdown',
615 [t.path for t in self.tests_to_run])
616 # Save shutdown time
617 self.state_instance.set_shared_data('shutdown_time',
618 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800619
Jon Salz0697cbf2012-07-04 15:14:04 +0800620 with self.env.lock:
621 self.event_log.Log('shutdown', operation=test.operation)
622 shutdown_result = self.env.shutdown(test.operation)
623 if shutdown_result:
624 # That's all, folks!
625 self.run_queue.put(None)
626 return
627 else:
628 # Just pass (e.g., in the chroot).
629 test.update_state(status=TestState.PASSED)
630 self.state_instance.set_shared_data(
631 'tests_after_shutdown', None)
632 # Send event with no fields to indicate that there is no
633 # longer a pending shutdown.
634 self.event_client.post_event(Event(
635 Event.Type.PENDING_SHUTDOWN))
636 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800637
Jon Salz1acc8742012-07-17 17:45:55 +0800638 self._run_test(test, test.iterations)
639
640 def _run_test(self, test, iterations_left=None):
641 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
642 new_state = test.update_state(
643 status=TestState.ACTIVE, increment_count=1, error_msg='',
644 invocation=invoc.uuid, iterations_left=iterations_left)
645 invoc.count = new_state.count
646
647 self.invocations[test] = invoc
648 if self.visible_test is None and test.has_ui:
649 self.set_visible_test(test)
650 self.check_connection_manager()
651 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800652
Jon Salz0697cbf2012-07-04 15:14:04 +0800653 def check_connection_manager(self):
654 exclusive_tests = [
655 test.path
656 for test in self.invocations
657 if test.is_exclusive(
658 factory.FactoryTest.EXCLUSIVE_OPTIONS.NETWORKING)]
659 if exclusive_tests:
660 # Make sure networking is disabled.
661 if self.network_enabled:
662 logging.info('Disabling network, as requested by %s',
663 exclusive_tests)
664 self.connection_manager.DisableNetworking()
665 self.network_enabled = False
666 else:
667 # Make sure networking is enabled.
668 if not self.network_enabled:
669 logging.info('Re-enabling network')
670 self.connection_manager.EnableNetworking()
671 self.network_enabled = True
Jon Salz5da61e62012-05-31 13:06:22 +0800672
cychiang21886742012-07-05 15:16:32 +0800673 def check_for_updates(self):
674 '''
675 Schedules an asynchronous check for updates if necessary.
676 '''
677 if not self.test_list.options.update_period_secs:
678 # Not enabled.
679 return
680
681 now = time.time()
682 if self.last_update_check and (
683 now - self.last_update_check <
684 self.test_list.options.update_period_secs):
685 # Not yet time for another check.
686 return
687
688 self.last_update_check = now
689
690 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
691 if reached_shopfloor:
692 new_update_md5sum = md5sum if needs_update else None
693 if system.SystemInfo.update_md5sum != new_update_md5sum:
694 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
695 system.SystemInfo.update_md5sum = new_update_md5sum
696 self.run_queue.put(self.update_system_info)
697
698 updater.CheckForUpdateAsync(
699 handle_check_for_update,
700 self.test_list.options.shopfloor_timeout_secs)
701
Jon Salza6711d72012-07-18 14:33:03 +0800702 def cancel_pending_tests(self):
703 '''Cancels any tests in the run queue.'''
704 self.run_tests([])
705
Jon Salz0697cbf2012-07-04 15:14:04 +0800706 def run_tests(self, subtrees, untested_only=False):
707 '''
708 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800709
Jon Salz0697cbf2012-07-04 15:14:04 +0800710 The tests are run in order unless one fails (then stops).
711 Backgroundable tests are run simultaneously; when a foreground test is
712 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800713
Jon Salz0697cbf2012-07-04 15:14:04 +0800714 @param subtrees: Node or nodes containing tests to run (may either be
715 a single test or a list). Duplicates will be ignored.
716 '''
717 if type(subtrees) != list:
718 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800719
Jon Salz0697cbf2012-07-04 15:14:04 +0800720 # Nodes we've seen so far, to avoid duplicates.
721 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800722
Jon Salz0697cbf2012-07-04 15:14:04 +0800723 self.tests_to_run = deque()
724 for subtree in subtrees:
725 for test in subtree.walk():
726 if test in seen:
727 continue
728 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800729
Jon Salz0697cbf2012-07-04 15:14:04 +0800730 if not test.is_leaf():
731 continue
732 if (untested_only and
733 test.get_state().status != TestState.UNTESTED):
734 continue
735 self.tests_to_run.append(test)
736 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800737
Jon Salz0697cbf2012-07-04 15:14:04 +0800738 def reap_completed_tests(self):
739 '''
740 Removes completed tests from the set of active tests.
741
742 Also updates the visible test if it was reaped.
743 '''
744 for t, v in dict(self.invocations).iteritems():
745 if v.is_completed():
Jon Salz1acc8742012-07-17 17:45:55 +0800746 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800747 del self.invocations[t]
748
Jon Salz1acc8742012-07-17 17:45:55 +0800749 if new_state.iterations_left and new_state.status == TestState.PASSED:
750 # Play it again, Sam!
751 self._run_test(t)
752
Jon Salz0697cbf2012-07-04 15:14:04 +0800753 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800754 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800755 self.set_visible_test(None)
756 # Make the first running test, if any, the visible test
757 for t in self.test_list.walk():
758 if t in self.invocations:
759 self.set_visible_test(t)
760 break
761
Jon Salz85a39882012-07-05 16:45:04 +0800762 def kill_active_tests(self, abort, root=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800763 '''
764 Kills and waits for all active tests.
765
Jon Salz85a39882012-07-05 16:45:04 +0800766 Args:
767 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800768 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800769 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800770 '''
771 self.reap_completed_tests()
772 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800773 if root and not test.has_ancestor(root):
774 continue
775
Jon Salz0697cbf2012-07-04 15:14:04 +0800776 factory.console.info('Killing active test %s...' % test.path)
777 invoc.abort_and_join()
778 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800779 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800780 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800781
Jon Salz0697cbf2012-07-04 15:14:04 +0800782 if not abort:
783 test.update_state(status=TestState.UNTESTED)
784 self.reap_completed_tests()
785
Jon Salz85a39882012-07-05 16:45:04 +0800786 def stop(self, root=None, fail=False):
787 self.kill_active_tests(fail, root)
788 # Remove any tests in the run queue under the root.
789 self.tests_to_run = deque([x for x in self.tests_to_run
790 if root and not x.has_ancestor(root)])
791 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800792
793 def abort_active_tests(self):
794 self.kill_active_tests(True)
795
796 def main(self):
797 try:
798 self.init()
799 self.event_log.Log('goofy_init',
800 success=True)
801 except:
802 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800803 try:
Jon Salz0697cbf2012-07-04 15:14:04 +0800804 self.event_log.Log('goofy_init',
805 success=False,
806 trace=traceback.format_exc())
807 except: # pylint: disable=W0702
808 pass
809 raise
810
811 self.run()
812
813 def update_system_info(self):
814 '''Updates system info.'''
815 system_info = system.SystemInfo()
816 self.state_instance.set_shared_data('system_info', system_info.__dict__)
817 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
818 system_info=system_info.__dict__))
819 logging.info('System info: %r', system_info.__dict__)
820
Jon Salzeb42f0d2012-07-27 19:14:04 +0800821 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
822 '''Commences updating factory software.
823
824 Args:
825 auto_run_on_restart: Auto-run when the machine comes back up.
826 post_update_hook: Code to call after update but immediately before
827 restart.
828
829 Returns:
830 Never if the update was successful (we just reboot).
831 False if the update was unnecessary (no update available).
832 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800833 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +0800834 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800835
Jon Salz5c344f62012-07-13 14:31:16 +0800836 def pre_update_hook():
837 if auto_run_on_restart:
838 self.state_instance.set_shared_data('tests_after_shutdown',
839 FORCE_AUTO_RUN)
840 self.state_instance.close()
841
Jon Salzeb42f0d2012-07-27 19:14:04 +0800842 if updater.TryUpdate(pre_update_hook=pre_update_hook):
843 if post_update_hook:
844 post_update_hook()
845 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800846
847 def init(self, args=None, env=None):
848 '''Initializes Goofy.
849
850 Args:
851 args: A list of command-line arguments. Uses sys.argv if
852 args is None.
853 env: An Environment instance to use (or None to choose
854 FakeChrootEnvironment or DUTEnvironment as appropriate).
855 '''
856 parser = OptionParser()
857 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800858 action='store_true',
859 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800860 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800861 metavar='FILE',
862 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800863 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800864 action='store_true',
865 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800866 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +0800867 choices=['none', 'gtk', 'chrome'],
868 default=('chrome' if utils.in_chroot() else 'gtk'),
869 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800870 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +0800871 type='int', default=1,
872 help=('Factor by which to scale UI '
873 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +0800874 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +0800875 metavar='FILE',
876 help='Use FILE as test list')
Jon Salz0697cbf2012-07-04 15:14:04 +0800877 (self.options, self.args) = parser.parse_args(args)
878
Jon Salz46b89562012-07-05 11:49:22 +0800879 # Make sure factory directories exist.
880 factory.get_log_root()
881 factory.get_state_root()
882 factory.get_test_data_root()
883
Jon Salz0697cbf2012-07-04 15:14:04 +0800884 global _inited_logging # pylint: disable=W0603
885 if not _inited_logging:
886 factory.init_logging('goofy', verbose=self.options.verbose)
887 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800888
Jon Salzee85d522012-07-17 14:34:46 +0800889 event_log.IncrementBootSequence()
Jon Salz0697cbf2012-07-04 15:14:04 +0800890 self.event_log = EventLog('goofy')
891
892 if (not suppress_chroot_warning and
893 factory.in_chroot() and
894 self.options.ui == 'gtk' and
895 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
896 # That's not going to work! Tell the user how to run
897 # this way.
898 logging.warn(GOOFY_IN_CHROOT_WARNING)
899 time.sleep(1)
900
901 if env:
902 self.env = env
903 elif factory.in_chroot():
904 self.env = test_environment.FakeChrootEnvironment()
905 logging.warn(
906 'Using chroot environment: will not actually run autotests')
907 else:
908 self.env = test_environment.DUTEnvironment()
909 self.env.goofy = self
910
911 if self.options.restart:
912 state.clear_state()
913
914 if self.options.print_test_list:
Jon Salzeb42f0d2012-07-27 19:14:04 +0800915 print factory.read_test_list(
916 self.options.print_test_list).__repr__(recursive=True)
Jon Salz0697cbf2012-07-04 15:14:04 +0800917 return
918
919 if self.options.ui_scale_factor != 1 and utils.in_qemu():
920 logging.warn(
921 'In QEMU; ignoring ui_scale_factor argument')
922 self.options.ui_scale_factor = 1
923
924 logging.info('Started')
925
926 self.start_state_server()
927 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
928 self.state_instance.set_shared_data('ui_scale_factor',
929 self.options.ui_scale_factor)
930 self.last_shutdown_time = (
931 self.state_instance.get_shared_data('shutdown_time', optional=True))
932 self.state_instance.del_shared_data('shutdown_time', optional=True)
933
934 if not self.options.test_list:
935 self.options.test_list = find_test_list()
936 if not self.options.test_list:
937 logging.error('No test list. Aborting.')
938 sys.exit(1)
939 logging.info('Using test list %s', self.options.test_list)
940
941 self.test_list = factory.read_test_list(
942 self.options.test_list,
Jon Salzeb42f0d2012-07-27 19:14:04 +0800943 self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800944 if not self.state_instance.has_shared_data('ui_lang'):
945 self.state_instance.set_shared_data('ui_lang',
946 self.test_list.options.ui_lang)
947 self.state_instance.set_shared_data(
948 'test_list_options',
949 self.test_list.options.__dict__)
950 self.state_instance.test_list = self.test_list
951
Jon Salz8fa8e832012-07-13 19:04:09 +0800952 if self.test_list.options.time_sanitizer:
953 self.time_sanitizer = time_sanitizer.TimeSanitizer(
954 base_time=time_sanitizer.GetBaseTimeFromFile(
955 # lsb-factory is written by the factory install shim during
956 # installation, so it should have a good time obtained from
957 # the mini-Omaha server.
958 '/usr/local/etc/lsb-factory'))
959 self.time_sanitizer.RunOnce()
960
Jon Salz0697cbf2012-07-04 15:14:04 +0800961 self.init_states()
962 self.start_event_server()
963 self.connection_manager = self.env.create_connection_manager(
964 self.test_list.options.wlans)
965 # Note that we create a log watcher even if
966 # sync_event_log_period_secs isn't set (no background
967 # syncing), since we may use it to flush event logs as well.
968 self.log_watcher = EventLogWatcher(
969 self.test_list.options.sync_event_log_period_secs,
Jon Salz16d10542012-07-23 12:18:45 +0800970 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +0800971 if self.test_list.options.sync_event_log_period_secs:
972 self.log_watcher.StartWatchThread()
973
974 self.update_system_info()
975
Vic Yang4953fc12012-07-26 16:19:53 +0800976 assert ((self.test_list.options.min_charge_pct is None) ==
977 (self.test_list.options.max_charge_pct is None))
978 if (self.test_list.options.min_charge_pct and
979 self.test_list.options.max_charge_pct):
980 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
981 self.test_list.options.max_charge_pct)
982
Jon Salz0697cbf2012-07-04 15:14:04 +0800983 os.environ['CROS_FACTORY'] = '1'
984 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
985
986 # Set CROS_UI since some behaviors in ui.py depend on the
987 # particular UI in use. TODO(jsalz): Remove this (and all
988 # places it is used) when the GTK UI is removed.
989 os.environ['CROS_UI'] = self.options.ui
990
991 if self.options.ui == 'chrome':
992 self.env.launch_chrome()
993 logging.info('Waiting for a web socket connection')
994 self.web_socket_manager.wait()
995
996 # Wait for the test widget size to be set; this is done in
997 # an asynchronous RPC so there is a small chance that the
998 # web socket might be opened first.
999 for _ in range(100): # 10 s
1000 try:
1001 if self.state_instance.get_shared_data('test_widget_size'):
1002 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001003 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001004 pass # Retry
1005 time.sleep(0.1) # 100 ms
1006 else:
1007 logging.warn('Never received test_widget_size from UI')
1008 elif self.options.ui == 'gtk':
1009 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001010
Jon Salz0697cbf2012-07-04 15:14:04 +08001011 def state_change_callback(test, test_state):
1012 self.event_client.post_event(
1013 Event(Event.Type.STATE_CHANGE,
1014 path=test.path, state=test_state))
1015 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001016
Jon Salza6711d72012-07-18 14:33:03 +08001017 for handler in self.on_ui_startup:
1018 handler()
1019
1020 self.prespawner = Prespawner()
1021 self.prespawner.start()
1022
Jon Salz0697cbf2012-07-04 15:14:04 +08001023 try:
1024 tests_after_shutdown = self.state_instance.get_shared_data(
1025 'tests_after_shutdown')
1026 except KeyError:
1027 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001028
Jon Salz5c344f62012-07-13 14:31:16 +08001029 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1030 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001031 logging.info('Resuming tests after shutdown: %s',
1032 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001033 self.tests_to_run.extend(
1034 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1035 self.run_queue.put(self.run_next_test)
1036 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001037 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001038 self.run_queue.put(
1039 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001040 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001041
Jon Salz0697cbf2012-07-04 15:14:04 +08001042 def run(self):
1043 '''Runs Goofy.'''
1044 # Process events forever.
1045 while self.run_once(True):
1046 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001047
Jon Salz0697cbf2012-07-04 15:14:04 +08001048 def run_once(self, block=False):
1049 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001050
Jon Salz0697cbf2012-07-04 15:14:04 +08001051 Args:
1052 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001053
Jon Salz0697cbf2012-07-04 15:14:04 +08001054 Returns:
1055 True to keep going or False to shut down.
1056 '''
1057 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001058 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001059 # Nothing on the run queue.
1060 self._run_queue_idle()
1061 if block:
1062 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001063 try:
1064 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1065 except Queue.Empty:
1066 # Keep going (calling _run_queue_idle() again at the top of
1067 # the loop)
1068 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001069 # ...and grab anything else that showed up at the same
1070 # time.
1071 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001072 else:
1073 break
Jon Salz51528e12012-07-02 18:54:45 +08001074
Jon Salz0697cbf2012-07-04 15:14:04 +08001075 for event in events:
1076 if not event:
1077 # Shutdown request.
1078 self.run_queue.task_done()
1079 return False
Jon Salz51528e12012-07-02 18:54:45 +08001080
Jon Salz0697cbf2012-07-04 15:14:04 +08001081 try:
1082 event()
Jon Salz85a39882012-07-05 16:45:04 +08001083 except: # pylint: disable=W0702
1084 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001085 self.record_exception(traceback.format_exception_only(
1086 *sys.exc_info()[:2]))
1087 # But keep going
1088 finally:
1089 self.run_queue.task_done()
1090 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001091
Jon Salz8fa8e832012-07-13 19:04:09 +08001092 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001093 '''Writes out current time and tries to sync with shopfloor server.'''
1094 if not self.time_sanitizer:
1095 return
1096
1097 # Write out the current time.
1098 self.time_sanitizer.SaveTime()
1099
Jon Salz8fa8e832012-07-13 19:04:09 +08001100 if ((not self.test_list.options.sync_time_period_secs) or
1101 (not self.time_sanitizer) or
1102 self.time_synced or
1103 factory.in_chroot()):
1104 # Not enabled or already succeeded.
1105 return
1106
1107 now = time.time()
1108 if self.last_sync_time and (
1109 now - self.last_sync_time <
1110 self.test_list.options.sync_time_period_secs):
1111 # Not yet time for another check.
1112 return
1113 self.last_sync_time = now
1114
1115 def target():
1116 try:
1117 self.time_sanitizer.SyncWithShopfloor()
1118 self.time_synced = True
1119 except: # pylint: disable=W0702
1120 # Oh well. Log an error (but no trace)
1121 logging.info(
1122 'Unable to get time from shopfloor server: %s',
1123 utils.FormatExceptionOnly())
1124
1125 thread = threading.Thread(target=target)
1126 thread.daemon = True
1127 thread.start()
1128
Jon Salz0697cbf2012-07-04 15:14:04 +08001129 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001130 '''Invoked when the run queue has no events.
1131
1132 This method must not raise exception.
1133 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001134 now = time.time()
1135 if (self.last_idle and
1136 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1137 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1138 # 1) seconds.
1139 return
1140
1141 self.last_idle = now
1142
Jon Salz0697cbf2012-07-04 15:14:04 +08001143 self.check_connection_manager()
cychiang21886742012-07-05 15:16:32 +08001144 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001145 self.sync_time_in_background()
Vic Yang4953fc12012-07-26 16:19:53 +08001146 if self.charge_manager:
1147 self.charge_manager.AdjustChargeState()
Jon Salz57717ca2012-04-04 16:47:25 +08001148
Jon Salz16d10542012-07-23 12:18:45 +08001149 def handle_event_logs(self, log_name, chunk):
Jon Salz0697cbf2012-07-04 15:14:04 +08001150 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001151
Jon Salz0697cbf2012-07-04 15:14:04 +08001152 Attempts to upload the event logs to the shopfloor server.
1153 '''
1154 description = 'event logs (%s, %d bytes)' % (log_name, len(chunk))
1155 start_time = time.time()
1156 logging.info('Syncing %s', description)
1157 shopfloor_client = shopfloor.get_instance(
1158 detect=True,
1159 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzb10cf512012-08-09 17:29:21 +08001160 shopfloor_client.UploadEvent(log_name, Binary(chunk))
Jon Salz0697cbf2012-07-04 15:14:04 +08001161 logging.info(
1162 'Successfully synced %s in %.03f s',
1163 description, time.time() - start_time)
Jon Salz57717ca2012-04-04 16:47:25 +08001164
Jon Salz0697cbf2012-07-04 15:14:04 +08001165 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1166 root=None):
1167 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001168
Jon Salz0697cbf2012-07-04 15:14:04 +08001169 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001170
Jon Salz0697cbf2012-07-04 15:14:04 +08001171 Args:
1172 starting_at: If provided, only auto-runs tests beginning with
1173 this test.
1174 '''
1175 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001176
Jon Salz0697cbf2012-07-04 15:14:04 +08001177 if starting_at:
1178 # Make sure they passed a test, not a string.
1179 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001180
Jon Salz0697cbf2012-07-04 15:14:04 +08001181 tests_to_reset = []
1182 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001183
Jon Salz0697cbf2012-07-04 15:14:04 +08001184 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001185
Jon Salz0697cbf2012-07-04 15:14:04 +08001186 for test in root.get_top_level_tests():
1187 if starting_at:
1188 if test == starting_at:
1189 # We've found starting_at; do auto-run on all
1190 # subsequent tests.
1191 found_starting_at = True
1192 if not found_starting_at:
1193 # Don't start this guy yet
1194 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001195
Jon Salz0697cbf2012-07-04 15:14:04 +08001196 status = test.get_state().status
1197 if status == TestState.ACTIVE or status in statuses_to_run:
1198 # Reset the test (later; we will need to abort
1199 # all active tests first).
1200 tests_to_reset.append(test)
1201 if status in statuses_to_run:
1202 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001203
Jon Salz0697cbf2012-07-04 15:14:04 +08001204 self.abort_active_tests()
Jon Salz258a40c2012-04-19 12:34:01 +08001205
Jon Salz0697cbf2012-07-04 15:14:04 +08001206 # Reset all statuses of the tests to run (in case any tests were active;
1207 # we want them to be run again).
1208 for test_to_reset in tests_to_reset:
1209 for test in test_to_reset.walk():
1210 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001211
Jon Salz0697cbf2012-07-04 15:14:04 +08001212 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001213
Jon Salz0697cbf2012-07-04 15:14:04 +08001214 def restart_tests(self, root=None):
1215 '''Restarts all tests.'''
1216 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001217
Jon Salz0697cbf2012-07-04 15:14:04 +08001218 self.abort_active_tests()
1219 for test in root.walk():
1220 test.update_state(status=TestState.UNTESTED)
1221 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001222
Jon Salz0697cbf2012-07-04 15:14:04 +08001223 def auto_run(self, starting_at=None, root=None):
1224 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001225
Jon Salz0697cbf2012-07-04 15:14:04 +08001226 Args:
1227 starting_at: If provide, only auto-runs tests beginning with
1228 this test.
1229 '''
1230 root = root or self.test_list
1231 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1232 starting_at=starting_at,
1233 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001234
Jon Salz0697cbf2012-07-04 15:14:04 +08001235 def re_run_failed(self, root=None):
1236 '''Re-runs failed tests.'''
1237 root = root or self.test_list
1238 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001239
Jon Salz0697cbf2012-07-04 15:14:04 +08001240 def show_review_information(self):
1241 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001242
Jon Salz0697cbf2012-07-04 15:14:04 +08001243 The information screene is rendered by main UI program (ui.py), so in
1244 goofy we only need to kill all active tests, set them as untested, and
1245 clear remaining tests.
1246 '''
1247 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001248 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001249
Jon Salz0697cbf2012-07-04 15:14:04 +08001250 def handle_switch_test(self, event):
1251 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001252
Jon Salz0697cbf2012-07-04 15:14:04 +08001253 @param event: The SWITCH_TEST event.
1254 '''
1255 test = self.test_list.lookup_path(event.path)
1256 if not test:
1257 logging.error('Unknown test %r', event.key)
1258 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001259
Jon Salz0697cbf2012-07-04 15:14:04 +08001260 invoc = self.invocations.get(test)
1261 if invoc and test.backgroundable:
1262 # Already running: just bring to the front if it
1263 # has a UI.
1264 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001265 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001266 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001267
Jon Salz0697cbf2012-07-04 15:14:04 +08001268 self.abort_active_tests()
1269 for t in test.walk():
1270 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001271
Jon Salz0697cbf2012-07-04 15:14:04 +08001272 if self.test_list.options.auto_run_on_keypress:
1273 self.auto_run(starting_at=test)
1274 else:
1275 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001276
Jon Salz0697cbf2012-07-04 15:14:04 +08001277 def wait(self):
1278 '''Waits for all pending invocations.
1279
1280 Useful for testing.
1281 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001282 while self.invocations:
1283 for k, v in self.invocations.iteritems():
1284 logging.info('Waiting for %s to complete...', k)
1285 v.thread.join()
1286 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001287
1288 def check_exceptions(self):
1289 '''Raises an error if any exceptions have occurred in
1290 invocation threads.'''
1291 if self.exceptions:
1292 raise RuntimeError('Exception in invocation thread: %r' %
1293 self.exceptions)
1294
1295 def record_exception(self, msg):
1296 '''Records an exception in an invocation thread.
1297
1298 An exception with the given message will be rethrown when
1299 Goofy is destroyed.'''
1300 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001301
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001302
1303if __name__ == '__main__':
Jon Salz0697cbf2012-07-04 15:14:04 +08001304 Goofy().main()