blob: ba49a7a76e6144444da06765d0e96a6d4c93fa19 [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 Salz8796e362012-05-24 11:39:09 +080054# File that suppresses reboot if present (e.g., for development).
55NO_REBOOT_FILE = '/var/log/factory.noreboot'
56
Jon Salz758e6cc2012-04-03 15:47:07 +080057GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
58You are running Goofy inside the chroot. Autotests are not supported.
59
60To use Goofy in the chroot, first install an Xvnc server:
61
62 sudo apt-get install tightvncserver
63
64...and then start a VNC X server outside the chroot:
65
66 vncserver :10 &
67 vncviewer :10
68
69...and run Goofy as follows:
70
71 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
72''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080073suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080074
75def get_hwid_cfg():
76 '''
77 Returns the HWID config tag, or an empty string if none can be found.
78 '''
79 if 'CROS_HWID' in os.environ:
80 return os.environ['CROS_HWID']
81 if os.path.exists(HWID_CFG_PATH):
82 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
83 return hwid_cfg_handle.read().strip()
84 return ''
85
86
87def find_test_list():
88 '''
89 Returns the path to the active test list, based on the HWID config tag.
90 '''
91 hwid_cfg = get_hwid_cfg()
92
93 # Try in order: test_list, test_list.$hwid_cfg, test_list.all
94 if hwid_cfg:
95 test_list = '%s_%s' % (DEFAULT_TEST_LIST_PATH, hwid_cfg)
96 if os.path.exists(test_list):
97 logging.info('Using special test list: %s', test_list)
98 return test_list
99 logging.info('WARNING: no specific test list for config: %s', hwid_cfg)
100
101 test_list = DEFAULT_TEST_LIST_PATH
102 if os.path.exists(test_list):
103 return test_list
104
105 test_list = ('%s.all' % DEFAULT_TEST_LIST_PATH)
106 if os.path.exists(test_list):
107 logging.info('Using default test list: ' + test_list)
108 return test_list
109 logging.info('ERROR: Cannot find any test list.')
110
Jon Salz73e0fd02012-04-04 11:46:38 +0800111
Jon Salz73e0fd02012-04-04 11:46:38 +0800112_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800113
114class Goofy(object):
115 '''
116 The main factory flow.
117
118 Note that all methods in this class must be invoked from the main
119 (event) thread. Other threads, such as callbacks and TestInvocation
120 methods, should instead post events on the run queue.
121
122 TODO: Unit tests. (chrome-os-partner:7409)
123
124 Properties:
Jon Salz258a40c2012-04-19 12:34:01 +0800125 uuid: A unique UUID for this invocation of Goofy.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800126 state_instance: An instance of FactoryState.
127 state_server: The FactoryState XML/RPC server.
128 state_server_thread: A thread running state_server.
129 event_server: The EventServer socket server.
130 event_server_thread: A thread running event_server.
131 event_client: A client to the event server.
Hung-Te Lin6bb48552012-02-09 14:37:43 +0800132 ui_process: The factory ui process object.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800133 run_queue: A queue of callbacks to invoke from the main thread.
134 invocations: A map from FactoryTest objects to the corresponding
135 TestInvocations objects representing active tests.
136 tests_to_run: A deque of tests that should be run when the current
137 test(s) complete.
138 options: Command-line options.
139 args: Command-line args.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800140 test_list: The test list.
Jon Salz0405ab52012-03-16 15:26:52 +0800141 event_handlers: Map of Event.Type to the method used to handle that
142 event. If the method has an 'event' argument, the event is passed
143 to the handler.
Jon Salz73e0fd02012-04-04 11:46:38 +0800144 exceptions: Exceptions encountered in invocation threads.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800145 '''
146 def __init__(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800147 self.uuid = str(uuid.uuid4())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800148 self.state_instance = None
149 self.state_server = None
150 self.state_server_thread = None
151 self.event_server = None
152 self.event_server_thread = None
153 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800154 self.event_log = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800155 self.prespawner = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800156 self.ui_process = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800157 self.run_queue = Queue.Queue()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800158 self.invocations = {}
159 self.tests_to_run = deque()
160 self.visible_test = None
Jon Salz258a40c2012-04-19 12:34:01 +0800161 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800162
163 self.options = None
164 self.args = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800165 self.test_list = None
166
Jon Salz258a40c2012-04-19 12:34:01 +0800167 def test_or_root(event):
168 '''Returns the top-level parent for a test (the root node of the
169 tests that need to be run together if the given test path is to
170 be run).'''
171 try:
172 path = event.path
Jon Salzd2ed6cb2012-05-02 09:35:14 +0800173 except AttributeError:
Jon Salz258a40c2012-04-19 12:34:01 +0800174 path = None
175
176 if path:
Jon Salzf617b282012-05-24 14:14:04 +0800177 return (self.test_list.lookup_path(path).
178 get_top_level_parent_or_group())
Jon Salz258a40c2012-04-19 12:34:01 +0800179 else:
180 return self.test_list
181
Jon Salz0405ab52012-03-16 15:26:52 +0800182 self.event_handlers = {
183 Event.Type.SWITCH_TEST: self.handle_switch_test,
Jon Salz968e90b2012-03-18 16:12:43 +0800184 Event.Type.SHOW_NEXT_ACTIVE_TEST:
185 lambda event: self.show_next_active_test(),
186 Event.Type.RESTART_TESTS:
Jon Salz258a40c2012-04-19 12:34:01 +0800187 lambda event: self.restart_tests(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800188 Event.Type.AUTO_RUN:
Jon Salz258a40c2012-04-19 12:34:01 +0800189 lambda event: self.auto_run(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800190 Event.Type.RE_RUN_FAILED:
Jon Salz258a40c2012-04-19 12:34:01 +0800191 lambda event: self.re_run_failed(root=test_or_root(event)),
Jon Salz968e90b2012-03-18 16:12:43 +0800192 Event.Type.REVIEW:
193 lambda event: self.show_review_information(),
Jon Salz5f2a0672012-05-22 17:14:06 +0800194 Event.Type.UPDATE_SYSTEM_INFO:
195 lambda event: self.update_system_info(),
Jon Salz0405ab52012-03-16 15:26:52 +0800196 }
197
Jon Salz73e0fd02012-04-04 11:46:38 +0800198 self.exceptions = []
Jon Salz258a40c2012-04-19 12:34:01 +0800199 self.web_socket_manager = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800200
201 def destroy(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800202 if self.chrome:
203 self.chrome.kill()
204 self.chrome = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800205 if self.ui_process:
Jon Salz258a40c2012-04-19 12:34:01 +0800206 utils.kill_process_tree(self.ui_process, 'ui')
Jon Salz73e0fd02012-04-04 11:46:38 +0800207 self.ui_process = None
Jon Salz258a40c2012-04-19 12:34:01 +0800208 if self.web_socket_manager:
209 logging.info('Stopping web sockets')
210 self.web_socket_manager.close()
211 self.web_socket_manager = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800212 if self.state_server_thread:
213 logging.info('Stopping state server')
214 self.state_server.shutdown()
215 self.state_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800216 self.state_server.server_close()
217 self.state_server_thread = None
Jon Salz66f65e62012-05-24 17:40:26 +0800218 if self.state_instance:
219 self.state_instance.close()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800220 if self.event_server_thread:
221 logging.info('Stopping event server')
222 self.event_server.shutdown() # pylint: disable=E1101
223 self.event_server_thread.join()
Jon Salz73e0fd02012-04-04 11:46:38 +0800224 self.event_server.server_close()
225 self.event_server_thread = None
Jon Salz8375c2e2012-04-04 15:22:24 +0800226 if self.prespawner:
227 logging.info('Stopping prespawner')
228 self.prespawner.stop()
229 self.prespawner = None
230 if self.event_client:
Jon Salz258a40c2012-04-19 12:34:01 +0800231 logging.info('Closing event client')
Jon Salz8375c2e2012-04-04 15:22:24 +0800232 self.event_client.close()
Jon Salz258a40c2012-04-19 12:34:01 +0800233 self.event_client = None
Jon Salzeb8d25f2012-05-22 15:17:32 +0800234 if self.event_log:
235 self.event_log.Close()
236 self.event_log = None
Jon Salz73e0fd02012-04-04 11:46:38 +0800237 self.check_exceptions()
Jon Salz258a40c2012-04-19 12:34:01 +0800238 logging.info('Done destroying Goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800239
240 def start_state_server(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800241 self.state_instance, self.state_server = (
242 state.create_server(bind_address='0.0.0.0'))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800243 logging.info('Starting state server')
244 self.state_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800245 target=self.state_server.serve_forever,
246 name='StateServer')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800247 self.state_server_thread.start()
248
249 def start_event_server(self):
250 self.event_server = EventServer()
251 logging.info('Starting factory event server')
252 self.event_server_thread = threading.Thread(
Jon Salz8375c2e2012-04-04 15:22:24 +0800253 target=self.event_server.serve_forever,
254 name='EventServer') # pylint: disable=E1101
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800255 self.event_server_thread.start()
256
257 self.event_client = EventClient(
258 callback=self.handle_event, event_loop=self.run_queue)
259
Jon Salz258a40c2012-04-19 12:34:01 +0800260 self.web_socket_manager = WebSocketManager(self.uuid)
261 self.state_server.add_handler("/event",
262 self.web_socket_manager.handle_web_socket)
263
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800264 def start_ui(self):
Jon Salz258a40c2012-04-19 12:34:01 +0800265 ui_proc_args = [os.path.join(factory.CROS_FACTORY_LIB_PATH, 'ui'),
266 self.options.test_list]
Jon Salz14bcbb02012-03-17 15:11:50 +0800267 if self.options.verbose:
268 ui_proc_args.append('-v')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800269 logging.info('Starting ui %s', ui_proc_args)
270 self.ui_process = subprocess.Popen(ui_proc_args)
271 logging.info('Waiting for UI to come up...')
272 self.event_client.wait(
273 lambda event: event.type == Event.Type.UI_READY)
274 logging.info('UI has started')
275
276 def set_visible_test(self, test):
277 if self.visible_test == test:
278 return
279
280 if test:
281 test.update_state(visible=True)
282 if self.visible_test:
283 self.visible_test.update_state(visible=False)
284 self.visible_test = test
285
Jon Salz74ad3262012-03-16 14:40:55 +0800286 def handle_shutdown_complete(self, test, state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800287 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800288 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800289
Jon Salz74ad3262012-03-16 14:40:55 +0800290 @param test: The ShutdownStep.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800291 @param state: The test state.
292 '''
Jon Salz74ad3262012-03-16 14:40:55 +0800293 state = test.update_state(increment_shutdown_count=1)
294 logging.info('Detected shutdown (%d of %d)',
295 state.shutdown_count, test.iterations)
296 if state.shutdown_count == test.iterations:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800297 # Good!
298 test.update_state(status=TestState.PASSED, error_msg='')
Jon Salz74ad3262012-03-16 14:40:55 +0800299 elif state.shutdown_count > test.iterations:
Jon Salz73e0fd02012-04-04 11:46:38 +0800300 # Shut down too many times
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800301 test.update_state(status=TestState.FAILED,
Jon Salz74ad3262012-03-16 14:40:55 +0800302 error_msg='Too many shutdowns')
Jon Salz258a40c2012-04-19 12:34:01 +0800303 elif utils.are_shift_keys_depressed():
Jon Salz73e0fd02012-04-04 11:46:38 +0800304 logging.info('Shift keys are depressed; cancelling restarts')
305 # Abort shutdown
306 test.update_state(
307 status=TestState.FAILED,
308 error_msg='Shutdown aborted with double shift keys')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800309 else:
Jon Salz74ad3262012-03-16 14:40:55 +0800310 # Need to shutdown again
Jon Salzb9038572012-05-24 10:34:51 +0800311 self.event_log.Log('shutdown', operation='reboot')
Jon Salz73e0fd02012-04-04 11:46:38 +0800312 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800313
314 def init_states(self):
315 '''
316 Initializes all states on startup.
317 '''
318 for test in self.test_list.get_all_tests():
319 # Make sure the state server knows about all the tests,
320 # defaulting to an untested state.
321 test.update_state(update_parent=False, visible=False)
322
323 # Any 'active' tests should be marked as failed now.
324 for test in self.test_list.walk():
325 state = test.get_state()
Hung-Te Lin96632362012-03-20 21:14:18 +0800326 if state.status != TestState.ACTIVE:
327 continue
328 if isinstance(test, factory.ShutdownStep):
329 # Shutdown while the test was active - that's good.
330 self.handle_shutdown_complete(test, state)
331 else:
332 test.update_state(status=TestState.FAILED,
333 error_msg='Unknown (shutdown?)')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800334
335 def show_next_active_test(self):
336 '''
337 Rotates to the next visible active test.
338 '''
339 self.reap_completed_tests()
340 active_tests = [
341 t for t in self.test_list.walk()
342 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
343 if not active_tests:
344 return
345
346 try:
347 next_test = active_tests[
348 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
349 except ValueError: # visible_test not present in active_tests
350 next_test = active_tests[0]
351
352 self.set_visible_test(next_test)
353
354 def handle_event(self, event):
355 '''
356 Handles an event from the event server.
357 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800358 handler = self.event_handlers.get(event.type)
359 if handler:
Jon Salz968e90b2012-03-18 16:12:43 +0800360 handler(event)
Jon Salz0405ab52012-03-16 15:26:52 +0800361 else:
Jon Salz968e90b2012-03-18 16:12:43 +0800362 # We don't register handlers for all event types - just ignore
363 # this event.
364 logging.debug('Unbound event type %s', event.type)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800365
366 def run_next_test(self):
367 '''
368 Runs the next eligible test (or tests) in self.tests_to_run.
369 '''
370 self.reap_completed_tests()
371 while self.tests_to_run:
372 logging.debug('Tests to run: %s',
373 [x.path for x in self.tests_to_run])
374
375 test = self.tests_to_run[0]
376
377 if test in self.invocations:
378 logging.info('Next test %s is already running', test.path)
379 self.tests_to_run.popleft()
380 return
381
382 if self.invocations and not (test.backgroundable and all(
383 [x.backgroundable for x in self.invocations])):
384 logging.debug('Waiting for non-backgroundable tests to '
385 'complete before running %s', test.path)
386 return
387
388 self.tests_to_run.popleft()
389
Jon Salz74ad3262012-03-16 14:40:55 +0800390 if isinstance(test, factory.ShutdownStep):
Jon Salz8796e362012-05-24 11:39:09 +0800391 if os.path.exists(NO_REBOOT_FILE):
392 test.update_state(
393 status=TestState.FAILED, increment_count=1,
394 error_msg=('Skipped shutdown since %s is present' %
395 NO_REBOOT_FILE))
396 continue
397
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800398 test.update_state(status=TestState.ACTIVE, increment_count=1,
Jon Salz74ad3262012-03-16 14:40:55 +0800399 error_msg='', shutdown_count=0)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800400 # Save pending test list in the state server
401 self.state_instance.set_shared_data(
Jon Salz74ad3262012-03-16 14:40:55 +0800402 'tests_after_shutdown',
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800403 [t.path for t in self.tests_to_run])
Jon Salz74ad3262012-03-16 14:40:55 +0800404
Jon Salz73e0fd02012-04-04 11:46:38 +0800405 with self.env.lock:
Jon Salzb9038572012-05-24 10:34:51 +0800406 self.event_log.Log('shutdown', operation=test.operation)
Jon Salz73e0fd02012-04-04 11:46:38 +0800407 shutdown_result = self.env.shutdown(test.operation)
408 if shutdown_result:
409 # That's all, folks!
410 self.run_queue.put(None)
411 return
412 else:
413 # Just pass (e.g., in the chroot).
414 test.update_state(status=TestState.PASSED)
415 self.state_instance.set_shared_data(
416 'tests_after_shutdown', None)
417 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800418
419 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
420 self.invocations[test] = invoc
421 if self.visible_test is None and test.has_ui:
422 self.set_visible_test(test)
423 invoc.start()
424
Jon Salz0405ab52012-03-16 15:26:52 +0800425 def run_tests(self, subtrees, untested_only=False):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800426 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800427 Runs tests under subtree.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800428
429 The tests are run in order unless one fails (then stops).
430 Backgroundable tests are run simultaneously; when a foreground test is
431 encountered, we wait for all active tests to finish before continuing.
Jon Salz0405ab52012-03-16 15:26:52 +0800432
433 @param subtrees: Node or nodes containing tests to run (may either be
434 a single test or a list). Duplicates will be ignored.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800435 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800436 if type(subtrees) != list:
437 subtrees = [subtrees]
438
439 # Nodes we've seen so far, to avoid duplicates.
440 seen = set()
441
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800442 self.tests_to_run = deque()
Jon Salz0405ab52012-03-16 15:26:52 +0800443 for subtree in subtrees:
444 for test in subtree.walk():
445 if test in seen:
446 continue
447 seen.add(test)
448
449 if not test.is_leaf():
450 continue
451 if (untested_only and
452 test.get_state().status != TestState.UNTESTED):
453 continue
454 self.tests_to_run.append(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800455 self.run_next_test()
456
457 def reap_completed_tests(self):
458 '''
459 Removes completed tests from the set of active tests.
460
461 Also updates the visible test if it was reaped.
462 '''
463 for t, v in dict(self.invocations).iteritems():
464 if v.is_completed():
465 del self.invocations[t]
466
467 if (self.visible_test is None or
468 self.visible_test not in self.invocations):
469 self.set_visible_test(None)
470 # Make the first running test, if any, the visible test
471 for t in self.test_list.walk():
472 if t in self.invocations:
473 self.set_visible_test(t)
474 break
475
Hung-Te Lin96632362012-03-20 21:14:18 +0800476 def kill_active_tests(self, abort):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800477 '''
478 Kills and waits for all active tests.
Hung-Te Lin96632362012-03-20 21:14:18 +0800479
480 @param abort: True to change state of killed tests to FAILED, False for
481 UNTESTED.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800482 '''
483 self.reap_completed_tests()
484 for test, invoc in self.invocations.items():
485 factory.console.info('Killing active test %s...' % test.path)
486 invoc.abort_and_join()
487 factory.console.info('Killed %s' % test.path)
488 del self.invocations[test]
Hung-Te Lin96632362012-03-20 21:14:18 +0800489 if not abort:
490 test.update_state(status=TestState.UNTESTED)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800491 self.reap_completed_tests()
492
Hung-Te Lin96632362012-03-20 21:14:18 +0800493 def abort_active_tests(self):
494 self.kill_active_tests(True)
495
Jon Salz73e0fd02012-04-04 11:46:38 +0800496 def main(self):
Jon Salzeb8d25f2012-05-22 15:17:32 +0800497 try:
498 self.init()
Jon Salzb9038572012-05-24 10:34:51 +0800499 self.event_log.Log('goofy_init',
500 success=True)
Jon Salzeb8d25f2012-05-22 15:17:32 +0800501 except:
502 if self.event_log:
503 try:
Jon Salzb9038572012-05-24 10:34:51 +0800504 self.event_log.Log('goofy_init',
505 success=False,
506 trace=traceback.format_exc())
Jon Salzeb8d25f2012-05-22 15:17:32 +0800507 except:
508 pass
509 raise
510
Jon Salz73e0fd02012-04-04 11:46:38 +0800511 self.run()
512
Jon Salz5f2a0672012-05-22 17:14:06 +0800513 def update_system_info(self):
514 '''Updates system info.'''
515 system_info = test_environment.SystemInfo(self.env, self.state_instance)
516 self.state_instance.set_shared_data('system_info', system_info.__dict__)
517 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
518 system_info=system_info.__dict__))
519 logging.info('System info: %r', system_info.__dict__)
520
Jon Salz73e0fd02012-04-04 11:46:38 +0800521 def init(self, args=None, env=None):
522 '''Initializes Goofy.
Jon Salz74ad3262012-03-16 14:40:55 +0800523
524 Args:
Jon Salz73e0fd02012-04-04 11:46:38 +0800525 args: A list of command-line arguments. Uses sys.argv if
526 args is None.
527 env: An Environment instance to use (or None to choose
Jon Salz258a40c2012-04-19 12:34:01 +0800528 FakeChrootEnvironment or DUTEnvironment as appropriate).
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800529 '''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800530 parser = OptionParser()
531 parser.add_option('-v', '--verbose', dest='verbose',
532 action='store_true',
533 help='Enable debug logging')
534 parser.add_option('--print_test_list', dest='print_test_list',
535 metavar='FILE',
536 help='Read and print test list FILE, and exit')
Jon Salz758e6cc2012-04-03 15:47:07 +0800537 parser.add_option('--restart', dest='restart',
538 action='store_true',
539 help='Clear all test state')
Jon Salz258a40c2012-04-19 12:34:01 +0800540 parser.add_option('--ui', dest='ui', type='choice',
541 choices=['none', 'gtk', 'chrome'],
542 default='gtk',
543 help='UI to use')
Jon Salz63585ea2012-05-21 15:03:32 +0800544 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
545 type='int', default=1,
546 help=('Factor by which to scale UI '
547 '(Chrome UI only)'))
Jon Salz73e0fd02012-04-04 11:46:38 +0800548 parser.add_option('--test_list', dest='test_list',
549 metavar='FILE',
550 help='Use FILE as test list')
551 (self.options, self.args) = parser.parse_args(args)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800552
Jon Salz73e0fd02012-04-04 11:46:38 +0800553 global _inited_logging
554 if not _inited_logging:
555 factory.init_logging('goofy', verbose=self.options.verbose)
556 _inited_logging = True
Jon Salzeb8d25f2012-05-22 15:17:32 +0800557 self.event_log = EventLog('goofy')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800558
Jon Salz73e0fd02012-04-04 11:46:38 +0800559 if (not suppress_chroot_warning and
560 factory.in_chroot() and
Jon Salz258a40c2012-04-19 12:34:01 +0800561 self.options.ui == 'gtk' and
Jon Salz758e6cc2012-04-03 15:47:07 +0800562 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
563 # That's not going to work! Tell the user how to run
564 # this way.
565 logging.warn(GOOFY_IN_CHROOT_WARNING)
566 time.sleep(1)
567
Jon Salz73e0fd02012-04-04 11:46:38 +0800568 if env:
569 self.env = env
570 elif factory.in_chroot():
Jon Salz5f2a0672012-05-22 17:14:06 +0800571 self.env = test_environment.FakeChrootEnvironment()
Jon Salz73e0fd02012-04-04 11:46:38 +0800572 logging.warn(
573 'Using chroot environment: will not actually run autotests')
574 else:
Jon Salz5f2a0672012-05-22 17:14:06 +0800575 self.env = test_environment.DUTEnvironment()
Jon Salz323dd3d2012-04-09 18:40:43 +0800576 self.env.goofy = self
Jon Salz73e0fd02012-04-04 11:46:38 +0800577
Jon Salz758e6cc2012-04-03 15:47:07 +0800578 if self.options.restart:
579 state.clear_state()
580
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800581 if self.options.print_test_list:
582 print (factory.read_test_list(self.options.print_test_list).
583 __repr__(recursive=True))
584 return
585
586 logging.info('Started')
587
588 self.start_state_server()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800589 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz63585ea2012-05-21 15:03:32 +0800590 self.state_instance.set_shared_data('ui_scale_factor',
591 self.options.ui_scale_factor)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800592
Jon Salz73e0fd02012-04-04 11:46:38 +0800593 self.options.test_list = (self.options.test_list or find_test_list())
594 self.test_list = factory.read_test_list(self.options.test_list,
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800595 self.state_instance)
Jon Salz06fbeff2012-05-21 17:06:05 +0800596 if not self.state_instance.has_shared_data('ui_lang'):
597 self.state_instance.set_shared_data('ui_lang',
598 self.test_list.options.ui_lang)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800599 logging.info('TEST_LIST:\n%s', self.test_list.__repr__(recursive=True))
Jon Salz258a40c2012-04-19 12:34:01 +0800600 self.state_instance.test_list = self.test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800601
602 self.init_states()
603 self.start_event_server()
Jon Salz258a40c2012-04-19 12:34:01 +0800604
Jon Salz5f2a0672012-05-22 17:14:06 +0800605 self.update_system_info()
606
Jon Salzb1b39092012-05-03 02:05:09 +0800607 # Set CROS_UI since some behaviors in ui.py depend on the
608 # particular UI in use. TODO(jsalz): Remove this (and all
609 # places it is used) when the GTK UI is removed.
610 os.environ['CROS_UI'] = self.options.ui
Jon Salz258a40c2012-04-19 12:34:01 +0800611
Jon Salzb1b39092012-05-03 02:05:09 +0800612 if self.options.ui == 'chrome':
Jon Salz258a40c2012-04-19 12:34:01 +0800613 self.env.launch_chrome()
614 logging.info('Waiting for a web socket connection')
615 self.web_socket_manager.wait()
Jon Salzb1b39092012-05-03 02:05:09 +0800616
617 # Wait for the test widget size to be set; this is done in
618 # an asynchronous RPC so there is a small chance that the
619 # web socket might be opened first.
620 for i in range(100): # 10 s
Jon Salz63585ea2012-05-21 15:03:32 +0800621 try:
622 if self.state_instance.get_shared_data('test_widget_size'):
623 break
624 except KeyError:
625 pass # Retry
Jon Salzb1b39092012-05-03 02:05:09 +0800626 time.sleep(0.1) # 100 ms
627 else:
628 logging.warn('Never received test_widget_size from UI')
Jon Salz258a40c2012-04-19 12:34:01 +0800629 elif self.options.ui == 'gtk':
Jon Salz73e0fd02012-04-04 11:46:38 +0800630 self.start_ui()
Jon Salz258a40c2012-04-19 12:34:01 +0800631
Jon Salz8375c2e2012-04-04 15:22:24 +0800632 self.prespawner = Prespawner()
Jon Salz323dd3d2012-04-09 18:40:43 +0800633 self.prespawner.start()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800634
635 def state_change_callback(test, state):
636 self.event_client.post_event(
637 Event(Event.Type.STATE_CHANGE,
638 path=test.path, state=state))
639 self.test_list.state_change_callback = state_change_callback
640
641 try:
Jon Salz758e6cc2012-04-03 15:47:07 +0800642 tests_after_shutdown = self.state_instance.get_shared_data(
643 'tests_after_shutdown')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800644 except KeyError:
Jon Salz758e6cc2012-04-03 15:47:07 +0800645 tests_after_shutdown = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800646
Jon Salz758e6cc2012-04-03 15:47:07 +0800647 if tests_after_shutdown is not None:
648 logging.info('Resuming tests after shutdown: %s',
649 tests_after_shutdown)
650 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800651 self.tests_to_run.extend(
Jon Salz758e6cc2012-04-03 15:47:07 +0800652 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Jon Salz73e0fd02012-04-04 11:46:38 +0800653 self.run_queue.put(self.run_next_test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800654 else:
Jon Salz57717ca2012-04-04 16:47:25 +0800655 if self.test_list.options.auto_run_on_start:
656 self.run_queue.put(
657 lambda: self.run_tests(self.test_list, untested_only=True))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800658
Jon Salz73e0fd02012-04-04 11:46:38 +0800659 def run(self):
660 '''Runs Goofy.'''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800661 # Process events forever.
Jon Salz57717ca2012-04-04 16:47:25 +0800662 while self.run_once(True):
Jon Salz73e0fd02012-04-04 11:46:38 +0800663 pass
664
Jon Salz57717ca2012-04-04 16:47:25 +0800665 def run_once(self, block=False):
Jon Salz73e0fd02012-04-04 11:46:38 +0800666 '''Runs all items pending in the event loop.
667
Jon Salz57717ca2012-04-04 16:47:25 +0800668 Args:
669 block: If true, block until at least one event is processed.
670
Jon Salz73e0fd02012-04-04 11:46:38 +0800671 Returns:
672 True to keep going or False to shut down.
673 '''
Jon Salz57717ca2012-04-04 16:47:25 +0800674 events = []
675 if block:
676 # Get at least one event
677 events.append(self.run_queue.get())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800678 while True:
Jon Salz73e0fd02012-04-04 11:46:38 +0800679 try:
680 events.append(self.run_queue.get_nowait())
681 except Queue.Empty:
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800682 break
683
Jon Salz73e0fd02012-04-04 11:46:38 +0800684 for event in events:
685 if not event:
686 # Shutdown request.
687 self.run_queue.task_done()
688 return False
689
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800690 try:
691 event()
692 except Exception as e: # pylint: disable=W0703
693 logging.error('Error in event loop: %s', e)
694 traceback.print_exc(sys.stderr)
Jon Salz8375c2e2012-04-04 15:22:24 +0800695 self.record_exception(traceback.format_exception_only(
696 *sys.exc_info()[:2]))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800697 # But keep going
698 finally:
699 self.run_queue.task_done()
Jon Salz73e0fd02012-04-04 11:46:38 +0800700 return True
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800701
Jon Salz258a40c2012-04-19 12:34:01 +0800702 def run_tests_with_status(self, statuses_to_run, starting_at=None,
703 root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800704 '''Runs all top-level tests with a particular status.
705
706 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +0800707
708 Args:
709 starting_at: If provided, only auto-runs tests beginning with
710 this test.
Jon Salz0405ab52012-03-16 15:26:52 +0800711 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800712 root = root or self.test_list
713
Jon Salz57717ca2012-04-04 16:47:25 +0800714 if starting_at:
715 # Make sure they passed a test, not a string.
716 assert isinstance(starting_at, factory.FactoryTest)
717
Jon Salz0405ab52012-03-16 15:26:52 +0800718 tests_to_reset = []
719 tests_to_run = []
720
Jon Salz57717ca2012-04-04 16:47:25 +0800721 found_starting_at = False
722
Jon Salz258a40c2012-04-19 12:34:01 +0800723 for test in root.get_top_level_tests():
Jon Salz57717ca2012-04-04 16:47:25 +0800724 if starting_at:
725 if test == starting_at:
726 # We've found starting_at; do auto-run on all
727 # subsequent tests.
728 found_starting_at = True
729 if not found_starting_at:
730 # Don't start this guy yet
731 continue
732
Jon Salz0405ab52012-03-16 15:26:52 +0800733 status = test.get_state().status
734 if status == TestState.ACTIVE or status in statuses_to_run:
735 # Reset the test (later; we will need to abort
736 # all active tests first).
737 tests_to_reset.append(test)
738 if status in statuses_to_run:
739 tests_to_run.append(test)
740
741 self.abort_active_tests()
742
743 # Reset all statuses of the tests to run (in case any tests were active;
744 # we want them to be run again).
745 for test_to_reset in tests_to_reset:
746 for test in test_to_reset.walk():
747 test.update_state(status=TestState.UNTESTED)
748
749 self.run_tests(tests_to_run, untested_only=True)
750
Jon Salz258a40c2012-04-19 12:34:01 +0800751 def restart_tests(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800752 '''Restarts all tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800753 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +0800754
Jon Salz258a40c2012-04-19 12:34:01 +0800755 self.abort_active_tests()
756 for test in root.walk():
757 test.update_state(status=TestState.UNTESTED)
758 self.run_tests(root)
759
760 def auto_run(self, starting_at=None, root=None):
Jon Salz57717ca2012-04-04 16:47:25 +0800761 '''"Auto-runs" tests that have not been run yet.
762
763 Args:
764 starting_at: If provide, only auto-runs tests beginning with
765 this test.
766 '''
Jon Salz258a40c2012-04-19 12:34:01 +0800767 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +0800768 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Jon Salz258a40c2012-04-19 12:34:01 +0800769 starting_at=starting_at,
770 root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800771
Jon Salz258a40c2012-04-19 12:34:01 +0800772 def re_run_failed(self, root=None):
Jon Salz0405ab52012-03-16 15:26:52 +0800773 '''Re-runs failed tests.'''
Jon Salz258a40c2012-04-19 12:34:01 +0800774 root = root or self.test_list
775 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz0405ab52012-03-16 15:26:52 +0800776
Jon Salz968e90b2012-03-18 16:12:43 +0800777 def show_review_information(self):
Hung-Te Lin96632362012-03-20 21:14:18 +0800778 '''Event handler for showing review information screen.
779
780 The information screene is rendered by main UI program (ui.py), so in
781 goofy we only need to kill all active tests, set them as untested, and
782 clear remaining tests.
783 '''
784 self.kill_active_tests(False)
785 self.run_tests([])
786
Jon Salz0405ab52012-03-16 15:26:52 +0800787 def handle_switch_test(self, event):
Jon Salz968e90b2012-03-18 16:12:43 +0800788 '''Switches to a particular test.
789
790 @param event: The SWITCH_TEST event.
791 '''
Jon Salz0405ab52012-03-16 15:26:52 +0800792 test = self.test_list.lookup_path(event.path)
Jon Salz57717ca2012-04-04 16:47:25 +0800793 if not test:
Jon Salz968e90b2012-03-18 16:12:43 +0800794 logging.error('Unknown test %r', event.key)
Jon Salz57717ca2012-04-04 16:47:25 +0800795 return
796
797 invoc = self.invocations.get(test)
798 if invoc and test.backgroundable:
799 # Already running: just bring to the front if it
800 # has a UI.
801 logging.info('Setting visible test to %s', test.path)
802 self.event_client.post_event(
803 Event(Event.Type.SET_VISIBLE_TEST, path=test.path))
804 return
805
806 self.abort_active_tests()
807 for t in test.walk():
808 t.update_state(status=TestState.UNTESTED)
809
810 if self.test_list.options.auto_run_on_keypress:
811 self.auto_run(starting_at=test)
812 else:
813 self.run_tests(test)
Jon Salz0405ab52012-03-16 15:26:52 +0800814
Jon Salz73e0fd02012-04-04 11:46:38 +0800815 def wait(self):
816 '''Waits for all pending invocations.
817
818 Useful for testing.
819 '''
820 for k, v in self.invocations.iteritems():
821 logging.info('Waiting for %s to complete...', k)
822 v.thread.join()
823
824 def check_exceptions(self):
825 '''Raises an error if any exceptions have occurred in
826 invocation threads.'''
827 if self.exceptions:
828 raise RuntimeError('Exception in invocation thread: %r' %
829 self.exceptions)
830
831 def record_exception(self, msg):
832 '''Records an exception in an invocation thread.
833
834 An exception with the given message will be rethrown when
835 Goofy is destroyed.'''
836 self.exceptions.append(msg)
837
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800838
839if __name__ == '__main__':
840 Goofy().main()