blob: 834bb6ff9bed1742785207d1734de204571f260c [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
2#
3# -*- coding: utf-8 -*-
4#
5# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
6# 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 Salz258a40c2012-04-19 12:34:01 +080040from autotest_lib.client.cros.factory import utils
Hung-Te Linf2f78f72012-02-08 19:27:11 +080041from autotest_lib.client.cros.factory.event import Event
42from autotest_lib.client.cros.factory.event import EventClient
43from autotest_lib.client.cros.factory.event import EventServer
Jon Salzeb8d25f2012-05-22 15:17:32 +080044from autotest_lib.client.cros.factory.event_log import EventLog
Jon Salz258a40c2012-04-19 12:34:01 +080045from autotest_lib.client.cros.factory.invocation import TestInvocation
Jon Salz5f2a0672012-05-22 17:14:06 +080046from autotest_lib.client.cros.factory import test_environment
Jon Salz258a40c2012-04-19 12:34:01 +080047from autotest_lib.client.cros.factory.web_socket_manager import WebSocketManager
Hung-Te Linf2f78f72012-02-08 19:27:11 +080048
49
Hung-Te Linf2f78f72012-02-08 19:27:11 +080050DEFAULT_TEST_LIST_PATH = os.path.join(
Jon Salz258a40c2012-04-19 12:34:01 +080051 factory.CLIENT_PATH , 'site_tests', 'suite_Factory', 'test_list')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080052HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
53
Jon Salz758e6cc2012-04-03 15:47:07 +080054GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
55You are running Goofy inside the chroot. Autotests are not supported.
56
57To use Goofy in the chroot, first install an Xvnc server:
58
59 sudo apt-get install tightvncserver
60
61...and then start a VNC X server outside the chroot:
62
63 vncserver :10 &
64 vncviewer :10
65
66...and run Goofy as follows:
67
68 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
69''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080070suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080071
72def get_hwid_cfg():
73 '''
74 Returns the HWID config tag, or an empty string if none can be found.
75 '''
76 if 'CROS_HWID' in os.environ:
77 return os.environ['CROS_HWID']
78 if os.path.exists(HWID_CFG_PATH):
79 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
80 return hwid_cfg_handle.read().strip()
81 return ''
82
83
84def find_test_list():
85 '''
86 Returns the path to the active test list, based on the HWID config tag.
87 '''
88 hwid_cfg = get_hwid_cfg()
89
90 # Try in order: test_list, test_list.$hwid_cfg, test_list.all
91 if hwid_cfg:
92 test_list = '%s_%s' % (DEFAULT_TEST_LIST_PATH, hwid_cfg)
93 if os.path.exists(test_list):
94 logging.info('Using special test list: %s', test_list)
95 return test_list
96 logging.info('WARNING: no specific test list for config: %s', hwid_cfg)
97
98 test_list = DEFAULT_TEST_LIST_PATH
99 if os.path.exists(test_list):
100 return test_list
101
102 test_list = ('%s.all' % DEFAULT_TEST_LIST_PATH)
103 if os.path.exists(test_list):
104 logging.info('Using default test list: ' + test_list)
105 return test_list
106 logging.info('ERROR: Cannot find any test list.')
107
Jon Salz73e0fd02012-04-04 11:46:38 +0800108
Jon Salz73e0fd02012-04-04 11:46:38 +0800109_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800110
111class Goofy(object):
112 '''
113 The main factory flow.
114
115 Note that all methods in this class must be invoked from the main
116 (event) thread. Other threads, such as callbacks and TestInvocation
117 methods, should instead post events on the run queue.
118
119 TODO: Unit tests. (chrome-os-partner:7409)
120
121 Properties:
Jon Salz258a40c2012-04-19 12:34:01 +0800122 uuid: A unique UUID for this invocation of Goofy.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800123 state_instance: An instance of FactoryState.
124 state_server: The FactoryState XML/RPC server.
125 state_server_thread: A thread running state_server.
126 event_server: The EventServer socket server.
127 event_server_thread: A thread running event_server.
128 event_client: A client to the event server.
Hung-Te Lin6bb48552012-02-09 14:37:43 +0800129 ui_process: The factory ui process object.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800130 run_queue: A queue of callbacks to invoke from the main thread.
131 invocations: A map from FactoryTest objects to the corresponding
132 TestInvocations objects representing active tests.
133 tests_to_run: A deque of tests that should be run when the current
134 test(s) complete.
135 options: Command-line options.
136 args: Command-line args.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800137 test_list: The test list.
Jon Salz0405ab52012-03-16 15:26:52 +0800138 event_handlers: Map of Event.Type to the method used to handle that
139 event. If the method has an 'event' argument, the event is passed
140 to the handler.
Jon Salz73e0fd02012-04-04 11:46:38 +0800141 exceptions: Exceptions encountered in invocation threads.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800142 '''
143 def __init__(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800144 self.uuid = str(uuid.uuid4())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800145 self.state_instance = None
146 self.state_server = None
147 self.state_server_thread = None
148 self.event_server = None
149 self.event_server_thread = None
150 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800151 self.event_log = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800152 self.prespawner = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800153 self.ui_process = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800154 self.run_queue = Queue.Queue()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800155 self.invocations = {}
156 self.tests_to_run = deque()
157 self.visible_test = None
Jon Salz258a40c2012-04-19 12:34:01 +0800158 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800159
160 self.options = None
161 self.args = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800162 self.test_list = None
163
Jon Salz258a40c2012-04-19 12:34:01 +0800164 def test_or_root(event):
165 '''Returns the top-level parent for a test (the root node of the
166 tests that need to be run together if the given test path is to
167 be run).'''
168 try:
169 path = event.path
Jon Salzd2ed6cb2012-05-02 09:35:14 +0800170 except AttributeError:
Jon Salz258a40c2012-04-19 12:34:01 +0800171 path = None
172
173 if path:
Jon Salzf617b282012-05-24 14:14:04 +0800174 return (self.test_list.lookup_path(path).
175 get_top_level_parent_or_group())
Jon Salz258a40c2012-04-19 12:34:01 +0800176 else:
177 return self.test_list
178
Jon Salz0405ab52012-03-16 15:26:52 +0800179 self.event_handlers = {
180 Event.Type.SWITCH_TEST: self.handle_switch_test,
Jon Salz968e90b2012-03-18 16:12:43 +0800181 Event.Type.SHOW_NEXT_ACTIVE_TEST:
182 lambda event: self.show_next_active_test(),
183 Event.Type.RESTART_TESTS:
Jon Salz258a40c2012-04-19 12:34:01 +0800184 lambda event: self.restart_tests(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800185 Event.Type.AUTO_RUN:
Jon Salz258a40c2012-04-19 12:34:01 +0800186 lambda event: self.auto_run(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800187 Event.Type.RE_RUN_FAILED:
Jon Salz258a40c2012-04-19 12:34:01 +0800188 lambda event: self.re_run_failed(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800189 Event.Type.REVIEW:
190 lambda event: self.show_review_information(),
Jon Salz5f2a0672012-05-22 17:14:06 +0800191 Event.Type.UPDATE_SYSTEM_INFO:
192 lambda event: self.update_system_info(),
Jon Salz0405ab52012-03-16 15:26:52 +0800193 }
194
Jon Salz73e0fd02012-04-04 11:46:38 +0800195 self.exceptions = []
Jon Salz258a40c2012-04-19 12:34:01 +0800196 self.web_socket_manager = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800197
198 def destroy(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800199 if self.chrome:
200 self.chrome.kill()
201 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800202 if self.ui_process:
Jon Salz258a40c2012-04-19 12:34:01 +0800203 utils.kill_process_tree(self.ui_process, 'ui')
Jon Salz73e0fd02012-04-04 11:46:38 +0800204 self.ui_process = None
Jon Salz258a40c2012-04-19 12:34:01 +0800205 if self.web_socket_manager:
206 logging.info('Stopping web sockets')
207 self.web_socket_manager.close()
208 self.web_socket_manager = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800209 if self.state_server_thread:
210 logging.info('Stopping state server')
211 self.state_server.shutdown()
212 self.state_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800213 self.state_server.server_close()
214 self.state_server_thread = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800215 if self.event_server_thread:
216 logging.info('Stopping event server')
217 self.event_server.shutdown() # pylint: disable=E1101
218 self.event_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800219 self.event_server.server_close()
220 self.event_server_thread = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800221 if self.prespawner:
222 logging.info('Stopping prespawner')
223 self.prespawner.stop()
224 self.prespawner = None
225 if self.event_client:
Jon Salz258a40c2012-04-19 12:34:01 +0800226 logging.info('Closing event client')
Jon Salz8375c2e2012-04-04 15:22:24 +0800227 self.event_client.close()
Jon Salz258a40c2012-04-19 12:34:01 +0800228 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800229 if self.event_log:
230 self.event_log.Close()
231 self.event_log = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800232 self.check_exceptions()
Jon Salz258a40c2012-04-19 12:34:01 +0800233 logging.info('Done destroying Goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800234
235 def start_state_server(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800236 self.state_instance, self.state_server = (
237 state.create_server(bind_address='0.0.0.0'))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800238 logging.info('Starting state server')
239 self.state_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800240 target=self.state_server.serve_forever,
241 name='StateServer')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800242 self.state_server_thread.start()
243
244 def start_event_server(self):
245 self.event_server = EventServer()
246 logging.info('Starting factory event server')
247 self.event_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800248 target=self.event_server.serve_forever,
249 name='EventServer') # pylint: disable=E1101
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800250 self.event_server_thread.start()
251
252 self.event_client = EventClient(
253 callback=self.handle_event, event_loop=self.run_queue)
254
Jon Salz258a40c2012-04-19 12:34:01 +0800255 self.web_socket_manager = WebSocketManager(self.uuid)
256 self.state_server.add_handler("/event",
257 self.web_socket_manager.handle_web_socket)
258
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800259 def start_ui(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800260 ui_proc_args = [os.path.join(factory.CROS_FACTORY_LIB_PATH, 'ui'),
261 self.options.test_list]
Jon Salz14bcbb02012-03-17 15:11:50 +0800262 if self.options.verbose:
263 ui_proc_args.append('-v')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800264 logging.info('Starting ui %s', ui_proc_args)
265 self.ui_process = subprocess.Popen(ui_proc_args)
266 logging.info('Waiting for UI to come up...')
267 self.event_client.wait(
268 lambda event: event.type == Event.Type.UI_READY)
269 logging.info('UI has started')
270
271 def set_visible_test(self, test):
272 if self.visible_test == test:
273 return
274
275 if test:
276 test.update_state(visible=True)
277 if self.visible_test:
278 self.visible_test.update_state(visible=False)
279 self.visible_test = test
280
Jon Salz74ad3262012-03-16 14:40:55 +0800281 def handle_shutdown_complete(self, test, state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800282 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800283 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800284
Jon Salz74ad3262012-03-16 14:40:55 +0800285 @param test: The ShutdownStep.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800286 @param state: The test state.
287 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800288 state = test.update_state(increment_shutdown_count=1)
289 logging.info('Detected shutdown (%d of %d)',
290 state.shutdown_count, test.iterations)
291 if state.shutdown_count == test.iterations:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800292 # Good!
293 test.update_state(status=TestState.PASSED, error_msg='')
Jon Salz74ad3262012-03-16 14:40:55 +0800294 elif state.shutdown_count > test.iterations:
Jon Salz73e0fd02012-04-04 11:46:38 +0800295 # Shut down too many times
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800296 test.update_state(status=TestState.FAILED,
Jon Salz74ad3262012-03-16 14:40:55 +0800297 error_msg='Too many shutdowns')
Jon Salz258a40c2012-04-19 12:34:01 +0800298 elif utils.are_shift_keys_depressed():
Jon Salz73e0fd02012-04-04 11:46:38 +0800299 logging.info('Shift keys are depressed; cancelling restarts')
300 # Abort shutdown
301 test.update_state(
302 status=TestState.FAILED,
303 error_msg='Shutdown aborted with double shift keys')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800304 else:
Jon Salz74ad3262012-03-16 14:40:55 +0800305 # Need to shutdown again
Jon Salzb9038572012-05-24 10:34:51 +0800306 self.event_log.Log('shutdown', operation='reboot')
Jon Salz73e0fd02012-04-04 11:46:38 +0800307 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800308
309 def init_states(self):
310 '''
311 Initializes all states on startup.
312 '''
313 for test in self.test_list.get_all_tests():
314 # Make sure the state server knows about all the tests,
315 # defaulting to an untested state.
316 test.update_state(update_parent=False, visible=False)
317
318 # Any 'active' tests should be marked as failed now.
319 for test in self.test_list.walk():
320 state = test.get_state()
Hung-Te Lin96632362012-03-20 21:14:18 +0800321 if state.status != TestState.ACTIVE:
322 continue
323 if isinstance(test, factory.ShutdownStep):
324 # Shutdown while the test was active - that's good.
325 self.handle_shutdown_complete(test, state)
326 else:
327 test.update_state(status=TestState.FAILED,
328 error_msg='Unknown (shutdown?)')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800329
330 def show_next_active_test(self):
331 '''
332 Rotates to the next visible active test.
333 '''
334 self.reap_completed_tests()
335 active_tests = [
336 t for t in self.test_list.walk()
337 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
338 if not active_tests:
339 return
340
341 try:
342 next_test = active_tests[
343 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
344 except ValueError: # visible_test not present in active_tests
345 next_test = active_tests[0]
346
347 self.set_visible_test(next_test)
348
349 def handle_event(self, event):
350 '''
351 Handles an event from the event server.
352 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800353 handler = self.event_handlers.get(event.type)
354 if handler:
Jon Salz968e90b2012-03-18 16:12:43 +0800355 handler(event)
Jon Salz0405ab52012-03-16 15:26:52 +0800356 else:
Jon Salz968e90b2012-03-18 16:12:43 +0800357 # We don't register handlers for all event types - just ignore
358 # this event.
359 logging.debug('Unbound event type %s', event.type)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800360
361 def run_next_test(self):
362 '''
363 Runs the next eligible test (or tests) in self.tests_to_run.
364 '''
365 self.reap_completed_tests()
366 while self.tests_to_run:
367 logging.debug('Tests to run: %s',
368 [x.path for x in self.tests_to_run])
369
370 test = self.tests_to_run[0]
371
372 if test in self.invocations:
373 logging.info('Next test %s is already running', test.path)
374 self.tests_to_run.popleft()
375 return
376
377 if self.invocations and not (test.backgroundable and all(
378 [x.backgroundable for x in self.invocations])):
379 logging.debug('Waiting for non-backgroundable tests to '
380 'complete before running %s', test.path)
381 return
382
383 self.tests_to_run.popleft()
384
Jon Salz74ad3262012-03-16 14:40:55 +0800385 if isinstance(test, factory.ShutdownStep):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800386 test.update_state(status=TestState.ACTIVE, increment_count=1,
Jon Salz74ad3262012-03-16 14:40:55 +0800387 error_msg='', shutdown_count=0)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800388 # Save pending test list in the state server
389 self.state_instance.set_shared_data(
Jon Salz74ad3262012-03-16 14:40:55 +0800390 'tests_after_shutdown',
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800391 [t.path for t in self.tests_to_run])
Jon Salz74ad3262012-03-16 14:40:55 +0800392
Jon Salz73e0fd02012-04-04 11:46:38 +0800393 with self.env.lock:
Jon Salzb9038572012-05-24 10:34:51 +0800394 self.event_log.Log('shutdown', operation=test.operation)
Jon Salz73e0fd02012-04-04 11:46:38 +0800395 shutdown_result = self.env.shutdown(test.operation)
396 if shutdown_result:
397 # That's all, folks!
398 self.run_queue.put(None)
399 return
400 else:
401 # Just pass (e.g., in the chroot).
402 test.update_state(status=TestState.PASSED)
403 self.state_instance.set_shared_data(
404 'tests_after_shutdown', None)
405 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800406
407 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
408 self.invocations[test] = invoc
409 if self.visible_test is None and test.has_ui:
410 self.set_visible_test(test)
411 invoc.start()
412
Jon Salz0405ab52012-03-16 15:26:52 +0800413 def run_tests(self, subtrees, untested_only=False):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800414 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800415 Runs tests under subtree.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800416
417 The tests are run in order unless one fails (then stops).
418 Backgroundable tests are run simultaneously; when a foreground test is
419 encountered, we wait for all active tests to finish before continuing.
Jon Salz0405ab52012-03-16 15:26:52 +0800420
421 @param subtrees: Node or nodes containing tests to run (may either be
422 a single test or a list). Duplicates will be ignored.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800423 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800424 if type(subtrees) != list:
425 subtrees = [subtrees]
426
427 # Nodes we've seen so far, to avoid duplicates.
428 seen = set()
429
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800430 self.tests_to_run = deque()
Jon Salz0405ab52012-03-16 15:26:52 +0800431 for subtree in subtrees:
432 for test in subtree.walk():
433 if test in seen:
434 continue
435 seen.add(test)
436
437 if not test.is_leaf():
438 continue
439 if (untested_only and
440 test.get_state().status != TestState.UNTESTED):
441 continue
442 self.tests_to_run.append(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800443 self.run_next_test()
444
445 def reap_completed_tests(self):
446 '''
447 Removes completed tests from the set of active tests.
448
449 Also updates the visible test if it was reaped.
450 '''
451 for t, v in dict(self.invocations).iteritems():
452 if v.is_completed():
453 del self.invocations[t]
454
455 if (self.visible_test is None or
456 self.visible_test not in self.invocations):
457 self.set_visible_test(None)
458 # Make the first running test, if any, the visible test
459 for t in self.test_list.walk():
460 if t in self.invocations:
461 self.set_visible_test(t)
462 break
463
Hung-Te Lin96632362012-03-20 21:14:18 +0800464 def kill_active_tests(self, abort):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800465 '''
466 Kills and waits for all active tests.
Hung-Te Lin96632362012-03-20 21:14:18 +0800467
468 @param abort: True to change state of killed tests to FAILED, False for
469 UNTESTED.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800470 '''
471 self.reap_completed_tests()
472 for test, invoc in self.invocations.items():
473 factory.console.info('Killing active test %s...' % test.path)
474 invoc.abort_and_join()
475 factory.console.info('Killed %s' % test.path)
476 del self.invocations[test]
Hung-Te Lin96632362012-03-20 21:14:18 +0800477 if not abort:
478 test.update_state(status=TestState.UNTESTED)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800479 self.reap_completed_tests()
480
Hung-Te Lin96632362012-03-20 21:14:18 +0800481 def abort_active_tests(self):
482 self.kill_active_tests(True)
483
Jon Salz73e0fd02012-04-04 11:46:38 +0800484 def main(self):
Jon Salzeb8d25f2012-05-22 15:17:32 +0800485 try:
486 self.init()
Jon Salzb9038572012-05-24 10:34:51 +0800487 self.event_log.Log('goofy_init',
488 success=True)
Jon Salzeb8d25f2012-05-22 15:17:32 +0800489 except:
490 if self.event_log:
491 try:
Jon Salzb9038572012-05-24 10:34:51 +0800492 self.event_log.Log('goofy_init',
493 success=False,
494 trace=traceback.format_exc())
Jon Salzeb8d25f2012-05-22 15:17:32 +0800495 except:
496 pass
497 raise
498
Jon Salz73e0fd02012-04-04 11:46:38 +0800499 self.run()
500
Jon Salz5f2a0672012-05-22 17:14:06 +0800501 def update_system_info(self):
502 '''Updates system info.'''
503 system_info = test_environment.SystemInfo(self.env, self.state_instance)
504 self.state_instance.set_shared_data('system_info', system_info.__dict__)
505 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
506 system_info=system_info.__dict__))
507 logging.info('System info: %r', system_info.__dict__)
508
Jon Salz73e0fd02012-04-04 11:46:38 +0800509 def init(self, args=None, env=None):
510 '''Initializes Goofy.
Jon Salz74ad3262012-03-16 14:40:55 +0800511
512 Args:
Jon Salz73e0fd02012-04-04 11:46:38 +0800513 args: A list of command-line arguments. Uses sys.argv if
514 args is None.
515 env: An Environment instance to use (or None to choose
Jon Salz258a40c2012-04-19 12:34:01 +0800516 FakeChrootEnvironment or DUTEnvironment as appropriate).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800517 '''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800518 parser = OptionParser()
519 parser.add_option('-v', '--verbose', dest='verbose',
520 action='store_true',
521 help='Enable debug logging')
522 parser.add_option('--print_test_list', dest='print_test_list',
523 metavar='FILE',
524 help='Read and print test list FILE, and exit')
Jon Salz758e6cc2012-04-03 15:47:07 +0800525 parser.add_option('--restart', dest='restart',
526 action='store_true',
527 help='Clear all test state')
Jon Salz258a40c2012-04-19 12:34:01 +0800528 parser.add_option('--ui', dest='ui', type='choice',
529 choices=['none', 'gtk', 'chrome'],
530 default='gtk',
531 help='UI to use')
Jon Salz63585ea2012-05-21 15:03:32 +0800532 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
533 type='int', default=1,
534 help=('Factor by which to scale UI '
535 '(Chrome UI only)'))
Jon Salz73e0fd02012-04-04 11:46:38 +0800536 parser.add_option('--test_list', dest='test_list',
537 metavar='FILE',
538 help='Use FILE as test list')
539 (self.options, self.args) = parser.parse_args(args)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800540
Jon Salz73e0fd02012-04-04 11:46:38 +0800541 global _inited_logging
542 if not _inited_logging:
543 factory.init_logging('goofy', verbose=self.options.verbose)
544 _inited_logging = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800545 self.event_log = EventLog('goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800546
Jon Salz73e0fd02012-04-04 11:46:38 +0800547 if (not suppress_chroot_warning and
548 factory.in_chroot() and
Jon Salz258a40c2012-04-19 12:34:01 +0800549 self.options.ui == 'gtk' and
Jon Salz758e6cc2012-04-03 15:47:07 +0800550 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
551 # That's not going to work! Tell the user how to run
552 # this way.
553 logging.warn(GOOFY_IN_CHROOT_WARNING)
554 time.sleep(1)
555
Jon Salz73e0fd02012-04-04 11:46:38 +0800556 if env:
557 self.env = env
558 elif factory.in_chroot():
Jon Salz5f2a0672012-05-22 17:14:06 +0800559 self.env = test_environment.FakeChrootEnvironment()
Jon Salz73e0fd02012-04-04 11:46:38 +0800560 logging.warn(
561 'Using chroot environment: will not actually run autotests')
562 else:
Jon Salz5f2a0672012-05-22 17:14:06 +0800563 self.env = test_environment.DUTEnvironment()
Jon Salz323dd3d2012-04-09 18:40:43 +0800564 self.env.goofy = self
Jon Salz73e0fd02012-04-04 11:46:38 +0800565
Jon Salz758e6cc2012-04-03 15:47:07 +0800566 if self.options.restart:
567 state.clear_state()
568
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800569 if self.options.print_test_list:
570 print (factory.read_test_list(self.options.print_test_list).
571 __repr__(recursive=True))
572 return
573
574 logging.info('Started')
575
576 self.start_state_server()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800577 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz63585ea2012-05-21 15:03:32 +0800578 self.state_instance.set_shared_data('ui_scale_factor',
579 self.options.ui_scale_factor)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800580
Jon Salz73e0fd02012-04-04 11:46:38 +0800581 self.options.test_list = (self.options.test_list or find_test_list())
582 self.test_list = factory.read_test_list(self.options.test_list,
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800583 self.state_instance)
Jon Salz06fbeff2012-05-21 17:06:05 +0800584 if not self.state_instance.has_shared_data('ui_lang'):
585 self.state_instance.set_shared_data('ui_lang',
586 self.test_list.options.ui_lang)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800587 logging.info('TEST_LIST:\n%s', self.test_list.__repr__(recursive=True))
Jon Salz258a40c2012-04-19 12:34:01 +0800588 self.state_instance.test_list = self.test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800589
590 self.init_states()
591 self.start_event_server()
Jon Salz258a40c2012-04-19 12:34:01 +0800592
Jon Salz5f2a0672012-05-22 17:14:06 +0800593 self.update_system_info()
594
Jon Salzb1b39092012-05-03 02:05:09 +0800595 # Set CROS_UI since some behaviors in ui.py depend on the
596 # particular UI in use. TODO(jsalz): Remove this (and all
597 # places it is used) when the GTK UI is removed.
598 os.environ['CROS_UI'] = self.options.ui
Jon Salz258a40c2012-04-19 12:34:01 +0800599
Jon Salzb1b39092012-05-03 02:05:09 +0800600 if self.options.ui == 'chrome':
Jon Salz258a40c2012-04-19 12:34:01 +0800601 self.env.launch_chrome()
602 logging.info('Waiting for a web socket connection')
603 self.web_socket_manager.wait()
Jon Salzb1b39092012-05-03 02:05:09 +0800604
605 # Wait for the test widget size to be set; this is done in
606 # an asynchronous RPC so there is a small chance that the
607 # web socket might be opened first.
608 for i in range(100): # 10 s
Jon Salz63585ea2012-05-21 15:03:32 +0800609 try:
610 if self.state_instance.get_shared_data('test_widget_size'):
611 break
612 except KeyError:
613 pass # Retry
Jon Salzb1b39092012-05-03 02:05:09 +0800614 time.sleep(0.1) # 100 ms
615 else:
616 logging.warn('Never received test_widget_size from UI')
Jon Salz258a40c2012-04-19 12:34:01 +0800617 elif self.options.ui == 'gtk':
Jon Salz73e0fd02012-04-04 11:46:38 +0800618 self.start_ui()
Jon Salz258a40c2012-04-19 12:34:01 +0800619
Jon Salz8375c2e2012-04-04 15:22:24 +0800620 self.prespawner = Prespawner()
Jon Salz323dd3d2012-04-09 18:40:43 +0800621 self.prespawner.start()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800622
623 def state_change_callback(test, state):
624 self.event_client.post_event(
625 Event(Event.Type.STATE_CHANGE,
626 path=test.path, state=state))
627 self.test_list.state_change_callback = state_change_callback
628
629 try:
Jon Salz758e6cc2012-04-03 15:47:07 +0800630 tests_after_shutdown = self.state_instance.get_shared_data(
631 'tests_after_shutdown')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800632 except KeyError:
Jon Salz758e6cc2012-04-03 15:47:07 +0800633 tests_after_shutdown = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800634
Jon Salz758e6cc2012-04-03 15:47:07 +0800635 if tests_after_shutdown is not None:
636 logging.info('Resuming tests after shutdown: %s',
637 tests_after_shutdown)
638 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800639 self.tests_to_run.extend(
Jon Salz758e6cc2012-04-03 15:47:07 +0800640 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Jon Salz73e0fd02012-04-04 11:46:38 +0800641 self.run_queue.put(self.run_next_test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800642 else:
Jon Salz57717ca2012-04-04 16:47:25 +0800643 if self.test_list.options.auto_run_on_start:
644 self.run_queue.put(
645 lambda: self.run_tests(self.test_list, untested_only=True))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800646
Jon Salz73e0fd02012-04-04 11:46:38 +0800647 def run(self):
648 '''Runs Goofy.'''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800649 # Process events forever.
Jon Salz57717ca2012-04-04 16:47:25 +0800650 while self.run_once(True):
Jon Salz73e0fd02012-04-04 11:46:38 +0800651 pass
652
Jon Salz57717ca2012-04-04 16:47:25 +0800653 def run_once(self, block=False):
Jon Salz73e0fd02012-04-04 11:46:38 +0800654 '''Runs all items pending in the event loop.
655
Jon Salz57717ca2012-04-04 16:47:25 +0800656 Args:
657 block: If true, block until at least one event is processed.
658
Jon Salz73e0fd02012-04-04 11:46:38 +0800659 Returns:
660 True to keep going or False to shut down.
661 '''
Jon Salz57717ca2012-04-04 16:47:25 +0800662 events = []
663 if block:
664 # Get at least one event
665 events.append(self.run_queue.get())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800666 while True:
Jon Salz73e0fd02012-04-04 11:46:38 +0800667 try:
668 events.append(self.run_queue.get_nowait())
669 except Queue.Empty:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800670 break
671
Jon Salz73e0fd02012-04-04 11:46:38 +0800672 for event in events:
673 if not event:
674 # Shutdown request.
675 self.run_queue.task_done()
676 return False
677
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800678 try:
679 event()
680 except Exception as e: # pylint: disable=W0703
681 logging.error('Error in event loop: %s', e)
682 traceback.print_exc(sys.stderr)
Jon Salz8375c2e2012-04-04 15:22:24 +0800683 self.record_exception(traceback.format_exception_only(
684 *sys.exc_info()[:2]))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800685 # But keep going
686 finally:
687 self.run_queue.task_done()
Jon Salz73e0fd02012-04-04 11:46:38 +0800688 return True
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800689
Jon Salz258a40c2012-04-19 12:34:01 +0800690 def run_tests_with_status(self, statuses_to_run, starting_at=None,
691 root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800692 '''Runs all top-level tests with a particular status.
693
694 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +0800695
696 Args:
697 starting_at: If provided, only auto-runs tests beginning with
698 this test.
Jon Salz0405ab52012-03-16 15:26:52 +0800699 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800700 root = root or self.test_list
701
Jon Salz57717ca2012-04-04 16:47:25 +0800702 if starting_at:
703 # Make sure they passed a test, not a string.
704 assert isinstance(starting_at, factory.FactoryTest)
705
Jon Salz0405ab52012-03-16 15:26:52 +0800706 tests_to_reset = []
707 tests_to_run = []
708
Jon Salz57717ca2012-04-04 16:47:25 +0800709 found_starting_at = False
710
Jon Salz258a40c2012-04-19 12:34:01 +0800711 for test in root.get_top_level_tests():
Jon Salz57717ca2012-04-04 16:47:25 +0800712 if starting_at:
713 if test == starting_at:
714 # We've found starting_at; do auto-run on all
715 # subsequent tests.
716 found_starting_at = True
717 if not found_starting_at:
718 # Don't start this guy yet
719 continue
720
Jon Salz0405ab52012-03-16 15:26:52 +0800721 status = test.get_state().status
722 if status == TestState.ACTIVE or status in statuses_to_run:
723 # Reset the test (later; we will need to abort
724 # all active tests first).
725 tests_to_reset.append(test)
726 if status in statuses_to_run:
727 tests_to_run.append(test)
728
729 self.abort_active_tests()
730
731 # Reset all statuses of the tests to run (in case any tests were active;
732 # we want them to be run again).
733 for test_to_reset in tests_to_reset:
734 for test in test_to_reset.walk():
735 test.update_state(status=TestState.UNTESTED)
736
737 self.run_tests(tests_to_run, untested_only=True)
738
Jon Salz258a40c2012-04-19 12:34:01 +0800739 def restart_tests(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800740 '''Restarts all tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800741 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +0800742
Jon Salz258a40c2012-04-19 12:34:01 +0800743 self.abort_active_tests()
744 for test in root.walk():
745 test.update_state(status=TestState.UNTESTED)
746 self.run_tests(root)
747
748 def auto_run(self, starting_at=None, root=None):
Jon Salz57717ca2012-04-04 16:47:25 +0800749 '''"Auto-runs" tests that have not been run yet.
750
751 Args:
752 starting_at: If provide, only auto-runs tests beginning with
753 this test.
754 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800755 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +0800756 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Jon Salz258a40c2012-04-19 12:34:01 +0800757 starting_at=starting_at,
758 root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800759
Jon Salz258a40c2012-04-19 12:34:01 +0800760 def re_run_failed(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800761 '''Re-runs failed tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800762 root = root or self.test_list
763 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800764
Jon Salz968e90b2012-03-18 16:12:43 +0800765 def show_review_information(self):
Hung-Te Lin96632362012-03-20 21:14:18 +0800766 '''Event handler for showing review information screen.
767
768 The information screene is rendered by main UI program (ui.py), so in
769 goofy we only need to kill all active tests, set them as untested, and
770 clear remaining tests.
771 '''
772 self.kill_active_tests(False)
773 self.run_tests([])
774
Jon Salz0405ab52012-03-16 15:26:52 +0800775 def handle_switch_test(self, event):
Jon Salz968e90b2012-03-18 16:12:43 +0800776 '''Switches to a particular test.
777
778 @param event: The SWITCH_TEST event.
779 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800780 test = self.test_list.lookup_path(event.path)
Jon Salz57717ca2012-04-04 16:47:25 +0800781 if not test:
Jon Salz968e90b2012-03-18 16:12:43 +0800782 logging.error('Unknown test %r', event.key)
Jon Salz57717ca2012-04-04 16:47:25 +0800783 return
784
785 invoc = self.invocations.get(test)
786 if invoc and test.backgroundable:
787 # Already running: just bring to the front if it
788 # has a UI.
789 logging.info('Setting visible test to %s', test.path)
790 self.event_client.post_event(
791 Event(Event.Type.SET_VISIBLE_TEST, path=test.path))
792 return
793
794 self.abort_active_tests()
795 for t in test.walk():
796 t.update_state(status=TestState.UNTESTED)
797
798 if self.test_list.options.auto_run_on_keypress:
799 self.auto_run(starting_at=test)
800 else:
801 self.run_tests(test)
Jon Salz0405ab52012-03-16 15:26:52 +0800802
Jon Salz73e0fd02012-04-04 11:46:38 +0800803 def wait(self):
804 '''Waits for all pending invocations.
805
806 Useful for testing.
807 '''
808 for k, v in self.invocations.iteritems():
809 logging.info('Waiting for %s to complete...', k)
810 v.thread.join()
811
812 def check_exceptions(self):
813 '''Raises an error if any exceptions have occurred in
814 invocation threads.'''
815 if self.exceptions:
816 raise RuntimeError('Exception in invocation thread: %r' %
817 self.exceptions)
818
819 def record_exception(self, msg):
820 '''Records an exception in an invocation thread.
821
822 An exception with the given message will be rethrown when
823 Goofy is destroyed.'''
824 self.exceptions.append(msg)
825
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800826
827if __name__ == '__main__':
828 Goofy().main()