blob: 396b4f48c3f113e4c8d8e83ca2eb23a6ba7318b0 [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
2#
3# -*- coding: utf-8 -*-
4#
Jon Salz37eccbd2012-05-25 16:06:52 +08005# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08006# Use of this source code is governed by a BSD-style license that can be
7# found in the LICENSE file.
8
9'''
10The main factory flow that runs the factory test and finalizes a device.
11'''
12
Jon Salz73e0fd02012-04-04 11:46:38 +080013import array
14import fcntl
15import glob
Jon Salz0405ab52012-03-16 15:26:52 +080016import logging
17import os
Jon Salz258a40c2012-04-19 12:34:01 +080018import cPickle as pickle
Jon Salz0405ab52012-03-16 15:26:52 +080019import pipes
Jon Salz73e0fd02012-04-04 11:46:38 +080020import Queue
Jon Salz0405ab52012-03-16 15:26:52 +080021import re
22import signal
23import subprocess
24import sys
25import tempfile
26import threading
27import time
28import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080029import unittest
30import uuid
Hung-Te Linf2f78f72012-02-08 19:27:11 +080031from collections import deque
32from optparse import OptionParser
Jon Salz258a40c2012-04-19 12:34:01 +080033from StringIO import StringIO
Hung-Te Linf2f78f72012-02-08 19:27:11 +080034
35import factory_common
Jon Salz8375c2e2012-04-04 15:22:24 +080036from autotest_lib.client.bin.prespawner import Prespawner
Hung-Te Linf2f78f72012-02-08 19:27:11 +080037from autotest_lib.client.cros import factory
38from autotest_lib.client.cros.factory import state
39from autotest_lib.client.cros.factory import TestState
Jon Salz37eccbd2012-05-25 16:06:52 +080040from autotest_lib.client.cros.factory import updater
Jon Salz258a40c2012-04-19 12:34:01 +080041from autotest_lib.client.cros.factory import utils
Hung-Te Linf2f78f72012-02-08 19:27:11 +080042from autotest_lib.client.cros.factory.event import Event
43from autotest_lib.client.cros.factory.event import EventClient
44from autotest_lib.client.cros.factory.event import EventServer
Jon Salzeb8d25f2012-05-22 15:17:32 +080045from autotest_lib.client.cros.factory.event_log import EventLog
Jon Salz258a40c2012-04-19 12:34:01 +080046from autotest_lib.client.cros.factory.invocation import TestInvocation
Jon Salz5f2a0672012-05-22 17:14:06 +080047from autotest_lib.client.cros.factory import test_environment
Jon Salz258a40c2012-04-19 12:34:01 +080048from autotest_lib.client.cros.factory.web_socket_manager import WebSocketManager
Hung-Te Linf2f78f72012-02-08 19:27:11 +080049
50
Hung-Te Linf2f78f72012-02-08 19:27:11 +080051DEFAULT_TEST_LIST_PATH = os.path.join(
Jon Salz258a40c2012-04-19 12:34:01 +080052 factory.CLIENT_PATH , 'site_tests', 'suite_Factory', 'test_list')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080053HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
54
Jon Salz8796e362012-05-24 11:39:09 +080055# File that suppresses reboot if present (e.g., for development).
56NO_REBOOT_FILE = '/var/log/factory.noreboot'
57
Jon Salz758e6cc2012-04-03 15:47:07 +080058GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
59You are running Goofy inside the chroot. Autotests are not supported.
60
61To use Goofy in the chroot, first install an Xvnc server:
62
63 sudo apt-get install tightvncserver
64
65...and then start a VNC X server outside the chroot:
66
67 vncserver :10 &
68 vncviewer :10
69
70...and run Goofy as follows:
71
72 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
73''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080074suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080075
76def get_hwid_cfg():
77 '''
78 Returns the HWID config tag, or an empty string if none can be found.
79 '''
80 if 'CROS_HWID' in os.environ:
81 return os.environ['CROS_HWID']
82 if os.path.exists(HWID_CFG_PATH):
83 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
84 return hwid_cfg_handle.read().strip()
85 return ''
86
87
88def find_test_list():
89 '''
90 Returns the path to the active test list, based on the HWID config tag.
91 '''
92 hwid_cfg = get_hwid_cfg()
93
94 # Try in order: test_list, test_list.$hwid_cfg, test_list.all
95 if hwid_cfg:
96 test_list = '%s_%s' % (DEFAULT_TEST_LIST_PATH, hwid_cfg)
97 if os.path.exists(test_list):
98 logging.info('Using special test list: %s', test_list)
99 return test_list
100 logging.info('WARNING: no specific test list for config: %s', hwid_cfg)
101
102 test_list = DEFAULT_TEST_LIST_PATH
103 if os.path.exists(test_list):
104 return test_list
105
106 test_list = ('%s.all' % DEFAULT_TEST_LIST_PATH)
107 if os.path.exists(test_list):
108 logging.info('Using default test list: ' + test_list)
109 return test_list
110 logging.info('ERROR: Cannot find any test list.')
111
Jon Salz73e0fd02012-04-04 11:46:38 +0800112
Jon Salz73e0fd02012-04-04 11:46:38 +0800113_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800114
115class Goofy(object):
116 '''
117 The main factory flow.
118
119 Note that all methods in this class must be invoked from the main
120 (event) thread. Other threads, such as callbacks and TestInvocation
121 methods, should instead post events on the run queue.
122
123 TODO: Unit tests. (chrome-os-partner:7409)
124
125 Properties:
Jon Salz258a40c2012-04-19 12:34:01 +0800126 uuid: A unique UUID for this invocation of Goofy.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800127 state_instance: An instance of FactoryState.
128 state_server: The FactoryState XML/RPC server.
129 state_server_thread: A thread running state_server.
130 event_server: The EventServer socket server.
131 event_server_thread: A thread running event_server.
132 event_client: A client to the event server.
Hung-Te Lin6bb48552012-02-09 14:37:43 +0800133 ui_process: The factory ui process object.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800134 run_queue: A queue of callbacks to invoke from the main thread.
135 invocations: A map from FactoryTest objects to the corresponding
136 TestInvocations objects representing active tests.
137 tests_to_run: A deque of tests that should be run when the current
138 test(s) complete.
139 options: Command-line options.
140 args: Command-line args.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800141 test_list: The test list.
Jon Salz0405ab52012-03-16 15:26:52 +0800142 event_handlers: Map of Event.Type to the method used to handle that
143 event. If the method has an 'event' argument, the event is passed
144 to the handler.
Jon Salz73e0fd02012-04-04 11:46:38 +0800145 exceptions: Exceptions encountered in invocation threads.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800146 '''
147 def __init__(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800148 self.uuid = str(uuid.uuid4())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800149 self.state_instance = None
150 self.state_server = None
151 self.state_server_thread = None
152 self.event_server = None
153 self.event_server_thread = None
154 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800155 self.event_log = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800156 self.prespawner = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800157 self.ui_process = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800158 self.run_queue = Queue.Queue()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800159 self.invocations = {}
160 self.tests_to_run = deque()
161 self.visible_test = None
Jon Salz258a40c2012-04-19 12:34:01 +0800162 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800163
164 self.options = None
165 self.args = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800166 self.test_list = None
167
Jon Salz258a40c2012-04-19 12:34:01 +0800168 def test_or_root(event):
169 '''Returns the top-level parent for a test (the root node of the
170 tests that need to be run together if the given test path is to
171 be run).'''
172 try:
173 path = event.path
Jon Salzd2ed6cb2012-05-02 09:35:14 +0800174 except AttributeError:
Jon Salz258a40c2012-04-19 12:34:01 +0800175 path = None
176
177 if path:
Jon Salzf617b282012-05-24 14:14:04 +0800178 return (self.test_list.lookup_path(path).
179 get_top_level_parent_or_group())
Jon Salz258a40c2012-04-19 12:34:01 +0800180 else:
181 return self.test_list
182
Jon Salz0405ab52012-03-16 15:26:52 +0800183 self.event_handlers = {
184 Event.Type.SWITCH_TEST: self.handle_switch_test,
Jon Salz968e90b2012-03-18 16:12:43 +0800185 Event.Type.SHOW_NEXT_ACTIVE_TEST:
186 lambda event: self.show_next_active_test(),
187 Event.Type.RESTART_TESTS:
Jon Salz258a40c2012-04-19 12:34:01 +0800188 lambda event: self.restart_tests(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800189 Event.Type.AUTO_RUN:
Jon Salz258a40c2012-04-19 12:34:01 +0800190 lambda event: self.auto_run(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800191 Event.Type.RE_RUN_FAILED:
Jon Salz258a40c2012-04-19 12:34:01 +0800192 lambda event: self.re_run_failed(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800193 Event.Type.REVIEW:
194 lambda event: self.show_review_information(),
Jon Salz5f2a0672012-05-22 17:14:06 +0800195 Event.Type.UPDATE_SYSTEM_INFO:
196 lambda event: self.update_system_info(),
Jon Salz37eccbd2012-05-25 16:06:52 +0800197 Event.Type.UPDATE_FACTORY:
198 lambda event: self.update_factory(),
Jon Salz0405ab52012-03-16 15:26:52 +0800199 }
200
Jon Salz73e0fd02012-04-04 11:46:38 +0800201 self.exceptions = []
Jon Salz258a40c2012-04-19 12:34:01 +0800202 self.web_socket_manager = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800203
204 def destroy(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800205 if self.chrome:
206 self.chrome.kill()
207 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800208 if self.ui_process:
Jon Salz258a40c2012-04-19 12:34:01 +0800209 utils.kill_process_tree(self.ui_process, 'ui')
Jon Salz73e0fd02012-04-04 11:46:38 +0800210 self.ui_process = None
Jon Salz258a40c2012-04-19 12:34:01 +0800211 if self.web_socket_manager:
212 logging.info('Stopping web sockets')
213 self.web_socket_manager.close()
214 self.web_socket_manager = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800215 if self.state_server_thread:
216 logging.info('Stopping state server')
217 self.state_server.shutdown()
218 self.state_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800219 self.state_server.server_close()
220 self.state_server_thread = None
Jon Salz66f65e62012-05-24 17:40:26 +0800221 if self.state_instance:
222 self.state_instance.close()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800223 if self.event_server_thread:
224 logging.info('Stopping event server')
225 self.event_server.shutdown() # pylint: disable=E1101
226 self.event_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800227 self.event_server.server_close()
228 self.event_server_thread = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800229 if self.prespawner:
230 logging.info('Stopping prespawner')
231 self.prespawner.stop()
232 self.prespawner = None
233 if self.event_client:
Jon Salz258a40c2012-04-19 12:34:01 +0800234 logging.info('Closing event client')
Jon Salz8375c2e2012-04-04 15:22:24 +0800235 self.event_client.close()
Jon Salz258a40c2012-04-19 12:34:01 +0800236 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800237 if self.event_log:
238 self.event_log.Close()
239 self.event_log = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800240 self.check_exceptions()
Jon Salz258a40c2012-04-19 12:34:01 +0800241 logging.info('Done destroying Goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800242
243 def start_state_server(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800244 self.state_instance, self.state_server = (
245 state.create_server(bind_address='0.0.0.0'))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800246 logging.info('Starting state server')
247 self.state_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800248 target=self.state_server.serve_forever,
249 name='StateServer')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800250 self.state_server_thread.start()
251
252 def start_event_server(self):
253 self.event_server = EventServer()
254 logging.info('Starting factory event server')
255 self.event_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800256 target=self.event_server.serve_forever,
257 name='EventServer') # pylint: disable=E1101
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800258 self.event_server_thread.start()
259
260 self.event_client = EventClient(
261 callback=self.handle_event, event_loop=self.run_queue)
262
Jon Salz258a40c2012-04-19 12:34:01 +0800263 self.web_socket_manager = WebSocketManager(self.uuid)
264 self.state_server.add_handler("/event",
265 self.web_socket_manager.handle_web_socket)
266
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800267 def start_ui(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800268 ui_proc_args = [os.path.join(factory.CROS_FACTORY_LIB_PATH, 'ui'),
269 self.options.test_list]
Jon Salz14bcbb02012-03-17 15:11:50 +0800270 if self.options.verbose:
271 ui_proc_args.append('-v')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800272 logging.info('Starting ui %s', ui_proc_args)
273 self.ui_process = subprocess.Popen(ui_proc_args)
274 logging.info('Waiting for UI to come up...')
275 self.event_client.wait(
276 lambda event: event.type == Event.Type.UI_READY)
277 logging.info('UI has started')
278
279 def set_visible_test(self, test):
280 if self.visible_test == test:
281 return
282
283 if test:
284 test.update_state(visible=True)
285 if self.visible_test:
286 self.visible_test.update_state(visible=False)
287 self.visible_test = test
288
Jon Salz74ad3262012-03-16 14:40:55 +0800289 def handle_shutdown_complete(self, test, state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800290 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800291 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800292
Jon Salz74ad3262012-03-16 14:40:55 +0800293 @param test: The ShutdownStep.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800294 @param state: The test state.
295 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800296 state = test.update_state(increment_shutdown_count=1)
297 logging.info('Detected shutdown (%d of %d)',
298 state.shutdown_count, test.iterations)
299 if state.shutdown_count == test.iterations:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800300 # Good!
301 test.update_state(status=TestState.PASSED, error_msg='')
Jon Salz74ad3262012-03-16 14:40:55 +0800302 elif state.shutdown_count > test.iterations:
Jon Salz73e0fd02012-04-04 11:46:38 +0800303 # Shut down too many times
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800304 test.update_state(status=TestState.FAILED,
Jon Salz74ad3262012-03-16 14:40:55 +0800305 error_msg='Too many shutdowns')
Jon Salz258a40c2012-04-19 12:34:01 +0800306 elif utils.are_shift_keys_depressed():
Jon Salz73e0fd02012-04-04 11:46:38 +0800307 logging.info('Shift keys are depressed; cancelling restarts')
308 # Abort shutdown
309 test.update_state(
310 status=TestState.FAILED,
311 error_msg='Shutdown aborted with double shift keys')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800312 else:
Jon Salz74ad3262012-03-16 14:40:55 +0800313 # Need to shutdown again
Jon Salzb9038572012-05-24 10:34:51 +0800314 self.event_log.Log('shutdown', operation='reboot')
Jon Salz73e0fd02012-04-04 11:46:38 +0800315 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800316
317 def init_states(self):
318 '''
319 Initializes all states on startup.
320 '''
321 for test in self.test_list.get_all_tests():
322 # Make sure the state server knows about all the tests,
323 # defaulting to an untested state.
324 test.update_state(update_parent=False, visible=False)
325
326 # Any 'active' tests should be marked as failed now.
327 for test in self.test_list.walk():
328 state = test.get_state()
Hung-Te Lin96632362012-03-20 21:14:18 +0800329 if state.status != TestState.ACTIVE:
330 continue
331 if isinstance(test, factory.ShutdownStep):
332 # Shutdown while the test was active - that's good.
333 self.handle_shutdown_complete(test, state)
334 else:
335 test.update_state(status=TestState.FAILED,
336 error_msg='Unknown (shutdown?)')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800337
338 def show_next_active_test(self):
339 '''
340 Rotates to the next visible active test.
341 '''
342 self.reap_completed_tests()
343 active_tests = [
344 t for t in self.test_list.walk()
345 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
346 if not active_tests:
347 return
348
349 try:
350 next_test = active_tests[
351 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
352 except ValueError: # visible_test not present in active_tests
353 next_test = active_tests[0]
354
355 self.set_visible_test(next_test)
356
357 def handle_event(self, event):
358 '''
359 Handles an event from the event server.
360 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800361 handler = self.event_handlers.get(event.type)
362 if handler:
Jon Salz968e90b2012-03-18 16:12:43 +0800363 handler(event)
Jon Salz0405ab52012-03-16 15:26:52 +0800364 else:
Jon Salz968e90b2012-03-18 16:12:43 +0800365 # We don't register handlers for all event types - just ignore
366 # this event.
367 logging.debug('Unbound event type %s', event.type)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800368
369 def run_next_test(self):
370 '''
371 Runs the next eligible test (or tests) in self.tests_to_run.
372 '''
373 self.reap_completed_tests()
374 while self.tests_to_run:
375 logging.debug('Tests to run: %s',
376 [x.path for x in self.tests_to_run])
377
378 test = self.tests_to_run[0]
379
380 if test in self.invocations:
381 logging.info('Next test %s is already running', test.path)
382 self.tests_to_run.popleft()
383 return
384
385 if self.invocations and not (test.backgroundable and all(
386 [x.backgroundable for x in self.invocations])):
387 logging.debug('Waiting for non-backgroundable tests to '
388 'complete before running %s', test.path)
389 return
390
391 self.tests_to_run.popleft()
392
Jon Salz74ad3262012-03-16 14:40:55 +0800393 if isinstance(test, factory.ShutdownStep):
Jon Salz8796e362012-05-24 11:39:09 +0800394 if os.path.exists(NO_REBOOT_FILE):
395 test.update_state(
396 status=TestState.FAILED, increment_count=1,
397 error_msg=('Skipped shutdown since %s is present' %
398 NO_REBOOT_FILE))
399 continue
400
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800401 test.update_state(status=TestState.ACTIVE, increment_count=1,
Jon Salz74ad3262012-03-16 14:40:55 +0800402 error_msg='', shutdown_count=0)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800403 # Save pending test list in the state server
404 self.state_instance.set_shared_data(
Jon Salz74ad3262012-03-16 14:40:55 +0800405 'tests_after_shutdown',
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800406 [t.path for t in self.tests_to_run])
Jon Salz74ad3262012-03-16 14:40:55 +0800407
Jon Salz73e0fd02012-04-04 11:46:38 +0800408 with self.env.lock:
Jon Salzb9038572012-05-24 10:34:51 +0800409 self.event_log.Log('shutdown', operation=test.operation)
Jon Salz73e0fd02012-04-04 11:46:38 +0800410 shutdown_result = self.env.shutdown(test.operation)
411 if shutdown_result:
412 # That's all, folks!
413 self.run_queue.put(None)
414 return
415 else:
416 # Just pass (e.g., in the chroot).
417 test.update_state(status=TestState.PASSED)
418 self.state_instance.set_shared_data(
419 'tests_after_shutdown', None)
420 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800421
422 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
423 self.invocations[test] = invoc
424 if self.visible_test is None and test.has_ui:
425 self.set_visible_test(test)
426 invoc.start()
427
Jon Salz0405ab52012-03-16 15:26:52 +0800428 def run_tests(self, subtrees, untested_only=False):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800429 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800430 Runs tests under subtree.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800431
432 The tests are run in order unless one fails (then stops).
433 Backgroundable tests are run simultaneously; when a foreground test is
434 encountered, we wait for all active tests to finish before continuing.
Jon Salz0405ab52012-03-16 15:26:52 +0800435
436 @param subtrees: Node or nodes containing tests to run (may either be
437 a single test or a list). Duplicates will be ignored.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800438 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800439 if type(subtrees) != list:
440 subtrees = [subtrees]
441
442 # Nodes we've seen so far, to avoid duplicates.
443 seen = set()
444
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800445 self.tests_to_run = deque()
Jon Salz0405ab52012-03-16 15:26:52 +0800446 for subtree in subtrees:
447 for test in subtree.walk():
448 if test in seen:
449 continue
450 seen.add(test)
451
452 if not test.is_leaf():
453 continue
454 if (untested_only and
455 test.get_state().status != TestState.UNTESTED):
456 continue
457 self.tests_to_run.append(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800458 self.run_next_test()
459
460 def reap_completed_tests(self):
461 '''
462 Removes completed tests from the set of active tests.
463
464 Also updates the visible test if it was reaped.
465 '''
466 for t, v in dict(self.invocations).iteritems():
467 if v.is_completed():
468 del self.invocations[t]
469
470 if (self.visible_test is None or
471 self.visible_test not in self.invocations):
472 self.set_visible_test(None)
473 # Make the first running test, if any, the visible test
474 for t in self.test_list.walk():
475 if t in self.invocations:
476 self.set_visible_test(t)
477 break
478
Hung-Te Lin96632362012-03-20 21:14:18 +0800479 def kill_active_tests(self, abort):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800480 '''
481 Kills and waits for all active tests.
Hung-Te Lin96632362012-03-20 21:14:18 +0800482
483 @param abort: True to change state of killed tests to FAILED, False for
484 UNTESTED.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800485 '''
486 self.reap_completed_tests()
487 for test, invoc in self.invocations.items():
488 factory.console.info('Killing active test %s...' % test.path)
489 invoc.abort_and_join()
490 factory.console.info('Killed %s' % test.path)
491 del self.invocations[test]
Hung-Te Lin96632362012-03-20 21:14:18 +0800492 if not abort:
493 test.update_state(status=TestState.UNTESTED)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800494 self.reap_completed_tests()
495
Hung-Te Lin96632362012-03-20 21:14:18 +0800496 def abort_active_tests(self):
497 self.kill_active_tests(True)
498
Jon Salz73e0fd02012-04-04 11:46:38 +0800499 def main(self):
Jon Salzeb8d25f2012-05-22 15:17:32 +0800500 try:
501 self.init()
Jon Salzb9038572012-05-24 10:34:51 +0800502 self.event_log.Log('goofy_init',
503 success=True)
Jon Salzeb8d25f2012-05-22 15:17:32 +0800504 except:
505 if self.event_log:
506 try:
Jon Salzb9038572012-05-24 10:34:51 +0800507 self.event_log.Log('goofy_init',
508 success=False,
509 trace=traceback.format_exc())
Jon Salzeb8d25f2012-05-22 15:17:32 +0800510 except:
511 pass
512 raise
513
Jon Salz73e0fd02012-04-04 11:46:38 +0800514 self.run()
515
Jon Salz5f2a0672012-05-22 17:14:06 +0800516 def update_system_info(self):
517 '''Updates system info.'''
518 system_info = test_environment.SystemInfo(self.env, self.state_instance)
519 self.state_instance.set_shared_data('system_info', system_info.__dict__)
520 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
521 system_info=system_info.__dict__))
522 logging.info('System info: %r', system_info.__dict__)
523
Jon Salz37eccbd2012-05-25 16:06:52 +0800524 def update_factory(self):
525 self.kill_active_tests(False)
526 self.run_tests([])
527
528 try:
529 if updater.TryUpdate(pre_update_hook=self.state_instance.close):
530 self.env.shutdown('reboot')
531 except:
532 factory.console.exception('Unable to update')
533
Jon Salz73e0fd02012-04-04 11:46:38 +0800534 def init(self, args=None, env=None):
535 '''Initializes Goofy.
Jon Salz74ad3262012-03-16 14:40:55 +0800536
537 Args:
Jon Salz73e0fd02012-04-04 11:46:38 +0800538 args: A list of command-line arguments. Uses sys.argv if
539 args is None.
540 env: An Environment instance to use (or None to choose
Jon Salz258a40c2012-04-19 12:34:01 +0800541 FakeChrootEnvironment or DUTEnvironment as appropriate).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800542 '''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800543 parser = OptionParser()
544 parser.add_option('-v', '--verbose', dest='verbose',
545 action='store_true',
546 help='Enable debug logging')
547 parser.add_option('--print_test_list', dest='print_test_list',
548 metavar='FILE',
549 help='Read and print test list FILE, and exit')
Jon Salz758e6cc2012-04-03 15:47:07 +0800550 parser.add_option('--restart', dest='restart',
551 action='store_true',
552 help='Clear all test state')
Jon Salz258a40c2012-04-19 12:34:01 +0800553 parser.add_option('--ui', dest='ui', type='choice',
554 choices=['none', 'gtk', 'chrome'],
555 default='gtk',
556 help='UI to use')
Jon Salz63585ea2012-05-21 15:03:32 +0800557 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
558 type='int', default=1,
559 help=('Factor by which to scale UI '
560 '(Chrome UI only)'))
Jon Salz73e0fd02012-04-04 11:46:38 +0800561 parser.add_option('--test_list', dest='test_list',
562 metavar='FILE',
563 help='Use FILE as test list')
564 (self.options, self.args) = parser.parse_args(args)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800565
Jon Salz73e0fd02012-04-04 11:46:38 +0800566 global _inited_logging
567 if not _inited_logging:
568 factory.init_logging('goofy', verbose=self.options.verbose)
569 _inited_logging = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800570 self.event_log = EventLog('goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800571
Jon Salz73e0fd02012-04-04 11:46:38 +0800572 if (not suppress_chroot_warning and
573 factory.in_chroot() and
Jon Salz258a40c2012-04-19 12:34:01 +0800574 self.options.ui == 'gtk' and
Jon Salz758e6cc2012-04-03 15:47:07 +0800575 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
576 # That's not going to work! Tell the user how to run
577 # this way.
578 logging.warn(GOOFY_IN_CHROOT_WARNING)
579 time.sleep(1)
580
Jon Salz73e0fd02012-04-04 11:46:38 +0800581 if env:
582 self.env = env
583 elif factory.in_chroot():
Jon Salz5f2a0672012-05-22 17:14:06 +0800584 self.env = test_environment.FakeChrootEnvironment()
Jon Salz73e0fd02012-04-04 11:46:38 +0800585 logging.warn(
586 'Using chroot environment: will not actually run autotests')
587 else:
Jon Salz5f2a0672012-05-22 17:14:06 +0800588 self.env = test_environment.DUTEnvironment()
Jon Salz323dd3d2012-04-09 18:40:43 +0800589 self.env.goofy = self
Jon Salz73e0fd02012-04-04 11:46:38 +0800590
Jon Salz758e6cc2012-04-03 15:47:07 +0800591 if self.options.restart:
592 state.clear_state()
593
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800594 if self.options.print_test_list:
595 print (factory.read_test_list(self.options.print_test_list).
596 __repr__(recursive=True))
597 return
598
599 logging.info('Started')
600
601 self.start_state_server()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800602 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz63585ea2012-05-21 15:03:32 +0800603 self.state_instance.set_shared_data('ui_scale_factor',
604 self.options.ui_scale_factor)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800605
Jon Salz73e0fd02012-04-04 11:46:38 +0800606 self.options.test_list = (self.options.test_list or find_test_list())
607 self.test_list = factory.read_test_list(self.options.test_list,
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800608 self.state_instance)
Jon Salz06fbeff2012-05-21 17:06:05 +0800609 if not self.state_instance.has_shared_data('ui_lang'):
610 self.state_instance.set_shared_data('ui_lang',
611 self.test_list.options.ui_lang)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800612 logging.info('TEST_LIST:\n%s', self.test_list.__repr__(recursive=True))
Jon Salz258a40c2012-04-19 12:34:01 +0800613 self.state_instance.test_list = self.test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800614
615 self.init_states()
616 self.start_event_server()
Jon Salz258a40c2012-04-19 12:34:01 +0800617
Jon Salz5f2a0672012-05-22 17:14:06 +0800618 self.update_system_info()
619
Jon Salzb1b39092012-05-03 02:05:09 +0800620 # Set CROS_UI since some behaviors in ui.py depend on the
621 # particular UI in use. TODO(jsalz): Remove this (and all
622 # places it is used) when the GTK UI is removed.
623 os.environ['CROS_UI'] = self.options.ui
Jon Salz258a40c2012-04-19 12:34:01 +0800624
Jon Salzb1b39092012-05-03 02:05:09 +0800625 if self.options.ui == 'chrome':
Jon Salz258a40c2012-04-19 12:34:01 +0800626 self.env.launch_chrome()
627 logging.info('Waiting for a web socket connection')
628 self.web_socket_manager.wait()
Jon Salzb1b39092012-05-03 02:05:09 +0800629
630 # Wait for the test widget size to be set; this is done in
631 # an asynchronous RPC so there is a small chance that the
632 # web socket might be opened first.
633 for i in range(100): # 10 s
Jon Salz63585ea2012-05-21 15:03:32 +0800634 try:
635 if self.state_instance.get_shared_data('test_widget_size'):
636 break
637 except KeyError:
638 pass # Retry
Jon Salzb1b39092012-05-03 02:05:09 +0800639 time.sleep(0.1) # 100 ms
640 else:
641 logging.warn('Never received test_widget_size from UI')
Jon Salz258a40c2012-04-19 12:34:01 +0800642 elif self.options.ui == 'gtk':
Jon Salz73e0fd02012-04-04 11:46:38 +0800643 self.start_ui()
Jon Salz258a40c2012-04-19 12:34:01 +0800644
Jon Salz8375c2e2012-04-04 15:22:24 +0800645 self.prespawner = Prespawner()
Jon Salz323dd3d2012-04-09 18:40:43 +0800646 self.prespawner.start()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800647
648 def state_change_callback(test, state):
649 self.event_client.post_event(
650 Event(Event.Type.STATE_CHANGE,
651 path=test.path, state=state))
652 self.test_list.state_change_callback = state_change_callback
653
654 try:
Jon Salz758e6cc2012-04-03 15:47:07 +0800655 tests_after_shutdown = self.state_instance.get_shared_data(
656 'tests_after_shutdown')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800657 except KeyError:
Jon Salz758e6cc2012-04-03 15:47:07 +0800658 tests_after_shutdown = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800659
Jon Salz758e6cc2012-04-03 15:47:07 +0800660 if tests_after_shutdown is not None:
661 logging.info('Resuming tests after shutdown: %s',
662 tests_after_shutdown)
663 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800664 self.tests_to_run.extend(
Jon Salz758e6cc2012-04-03 15:47:07 +0800665 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Jon Salz73e0fd02012-04-04 11:46:38 +0800666 self.run_queue.put(self.run_next_test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800667 else:
Jon Salz57717ca2012-04-04 16:47:25 +0800668 if self.test_list.options.auto_run_on_start:
669 self.run_queue.put(
670 lambda: self.run_tests(self.test_list, untested_only=True))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800671
Jon Salz73e0fd02012-04-04 11:46:38 +0800672 def run(self):
673 '''Runs Goofy.'''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800674 # Process events forever.
Jon Salz57717ca2012-04-04 16:47:25 +0800675 while self.run_once(True):
Jon Salz73e0fd02012-04-04 11:46:38 +0800676 pass
677
Jon Salz57717ca2012-04-04 16:47:25 +0800678 def run_once(self, block=False):
Jon Salz73e0fd02012-04-04 11:46:38 +0800679 '''Runs all items pending in the event loop.
680
Jon Salz57717ca2012-04-04 16:47:25 +0800681 Args:
682 block: If true, block until at least one event is processed.
683
Jon Salz73e0fd02012-04-04 11:46:38 +0800684 Returns:
685 True to keep going or False to shut down.
686 '''
Jon Salz57717ca2012-04-04 16:47:25 +0800687 events = []
688 if block:
689 # Get at least one event
690 events.append(self.run_queue.get())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800691 while True:
Jon Salz73e0fd02012-04-04 11:46:38 +0800692 try:
693 events.append(self.run_queue.get_nowait())
694 except Queue.Empty:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800695 break
696
Jon Salz73e0fd02012-04-04 11:46:38 +0800697 for event in events:
698 if not event:
699 # Shutdown request.
700 self.run_queue.task_done()
701 return False
702
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800703 try:
704 event()
705 except Exception as e: # pylint: disable=W0703
706 logging.error('Error in event loop: %s', e)
707 traceback.print_exc(sys.stderr)
Jon Salz8375c2e2012-04-04 15:22:24 +0800708 self.record_exception(traceback.format_exception_only(
709 *sys.exc_info()[:2]))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800710 # But keep going
711 finally:
712 self.run_queue.task_done()
Jon Salz73e0fd02012-04-04 11:46:38 +0800713 return True
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800714
Jon Salz258a40c2012-04-19 12:34:01 +0800715 def run_tests_with_status(self, statuses_to_run, starting_at=None,
716 root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800717 '''Runs all top-level tests with a particular status.
718
719 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +0800720
721 Args:
722 starting_at: If provided, only auto-runs tests beginning with
723 this test.
Jon Salz0405ab52012-03-16 15:26:52 +0800724 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800725 root = root or self.test_list
726
Jon Salz57717ca2012-04-04 16:47:25 +0800727 if starting_at:
728 # Make sure they passed a test, not a string.
729 assert isinstance(starting_at, factory.FactoryTest)
730
Jon Salz0405ab52012-03-16 15:26:52 +0800731 tests_to_reset = []
732 tests_to_run = []
733
Jon Salz57717ca2012-04-04 16:47:25 +0800734 found_starting_at = False
735
Jon Salz258a40c2012-04-19 12:34:01 +0800736 for test in root.get_top_level_tests():
Jon Salz57717ca2012-04-04 16:47:25 +0800737 if starting_at:
738 if test == starting_at:
739 # We've found starting_at; do auto-run on all
740 # subsequent tests.
741 found_starting_at = True
742 if not found_starting_at:
743 # Don't start this guy yet
744 continue
745
Jon Salz0405ab52012-03-16 15:26:52 +0800746 status = test.get_state().status
747 if status == TestState.ACTIVE or status in statuses_to_run:
748 # Reset the test (later; we will need to abort
749 # all active tests first).
750 tests_to_reset.append(test)
751 if status in statuses_to_run:
752 tests_to_run.append(test)
753
754 self.abort_active_tests()
755
756 # Reset all statuses of the tests to run (in case any tests were active;
757 # we want them to be run again).
758 for test_to_reset in tests_to_reset:
759 for test in test_to_reset.walk():
760 test.update_state(status=TestState.UNTESTED)
761
762 self.run_tests(tests_to_run, untested_only=True)
763
Jon Salz258a40c2012-04-19 12:34:01 +0800764 def restart_tests(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800765 '''Restarts all tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800766 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +0800767
Jon Salz258a40c2012-04-19 12:34:01 +0800768 self.abort_active_tests()
769 for test in root.walk():
770 test.update_state(status=TestState.UNTESTED)
771 self.run_tests(root)
772
773 def auto_run(self, starting_at=None, root=None):
Jon Salz57717ca2012-04-04 16:47:25 +0800774 '''"Auto-runs" tests that have not been run yet.
775
776 Args:
777 starting_at: If provide, only auto-runs tests beginning with
778 this test.
779 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800780 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +0800781 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Jon Salz258a40c2012-04-19 12:34:01 +0800782 starting_at=starting_at,
783 root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800784
Jon Salz258a40c2012-04-19 12:34:01 +0800785 def re_run_failed(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800786 '''Re-runs failed tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800787 root = root or self.test_list
788 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800789
Jon Salz968e90b2012-03-18 16:12:43 +0800790 def show_review_information(self):
Hung-Te Lin96632362012-03-20 21:14:18 +0800791 '''Event handler for showing review information screen.
792
793 The information screene is rendered by main UI program (ui.py), so in
794 goofy we only need to kill all active tests, set them as untested, and
795 clear remaining tests.
796 '''
797 self.kill_active_tests(False)
798 self.run_tests([])
799
Jon Salz0405ab52012-03-16 15:26:52 +0800800 def handle_switch_test(self, event):
Jon Salz968e90b2012-03-18 16:12:43 +0800801 '''Switches to a particular test.
802
803 @param event: The SWITCH_TEST event.
804 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800805 test = self.test_list.lookup_path(event.path)
Jon Salz57717ca2012-04-04 16:47:25 +0800806 if not test:
Jon Salz968e90b2012-03-18 16:12:43 +0800807 logging.error('Unknown test %r', event.key)
Jon Salz57717ca2012-04-04 16:47:25 +0800808 return
809
810 invoc = self.invocations.get(test)
811 if invoc and test.backgroundable:
812 # Already running: just bring to the front if it
813 # has a UI.
814 logging.info('Setting visible test to %s', test.path)
815 self.event_client.post_event(
816 Event(Event.Type.SET_VISIBLE_TEST, path=test.path))
817 return
818
819 self.abort_active_tests()
820 for t in test.walk():
821 t.update_state(status=TestState.UNTESTED)
822
823 if self.test_list.options.auto_run_on_keypress:
824 self.auto_run(starting_at=test)
825 else:
826 self.run_tests(test)
Jon Salz0405ab52012-03-16 15:26:52 +0800827
Jon Salz73e0fd02012-04-04 11:46:38 +0800828 def wait(self):
829 '''Waits for all pending invocations.
830
831 Useful for testing.
832 '''
833 for k, v in self.invocations.iteritems():
834 logging.info('Waiting for %s to complete...', k)
835 v.thread.join()
836
837 def check_exceptions(self):
838 '''Raises an error if any exceptions have occurred in
839 invocation threads.'''
840 if self.exceptions:
841 raise RuntimeError('Exception in invocation thread: %r' %
842 self.exceptions)
843
844 def record_exception(self, msg):
845 '''Records an exception in an invocation thread.
846
847 An exception with the given message will be rethrown when
848 Goofy is destroyed.'''
849 self.exceptions.append(msg)
850
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800851
852if __name__ == '__main__':
853 Goofy().main()