blob: eae3efcec1b4aac318e6145e536a08c71f3e0e54 [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
Hung-Te Linf2f78f72012-02-08 19:27:11 +08002# -*- coding: utf-8 -*-
3#
Jon Salz37eccbd2012-05-25 16:06:52 +08004# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08005# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8'''
9The main factory flow that runs the factory test and finalizes a device.
10'''
11
Jon Salz0405ab52012-03-16 15:26:52 +080012import logging
13import os
Jon Salz73e0fd02012-04-04 11:46:38 +080014import Queue
Jon Salz77c151e2012-08-28 07:20:37 +080015import signal
Jon Salz0405ab52012-03-16 15:26:52 +080016import sys
Jon Salzeff94182013-06-19 15:06:28 +080017import syslog
Jon Salz0405ab52012-03-16 15:26:52 +080018import threading
19import time
20import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080021import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080022from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080023from collections import deque
24from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080025
Jon Salz0697cbf2012-07-04 15:14:04 +080026import factory_common # pylint: disable=W0611
jcliangcd688182012-08-20 21:01:26 +080027from cros.factory import event_log
28from cros.factory import system
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +080029from cros.factory.event_log import EventLog, FloatDigit
Tom Wai-Hong Tamd33723e2013-04-10 21:14:37 +080030from cros.factory.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080031from cros.factory.goofy import test_environment
32from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080033from cros.factory.goofy import updater
jcliangcd688182012-08-20 21:01:26 +080034from cros.factory.goofy.goofy_rpc import GoofyRPC
35from cros.factory.goofy.invocation import TestInvocation
36from cros.factory.goofy.prespawner import Prespawner
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +080037from cros.factory.goofy.system_log_manager import SystemLogManager
jcliangcd688182012-08-20 21:01:26 +080038from cros.factory.goofy.web_socket_manager import WebSocketManager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +080039from cros.factory.system.board import Board, BoardException
jcliangcd688182012-08-20 21:01:26 +080040from cros.factory.system.charge_manager import ChargeManager
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +080041from cros.factory.system.core_dump_manager import CoreDumpManager
Jon Salzce6a7f82013-06-10 18:22:54 +080042from cros.factory.system.cpufreq_manager import CpufreqManager
Jon Salzb92c5112012-09-21 15:40:11 +080043from cros.factory.system import disk_space
jcliangcd688182012-08-20 21:01:26 +080044from cros.factory.test import factory
45from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080046from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080047from cros.factory.test import utils
48from cros.factory.test.event import Event
49from cros.factory.test.event import EventClient
50from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080051from cros.factory.test.factory import TestState
Dean Liao592e4d52013-01-10 20:06:39 +080052from cros.factory.tools.key_filter import KeyFilter
Jon Salz2af235d2013-06-24 14:47:21 +080053from cros.factory.utils import file_utils
Jon Salz78c32392012-07-25 14:18:29 +080054from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080055
56
Jon Salz2f757d42012-06-27 17:06:42 +080057CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080058HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Chun-ta Lin279e7e92013-02-19 17:40:39 +080059CACHES_DIR = os.path.join(factory.get_state_root(), "caches")
Hung-Te Linf2f78f72012-02-08 19:27:11 +080060
Jon Salz8796e362012-05-24 11:39:09 +080061# File that suppresses reboot if present (e.g., for development).
62NO_REBOOT_FILE = '/var/log/factory.noreboot'
63
Jon Salz5c344f62012-07-13 14:31:16 +080064# Value for tests_after_shutdown that forces auto-run (e.g., after
65# a factory update, when the available set of tests might change).
66FORCE_AUTO_RUN = 'force_auto_run'
67
cychiang21886742012-07-05 15:16:32 +080068RUN_QUEUE_TIMEOUT_SECS = 10
69
Justin Chuang83813982013-05-13 01:26:32 +080070# Sync disks when battery level is higher than this value.
71# Otherwise, power loss during disk sync operation may incur even worse outcome.
72MIN_BATTERY_LEVEL_FOR_DISK_SYNC = 1.0
73
Jon Salz758e6cc2012-04-03 15:47:07 +080074GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
75You are running Goofy inside the chroot. Autotests are not supported.
76
77To use Goofy in the chroot, first install an Xvnc server:
78
Jon Salz0697cbf2012-07-04 15:14:04 +080079 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080080
81...and then start a VNC X server outside the chroot:
82
Jon Salz0697cbf2012-07-04 15:14:04 +080083 vncserver :10 &
84 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080085
86...and run Goofy as follows:
87
Jon Salz0697cbf2012-07-04 15:14:04 +080088 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080089''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080090suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080091
92def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080093 '''
94 Returns the HWID config tag, or an empty string if none can be found.
95 '''
96 if 'CROS_HWID' in os.environ:
97 return os.environ['CROS_HWID']
98 if os.path.exists(HWID_CFG_PATH):
99 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
100 return hwid_cfg_handle.read().strip()
101 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800102
103
104def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +0800105 '''
106 Returns the path to the active test list, based on the HWID config tag.
Jon Salzfb615892013-02-01 18:04:35 +0800107
108 The algorithm is:
109
110 - Try $FACTORY/test_lists/active (the symlink reflecting the option chosen
111 in the UI).
112 - For each of $FACTORY/custom, $FACTORY/test_lists (and
113 autotest/site_tests/suite_Factory for backward compatibility):
114 - Try test_list_${hwid_cfg} (if hwid_cfg is set)
115 - Try test_list
116 - Try test_list.generic
Jon Salz0697cbf2012-07-04 15:14:04 +0800117 '''
Jon Salzfb615892013-02-01 18:04:35 +0800118 # If the 'active' symlink is present, that trumps everything else.
119 if os.path.lexists(factory.ACTIVE_TEST_LIST_SYMLINK):
120 return os.path.realpath(factory.ACTIVE_TEST_LIST_SYMLINK)
121
Jon Salz0697cbf2012-07-04 15:14:04 +0800122 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800123
Jon Salzfb615892013-02-01 18:04:35 +0800124 search_dirs = [CUSTOM_DIR, factory.TEST_LISTS_PATH]
Jon Salz4be56b02012-12-22 07:30:46 +0800125 if not utils.in_chroot():
126 # Also look in suite_Factory. For backward compatibility only;
127 # new boards should just put the test list in the "test_lists"
128 # directory.
129 search_dirs.insert(0, os.path.join(
130 os.path.dirname(factory.FACTORY_PATH),
131 'autotest', 'site_tests', 'suite_Factory'))
Jon Salz2f757d42012-06-27 17:06:42 +0800132
Jon Salzfb615892013-02-01 18:04:35 +0800133
134 search_files = []
Jon Salz0697cbf2012-07-04 15:14:04 +0800135 if hwid_cfg:
Jon Salzfb615892013-02-01 18:04:35 +0800136 search_files += [hwid_cfg]
137 search_files += ['test_list', 'test_list.generic']
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800138
Jon Salz0697cbf2012-07-04 15:14:04 +0800139 for d in search_dirs:
140 for f in search_files:
141 test_list = os.path.join(d, f)
142 if os.path.exists(test_list):
143 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800144
Jon Salz0697cbf2012-07-04 15:14:04 +0800145 logging.warn('Cannot find test lists named any of %s in any of %s',
146 search_files, search_dirs)
147 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800148
Jon Salzfb615892013-02-01 18:04:35 +0800149
Jon Salz73e0fd02012-04-04 11:46:38 +0800150_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800151
152class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800153 '''
154 The main factory flow.
155
156 Note that all methods in this class must be invoked from the main
157 (event) thread. Other threads, such as callbacks and TestInvocation
158 methods, should instead post events on the run queue.
159
160 TODO: Unit tests. (chrome-os-partner:7409)
161
162 Properties:
163 uuid: A unique UUID for this invocation of Goofy.
164 state_instance: An instance of FactoryState.
165 state_server: The FactoryState XML/RPC server.
166 state_server_thread: A thread running state_server.
167 event_server: The EventServer socket server.
168 event_server_thread: A thread running event_server.
169 event_client: A client to the event server.
170 connection_manager: The connection_manager object.
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800171 system_log_manager: The SystemLogManager object.
172 core_dump_manager: The CoreDumpManager object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800173 ui_process: The factory ui process object.
174 run_queue: A queue of callbacks to invoke from the main thread.
175 invocations: A map from FactoryTest objects to the corresponding
176 TestInvocations objects representing active tests.
177 tests_to_run: A deque of tests that should be run when the current
178 test(s) complete.
179 options: Command-line options.
180 args: Command-line args.
181 test_list: The test list.
182 event_handlers: Map of Event.Type to the method used to handle that
183 event. If the method has an 'event' argument, the event is passed
184 to the handler.
185 exceptions: Exceptions encountered in invocation threads.
Jon Salz3c493bb2013-02-07 17:24:58 +0800186 last_log_disk_space_message: The last message we logged about disk space
187 (to avoid duplication).
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800188 last_kick_sync_time: The last time to kick system_log_manager to sync
189 because of core dump files (to avoid kicking too soon then abort the
190 sync.)
Jon Salz416f9cc2013-05-10 18:32:50 +0800191 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salz0697cbf2012-07-04 15:14:04 +0800192 '''
193 def __init__(self):
194 self.uuid = str(uuid.uuid4())
195 self.state_instance = None
196 self.state_server = None
197 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800198 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800199 self.event_server = None
200 self.event_server_thread = None
201 self.event_client = None
202 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800203 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800204 self.time_sanitizer = None
205 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800206 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800207 self.system_log_manager = None
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800208 self.core_dump_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800209 self.event_log = None
210 self.prespawner = None
211 self.ui_process = None
Jon Salzc79a9982012-08-30 04:42:01 +0800212 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800213 self.run_queue = Queue.Queue()
214 self.invocations = {}
215 self.tests_to_run = deque()
216 self.visible_test = None
217 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800218 self.hooks = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800219
220 self.options = None
221 self.args = None
222 self.test_list = None
223 self.on_ui_startup = []
224 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800225 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800226 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800227 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800228 self.last_sync_time = None
Jon Salzb92c5112012-09-21 15:40:11 +0800229 self.last_log_disk_space_time = None
Jon Salz3c493bb2013-02-07 17:24:58 +0800230 self.last_log_disk_space_message = None
Justin Chuang83813982013-05-13 01:26:32 +0800231 self.last_check_battery_time = None
232 self.last_check_battery_message = None
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800233 self.last_kick_sync_time = None
Vic Yang311ddb82012-09-26 12:08:28 +0800234 self.exclusive_items = set()
Jon Salz0f996602012-10-03 15:26:48 +0800235 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800236 self.key_filter = None
Jon Salzce6a7f82013-06-10 18:22:54 +0800237 self.cpufreq_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800238
Jon Salz85a39882012-07-05 16:45:04 +0800239 def test_or_root(event, parent_or_group=True):
240 '''Returns the test affected by a particular event.
241
242 Args:
243 event: The event containing an optional 'path' attribute.
244 parent_on_group: If True, returns the top-level parent for a test (the
245 root node of the tests that need to be run together if the given test
246 path is to be run).
247 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800248 try:
249 path = event.path
250 except AttributeError:
251 path = None
252
253 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800254 test = self.test_list.lookup_path(path)
255 if parent_or_group:
256 test = test.get_top_level_parent_or_group()
257 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800258 else:
259 return self.test_list
260
261 self.event_handlers = {
262 Event.Type.SWITCH_TEST: self.handle_switch_test,
263 Event.Type.SHOW_NEXT_ACTIVE_TEST:
264 lambda event: self.show_next_active_test(),
265 Event.Type.RESTART_TESTS:
266 lambda event: self.restart_tests(root=test_or_root(event)),
267 Event.Type.AUTO_RUN:
268 lambda event: self.auto_run(root=test_or_root(event)),
269 Event.Type.RE_RUN_FAILED:
270 lambda event: self.re_run_failed(root=test_or_root(event)),
271 Event.Type.RUN_TESTS_WITH_STATUS:
272 lambda event: self.run_tests_with_status(
273 event.status,
274 root=test_or_root(event)),
275 Event.Type.REVIEW:
276 lambda event: self.show_review_information(),
277 Event.Type.UPDATE_SYSTEM_INFO:
278 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800279 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800280 lambda event: self.stop(root=test_or_root(event, False),
Jon Salz6dc031d2013-06-19 13:06:23 +0800281 fail=getattr(event, 'fail', False),
282 reason=getattr(event, 'reason', None)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800283 Event.Type.SET_VISIBLE_TEST:
284 lambda event: self.set_visible_test(
285 self.test_list.lookup_path(event.path)),
Jon Salz4712ac72013-02-07 17:12:05 +0800286 Event.Type.CLEAR_STATE:
287 lambda event: self.clear_state(self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800288 }
289
290 self.exceptions = []
291 self.web_socket_manager = None
292
293 def destroy(self):
294 if self.chrome:
295 self.chrome.kill()
296 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800297 if self.dummy_shopfloor:
298 self.dummy_shopfloor.kill()
299 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800300 if self.ui_process:
301 utils.kill_process_tree(self.ui_process, 'ui')
302 self.ui_process = None
303 if self.web_socket_manager:
304 logging.info('Stopping web sockets')
305 self.web_socket_manager.close()
306 self.web_socket_manager = None
307 if self.state_server_thread:
308 logging.info('Stopping state server')
309 self.state_server.shutdown()
310 self.state_server_thread.join()
311 self.state_server.server_close()
312 self.state_server_thread = None
313 if self.state_instance:
314 self.state_instance.close()
315 if self.event_server_thread:
316 logging.info('Stopping event server')
317 self.event_server.shutdown() # pylint: disable=E1101
318 self.event_server_thread.join()
319 self.event_server.server_close()
320 self.event_server_thread = None
321 if self.log_watcher:
322 if self.log_watcher.IsThreadStarted():
323 self.log_watcher.StopWatchThread()
324 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800325 if self.system_log_manager:
326 if self.system_log_manager.IsThreadRunning():
327 self.system_log_manager.StopSyncThread()
328 self.system_log_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800329 if self.prespawner:
330 logging.info('Stopping prespawner')
331 self.prespawner.stop()
332 self.prespawner = None
333 if self.event_client:
334 logging.info('Closing event client')
335 self.event_client.close()
336 self.event_client = None
Jon Salzddf0d052013-06-18 12:52:44 +0800337 if self.cpufreq_manager:
338 self.cpufreq_manager.Stop()
Jon Salz0697cbf2012-07-04 15:14:04 +0800339 if self.event_log:
340 self.event_log.Close()
341 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800342 if self.key_filter:
343 self.key_filter.Stop()
344
Jon Salz0697cbf2012-07-04 15:14:04 +0800345 self.check_exceptions()
346 logging.info('Done destroying Goofy')
347
348 def start_state_server(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800349 # Before starting state server, remount stateful partitions with
350 # no commit flag. The default commit time (commit=600) makes corruption
351 # too likely.
352 file_utils.ResetCommitTime()
353
Jon Salz0697cbf2012-07-04 15:14:04 +0800354 self.state_instance, self.state_server = (
355 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800356 self.goofy_rpc = GoofyRPC(self)
357 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800358 logging.info('Starting state server')
359 self.state_server_thread = threading.Thread(
360 target=self.state_server.serve_forever,
361 name='StateServer')
362 self.state_server_thread.start()
363
364 def start_event_server(self):
365 self.event_server = EventServer()
366 logging.info('Starting factory event server')
367 self.event_server_thread = threading.Thread(
368 target=self.event_server.serve_forever,
369 name='EventServer') # pylint: disable=E1101
370 self.event_server_thread.start()
371
372 self.event_client = EventClient(
373 callback=self.handle_event, event_loop=self.run_queue)
374
375 self.web_socket_manager = WebSocketManager(self.uuid)
376 self.state_server.add_handler("/event",
377 self.web_socket_manager.handle_web_socket)
378
379 def start_ui(self):
380 ui_proc_args = [
381 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
382 self.options.test_list]
383 if self.options.verbose:
384 ui_proc_args.append('-v')
385 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800386 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800387 logging.info('Waiting for UI to come up...')
388 self.event_client.wait(
389 lambda event: event.type == Event.Type.UI_READY)
390 logging.info('UI has started')
391
392 def set_visible_test(self, test):
393 if self.visible_test == test:
394 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800395 if test and not test.has_ui:
396 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800397
398 if test:
399 test.update_state(visible=True)
400 if self.visible_test:
401 self.visible_test.update_state(visible=False)
402 self.visible_test = test
403
Jon Salzd4306c82012-11-30 15:16:36 +0800404 def _log_startup_messages(self):
405 '''Logs the tail of var/log/messages and mosys and EC console logs.'''
406 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
407 # for factory-3004.B only. Consolidate and merge back to ToT.
408 if utils.in_chroot():
409 return
410
411 try:
412 var_log_messages = (
413 utils.var_log_messages_before_reboot())
414 logging.info(
415 'Tail of /var/log/messages before last reboot:\n'
416 '%s', ('\n'.join(
417 ' ' + x for x in var_log_messages)))
418 except: # pylint: disable=W0702
419 logging.exception('Unable to grok /var/log/messages')
420
421 try:
422 mosys_log = utils.Spawn(
423 ['mosys', 'eventlog', 'list'],
424 read_stdout=True, log_stderr_on_error=True).stdout_data
425 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
426 except: # pylint: disable=W0702
427 logging.exception('Unable to read mosys eventlog')
428
429 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800430 board = system.GetBoard()
431 ec_console_log = board.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800432 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
433 except: # pylint: disable=W0702
434 logging.exception('Error retrieving EC console log')
435
Jon Salz0697cbf2012-07-04 15:14:04 +0800436 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800437 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800438 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800439
Jon Salz0697cbf2012-07-04 15:14:04 +0800440 @param test: The ShutdownStep.
441 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800442 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800443 test_state = test.update_state(increment_shutdown_count=1)
444 logging.info('Detected shutdown (%d of %d)',
445 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800446
Jon Salz0697cbf2012-07-04 15:14:04 +0800447 def log_and_update_state(status, error_msg, **kw):
448 self.event_log.Log('rebooted',
449 status=status, error_msg=error_msg, **kw)
Jon Salzd4306c82012-11-30 15:16:36 +0800450 logging.info('Rebooted: status=%s, %s', status,
451 (('error_msg=%s' % error_msg) if error_msg else None))
Jon Salz0697cbf2012-07-04 15:14:04 +0800452 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800453
Jon Salz0697cbf2012-07-04 15:14:04 +0800454 if not self.last_shutdown_time:
455 log_and_update_state(status=TestState.FAILED,
456 error_msg='Unable to read shutdown_time')
457 return
Jon Salz258a40c2012-04-19 12:34:01 +0800458
Jon Salz0697cbf2012-07-04 15:14:04 +0800459 now = time.time()
460 logging.info('%.03f s passed since reboot',
461 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800462
Jon Salz0697cbf2012-07-04 15:14:04 +0800463 if self.last_shutdown_time > now:
464 test.update_state(status=TestState.FAILED,
465 error_msg='Time moved backward during reboot')
466 elif (isinstance(test, factory.RebootStep) and
467 self.test_list.options.max_reboot_time_secs and
468 (now - self.last_shutdown_time >
469 self.test_list.options.max_reboot_time_secs)):
470 # A reboot took too long; fail. (We don't check this for
471 # HaltSteps, because the machine could be halted for a
472 # very long time, and even unplugged with battery backup,
473 # thus hosing the clock.)
474 log_and_update_state(
475 status=TestState.FAILED,
476 error_msg=('More than %d s elapsed during reboot '
477 '(%.03f s, from %s to %s)' % (
478 self.test_list.options.max_reboot_time_secs,
479 now - self.last_shutdown_time,
480 utils.TimeString(self.last_shutdown_time),
481 utils.TimeString(now))),
482 duration=(now-self.last_shutdown_time))
Jon Salzd4306c82012-11-30 15:16:36 +0800483 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800484 elif test_state.shutdown_count == test.iterations:
485 # Good!
486 log_and_update_state(status=TestState.PASSED,
487 duration=(now - self.last_shutdown_time),
488 error_msg='')
489 elif test_state.shutdown_count > test.iterations:
490 # Shut down too many times
491 log_and_update_state(status=TestState.FAILED,
492 error_msg='Too many shutdowns')
Jon Salzd4306c82012-11-30 15:16:36 +0800493 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800494 elif utils.are_shift_keys_depressed():
495 logging.info('Shift keys are depressed; cancelling restarts')
496 # Abort shutdown
497 log_and_update_state(
498 status=TestState.FAILED,
499 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800500 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800501 else:
502 def handler():
503 if self._prompt_cancel_shutdown(
504 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800505 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800506 log_and_update_state(
507 status=TestState.FAILED,
508 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800509 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800510 return
Jon Salz0405ab52012-03-16 15:26:52 +0800511
Jon Salz0697cbf2012-07-04 15:14:04 +0800512 # Time to shutdown again
513 log_and_update_state(
514 status=TestState.ACTIVE,
515 error_msg='',
516 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800517
Jon Salz0697cbf2012-07-04 15:14:04 +0800518 self.event_log.Log('shutdown', operation='reboot')
519 self.state_instance.set_shared_data('shutdown_time',
520 time.time())
521 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800522
Jon Salz0697cbf2012-07-04 15:14:04 +0800523 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800524
Jon Salz0697cbf2012-07-04 15:14:04 +0800525 def _prompt_cancel_shutdown(self, test, iteration):
526 if self.options.ui != 'chrome':
527 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800528
Jon Salz0697cbf2012-07-04 15:14:04 +0800529 pending_shutdown_data = {
530 'delay_secs': test.delay_secs,
531 'time': time.time() + test.delay_secs,
532 'operation': test.operation,
533 'iteration': iteration,
534 'iterations': test.iterations,
535 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800536
Jon Salz0697cbf2012-07-04 15:14:04 +0800537 # Create a new (threaded) event client since we
538 # don't want to use the event loop for this.
539 with EventClient() as event_client:
540 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
541 **pending_shutdown_data))
542 aborted = event_client.wait(
543 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
544 timeout=test.delay_secs) is not None
545 if aborted:
546 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
547 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800548
Jon Salz0697cbf2012-07-04 15:14:04 +0800549 def init_states(self):
550 '''
551 Initializes all states on startup.
552 '''
553 for test in self.test_list.get_all_tests():
554 # Make sure the state server knows about all the tests,
555 # defaulting to an untested state.
556 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800557
Jon Salz0697cbf2012-07-04 15:14:04 +0800558 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800559 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800560 ec_console_log = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800561
Jon Salz0697cbf2012-07-04 15:14:04 +0800562 # Any 'active' tests should be marked as failed now.
563 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800564 if not test.is_leaf():
565 # Don't bother with parents; they will be updated when their
566 # children are updated.
567 continue
568
Jon Salz0697cbf2012-07-04 15:14:04 +0800569 test_state = test.get_state()
570 if test_state.status != TestState.ACTIVE:
571 continue
572 if isinstance(test, factory.ShutdownStep):
573 # Shutdown while the test was active - that's good.
574 self.handle_shutdown_complete(test, test_state)
575 else:
576 # Unexpected shutdown. Grab /var/log/messages for context.
577 if var_log_messages is None:
578 try:
579 var_log_messages = (
580 utils.var_log_messages_before_reboot())
581 # Write it to the log, to make it easier to
582 # correlate with /var/log/messages.
583 logging.info(
584 'Unexpected shutdown. '
585 'Tail of /var/log/messages before last reboot:\n'
586 '%s', ('\n'.join(
587 ' ' + x for x in var_log_messages)))
588 except: # pylint: disable=W0702
589 logging.exception('Unable to grok /var/log/messages')
590 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800591
Jon Salz008f4ea2012-08-28 05:39:45 +0800592 if mosys_log is None and not utils.in_chroot():
593 try:
594 mosys_log = utils.Spawn(
595 ['mosys', 'eventlog', 'list'],
596 read_stdout=True, log_stderr_on_error=True).stdout_data
597 # Write it to the log also.
598 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
599 except: # pylint: disable=W0702
600 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800601
Vic Yange4c275d2012-08-28 01:50:20 +0800602 if ec_console_log is None:
603 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800604 board = system.GetBoard()
605 ec_console_log = board.GetECConsoleLog()
Vic Yange4c275d2012-08-28 01:50:20 +0800606 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Jon Salzfe1f6652012-09-07 05:40:14 +0800607 except: # pylint: disable=W0702
Vic Yange4c275d2012-08-28 01:50:20 +0800608 logging.exception('Error retrieving EC console log')
609
Jon Salz0697cbf2012-07-04 15:14:04 +0800610 error_msg = 'Unexpected shutdown while test was running'
611 self.event_log.Log('end_test',
612 path=test.path,
613 status=TestState.FAILED,
614 invocation=test.get_state().invocation,
615 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800616 var_log_messages='\n'.join(var_log_messages),
617 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800618 test.update_state(
619 status=TestState.FAILED,
620 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800621
Jon Salz50efe942012-07-26 11:54:10 +0800622 if not test.never_fails:
623 # For "never_fails" tests (such as "Start"), don't cancel
624 # pending tests, since reboot is expected.
625 factory.console.info('Unexpected shutdown while test %s '
626 'running; cancelling any pending tests',
627 test.path)
628 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800629
Jon Salz008f4ea2012-08-28 05:39:45 +0800630 self.update_skipped_tests()
631
632 def update_skipped_tests(self):
633 '''
634 Updates skipped states based on run_if.
635 '''
636 for t in self.test_list.walk():
637 if t.is_leaf() and t.run_if_table_name:
638 skip = False
639 try:
640 aux = shopfloor.get_selected_aux_data(t.run_if_table_name)
641 value = aux.get(t.run_if_col)
642 if value is not None:
643 skip = (not value) ^ t.run_if_not
644 except ValueError:
645 # Not available; assume it shouldn't be skipped
646 pass
647
648 test_state = t.get_state()
649 if ((not skip) and
650 (test_state.status == TestState.PASSED) and
651 (test_state.error_msg == TestState.SKIPPED_MSG)):
652 # It was marked as skipped before, but now we need to run it.
653 # Mark as untested.
654 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
655 else:
656 t.update_state(skip=skip)
657
Jon Salz0697cbf2012-07-04 15:14:04 +0800658 def show_next_active_test(self):
659 '''
660 Rotates to the next visible active test.
661 '''
662 self.reap_completed_tests()
663 active_tests = [
664 t for t in self.test_list.walk()
665 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
666 if not active_tests:
667 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800668
Jon Salz0697cbf2012-07-04 15:14:04 +0800669 try:
670 next_test = active_tests[
671 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
672 except ValueError: # visible_test not present in active_tests
673 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800674
Jon Salz0697cbf2012-07-04 15:14:04 +0800675 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800676
Jon Salz0697cbf2012-07-04 15:14:04 +0800677 def handle_event(self, event):
678 '''
679 Handles an event from the event server.
680 '''
681 handler = self.event_handlers.get(event.type)
682 if handler:
683 handler(event)
684 else:
685 # We don't register handlers for all event types - just ignore
686 # this event.
687 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800688
Vic Yangaabf9fd2013-04-09 18:56:13 +0800689 def check_critical_factory_note(self):
690 '''
691 Returns True if the last factory note is critical.
692 '''
693 notes = self.state_instance.get_shared_data('factory_note', True)
694 return notes and notes[-1]['level'] == 'CRITICAL'
695
Jon Salz0697cbf2012-07-04 15:14:04 +0800696 def run_next_test(self):
697 '''
698 Runs the next eligible test (or tests) in self.tests_to_run.
699 '''
700 self.reap_completed_tests()
Vic Yangaabf9fd2013-04-09 18:56:13 +0800701 if self.tests_to_run and self.check_critical_factory_note():
702 self.tests_to_run.clear()
703 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800704 while self.tests_to_run:
705 logging.debug('Tests to run: %s',
706 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800707
Jon Salz0697cbf2012-07-04 15:14:04 +0800708 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800709
Jon Salz0697cbf2012-07-04 15:14:04 +0800710 if test in self.invocations:
711 logging.info('Next test %s is already running', test.path)
712 self.tests_to_run.popleft()
713 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800714
Jon Salza1412922012-07-23 16:04:17 +0800715 for requirement in test.require_run:
716 for i in requirement.test.walk():
717 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800718 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800719 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800720 return
721
Jon Salz0697cbf2012-07-04 15:14:04 +0800722 if self.invocations and not (test.backgroundable and all(
723 [x.backgroundable for x in self.invocations])):
724 logging.debug('Waiting for non-backgroundable tests to '
725 'complete before running %s', test.path)
726 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800727
Jon Salz3e6f5202012-10-15 15:08:29 +0800728 if test.get_state().skip:
729 factory.console.info('Skipping test %s', test.path)
730 test.update_state(status=TestState.PASSED,
731 error_msg=TestState.SKIPPED_MSG)
732 self.tests_to_run.popleft()
733 continue
734
Jon Salz0697cbf2012-07-04 15:14:04 +0800735 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800736
Jon Salz304a75d2012-07-06 11:14:15 +0800737 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800738 for requirement in test.require_run:
739 for i in requirement.test.walk():
740 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800741 # We've hit this test itself; stop checking
742 break
Jon Salza1412922012-07-23 16:04:17 +0800743 if ((i.get_state().status == TestState.UNTESTED) or
744 (requirement.passed and i.get_state().status !=
745 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800746 # Found an untested test; move on to the next
747 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800748 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800749 break
750
751 if untested:
752 untested_paths = ', '.join(sorted([x.path for x in untested]))
753 if self.state_instance.get_shared_data('engineering_mode',
754 optional=True):
755 # In engineering mode, we'll let it go.
756 factory.console.warn('In engineering mode; running '
757 '%s even though required tests '
758 '[%s] have not completed',
759 test.path, untested_paths)
760 else:
761 # Not in engineering mode; mark it failed.
762 error_msg = ('Required tests [%s] have not been run yet'
763 % untested_paths)
764 factory.console.error('Not running %s: %s',
765 test.path, error_msg)
766 test.update_state(status=TestState.FAILED,
767 error_msg=error_msg)
768 continue
769
Jon Salz0697cbf2012-07-04 15:14:04 +0800770 if isinstance(test, factory.ShutdownStep):
771 if os.path.exists(NO_REBOOT_FILE):
772 test.update_state(
773 status=TestState.FAILED, increment_count=1,
774 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800775 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800776 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800777
Jon Salz0697cbf2012-07-04 15:14:04 +0800778 test.update_state(status=TestState.ACTIVE, increment_count=1,
779 error_msg='', shutdown_count=0)
780 if self._prompt_cancel_shutdown(test, 1):
781 self.event_log.Log('reboot_cancelled')
782 test.update_state(
783 status=TestState.FAILED, increment_count=1,
784 error_msg='Shutdown aborted by operator',
785 shutdown_count=0)
chungyiafe8f772012-08-15 19:36:29 +0800786 continue
Jon Salz2f757d42012-06-27 17:06:42 +0800787
Jon Salz0697cbf2012-07-04 15:14:04 +0800788 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800789 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800790 'tests_after_shutdown',
791 [t.path for t in self.tests_to_run])
792 # Save shutdown time
793 self.state_instance.set_shared_data('shutdown_time',
794 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800795
Jon Salz0697cbf2012-07-04 15:14:04 +0800796 with self.env.lock:
797 self.event_log.Log('shutdown', operation=test.operation)
798 shutdown_result = self.env.shutdown(test.operation)
799 if shutdown_result:
800 # That's all, folks!
801 self.run_queue.put(None)
802 return
803 else:
804 # Just pass (e.g., in the chroot).
805 test.update_state(status=TestState.PASSED)
806 self.state_instance.set_shared_data(
807 'tests_after_shutdown', None)
808 # Send event with no fields to indicate that there is no
809 # longer a pending shutdown.
810 self.event_client.post_event(Event(
811 Event.Type.PENDING_SHUTDOWN))
812 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800813
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800814 self._run_test(test, test.iterations, test.retries)
Jon Salz1acc8742012-07-17 17:45:55 +0800815
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800816 def _run_test(self, test, iterations_left=None, retries_left=None):
Jon Salz1acc8742012-07-17 17:45:55 +0800817 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
818 new_state = test.update_state(
819 status=TestState.ACTIVE, increment_count=1, error_msg='',
Jon Salzbd42ce12012-09-18 08:03:59 +0800820 invocation=invoc.uuid, iterations_left=iterations_left,
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800821 retries_left=retries_left,
Jon Salzbd42ce12012-09-18 08:03:59 +0800822 visible=(self.visible_test == test))
Jon Salz1acc8742012-07-17 17:45:55 +0800823 invoc.count = new_state.count
824
825 self.invocations[test] = invoc
826 if self.visible_test is None and test.has_ui:
827 self.set_visible_test(test)
Vic Yang311ddb82012-09-26 12:08:28 +0800828 self.check_exclusive()
Jon Salz1acc8742012-07-17 17:45:55 +0800829 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800830
Vic Yang311ddb82012-09-26 12:08:28 +0800831 def check_exclusive(self):
Jon Salzce6a7f82013-06-10 18:22:54 +0800832 # alias since this is really long
833 EXCL_OPT = factory.FactoryTest.EXCLUSIVE_OPTIONS
834
Vic Yang311ddb82012-09-26 12:08:28 +0800835 current_exclusive_items = set([
Jon Salzce6a7f82013-06-10 18:22:54 +0800836 item for item in EXCL_OPT
Vic Yang311ddb82012-09-26 12:08:28 +0800837 if any([test.is_exclusive(item) for test in self.invocations])])
838
839 new_exclusive_items = current_exclusive_items - self.exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800840 if EXCL_OPT.NETWORKING in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800841 logging.info('Disabling network')
842 self.connection_manager.DisableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800843 if EXCL_OPT.CHARGER in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800844 logging.info('Stop controlling charger')
845
846 new_non_exclusive_items = self.exclusive_items - current_exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800847 if EXCL_OPT.NETWORKING in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800848 logging.info('Re-enabling network')
849 self.connection_manager.EnableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800850 if EXCL_OPT.CHARGER in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800851 logging.info('Start controlling charger')
852
Jon Salzce6a7f82013-06-10 18:22:54 +0800853 if self.cpufreq_manager:
854 enabled = EXCL_OPT.CPUFREQ not in current_exclusive_items
855 try:
856 self.cpufreq_manager.SetEnabled(enabled)
857 except: # pylint: disable=W0702
858 logging.exception('Unable to %s cpufreq services',
859 'enable' if enabled else 'disable')
860
Vic Yang311ddb82012-09-26 12:08:28 +0800861 # Only adjust charge state if not excluded
Jon Salzce6a7f82013-06-10 18:22:54 +0800862 if (EXCL_OPT.CHARGER not in current_exclusive_items and
863 not utils.in_chroot()):
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +0800864 if self.charge_manager:
865 self.charge_manager.AdjustChargeState()
866 else:
867 try:
868 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
869 except BoardException:
870 logging.exception('Unable to set charge state on this board')
Vic Yang311ddb82012-09-26 12:08:28 +0800871
872 self.exclusive_items = current_exclusive_items
Jon Salz5da61e62012-05-31 13:06:22 +0800873
cychiang21886742012-07-05 15:16:32 +0800874 def check_for_updates(self):
875 '''
876 Schedules an asynchronous check for updates if necessary.
877 '''
878 if not self.test_list.options.update_period_secs:
879 # Not enabled.
880 return
881
882 now = time.time()
883 if self.last_update_check and (
884 now - self.last_update_check <
885 self.test_list.options.update_period_secs):
886 # Not yet time for another check.
887 return
888
889 self.last_update_check = now
890
891 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
892 if reached_shopfloor:
893 new_update_md5sum = md5sum if needs_update else None
894 if system.SystemInfo.update_md5sum != new_update_md5sum:
895 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
896 system.SystemInfo.update_md5sum = new_update_md5sum
897 self.run_queue.put(self.update_system_info)
898
899 updater.CheckForUpdateAsync(
900 handle_check_for_update,
901 self.test_list.options.shopfloor_timeout_secs)
902
Jon Salza6711d72012-07-18 14:33:03 +0800903 def cancel_pending_tests(self):
904 '''Cancels any tests in the run queue.'''
905 self.run_tests([])
906
Jon Salz0697cbf2012-07-04 15:14:04 +0800907 def run_tests(self, subtrees, untested_only=False):
908 '''
909 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800910
Jon Salz0697cbf2012-07-04 15:14:04 +0800911 The tests are run in order unless one fails (then stops).
912 Backgroundable tests are run simultaneously; when a foreground test is
913 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800914
Jon Salz0697cbf2012-07-04 15:14:04 +0800915 @param subtrees: Node or nodes containing tests to run (may either be
916 a single test or a list). Duplicates will be ignored.
917 '''
918 if type(subtrees) != list:
919 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800920
Jon Salz0697cbf2012-07-04 15:14:04 +0800921 # Nodes we've seen so far, to avoid duplicates.
922 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800923
Jon Salz0697cbf2012-07-04 15:14:04 +0800924 self.tests_to_run = deque()
925 for subtree in subtrees:
926 for test in subtree.walk():
927 if test in seen:
928 continue
929 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800930
Jon Salz0697cbf2012-07-04 15:14:04 +0800931 if not test.is_leaf():
932 continue
933 if (untested_only and
934 test.get_state().status != TestState.UNTESTED):
935 continue
936 self.tests_to_run.append(test)
937 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800938
Jon Salz0697cbf2012-07-04 15:14:04 +0800939 def reap_completed_tests(self):
940 '''
941 Removes completed tests from the set of active tests.
942
943 Also updates the visible test if it was reaped.
944 '''
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800945 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800946 for t, v in dict(self.invocations).iteritems():
947 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800948 test_completed = True
Jon Salz1acc8742012-07-17 17:45:55 +0800949 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800950 del self.invocations[t]
951
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800952 # Stop on failure if flag is true.
953 if (self.test_list.options.stop_on_failure and
954 new_state.status == TestState.FAILED):
955 # Clean all the tests to cause goofy to stop.
956 self.tests_to_run = []
957 factory.console.info("Stop on failure triggered. Empty the queue.")
958
Jon Salz1acc8742012-07-17 17:45:55 +0800959 if new_state.iterations_left and new_state.status == TestState.PASSED:
960 # Play it again, Sam!
961 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800962 # new_state.retries_left is obtained after update.
963 # For retries_left == 0, test can still be run for the last time.
964 elif (new_state.retries_left >= 0 and
965 new_state.status == TestState.FAILED):
966 # Still have to retry, Sam!
967 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +0800968
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800969 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800970 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800971
Jon Salz0697cbf2012-07-04 15:14:04 +0800972 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800973 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800974 self.set_visible_test(None)
975 # Make the first running test, if any, the visible test
976 for t in self.test_list.walk():
977 if t in self.invocations:
978 self.set_visible_test(t)
979 break
980
Jon Salz6dc031d2013-06-19 13:06:23 +0800981 def kill_active_tests(self, abort, root=None, reason=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800982 '''
983 Kills and waits for all active tests.
984
Jon Salz85a39882012-07-05 16:45:04 +0800985 Args:
986 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800987 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800988 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800989 '''
990 self.reap_completed_tests()
991 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800992 if root and not test.has_ancestor(root):
993 continue
994
Jon Salz0697cbf2012-07-04 15:14:04 +0800995 factory.console.info('Killing active test %s...' % test.path)
Jon Salz6dc031d2013-06-19 13:06:23 +0800996 invoc.abort_and_join(reason)
Jon Salz0697cbf2012-07-04 15:14:04 +0800997 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800998 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800999 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +08001000
Jon Salz0697cbf2012-07-04 15:14:04 +08001001 if not abort:
1002 test.update_state(status=TestState.UNTESTED)
1003 self.reap_completed_tests()
1004
Jon Salz6dc031d2013-06-19 13:06:23 +08001005 def stop(self, root=None, fail=False, reason=None):
1006 self.kill_active_tests(fail, root, reason)
Jon Salz85a39882012-07-05 16:45:04 +08001007 # Remove any tests in the run queue under the root.
1008 self.tests_to_run = deque([x for x in self.tests_to_run
1009 if root and not x.has_ancestor(root)])
1010 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +08001011
Jon Salz4712ac72013-02-07 17:12:05 +08001012 def clear_state(self, root=None):
Jon Salz6dc031d2013-06-19 13:06:23 +08001013 self.stop(root, reason='Clearing test state')
Jon Salz4712ac72013-02-07 17:12:05 +08001014 for f in root.walk():
1015 if f.is_leaf():
1016 f.update_state(status=TestState.UNTESTED)
1017
Jon Salz6dc031d2013-06-19 13:06:23 +08001018 def abort_active_tests(self, reason=None):
1019 self.kill_active_tests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +08001020
1021 def main(self):
Jon Salzeff94182013-06-19 15:06:28 +08001022 syslog.openlog('goofy')
1023
Jon Salz0697cbf2012-07-04 15:14:04 +08001024 try:
1025 self.init()
1026 self.event_log.Log('goofy_init',
1027 success=True)
1028 except:
1029 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001030 try:
Jon Salz0697cbf2012-07-04 15:14:04 +08001031 self.event_log.Log('goofy_init',
1032 success=False,
1033 trace=traceback.format_exc())
1034 except: # pylint: disable=W0702
1035 pass
1036 raise
1037
Jon Salzeff94182013-06-19 15:06:28 +08001038 syslog.syslog('Goofy (factory test harness) starting')
Jon Salz0697cbf2012-07-04 15:14:04 +08001039 self.run()
1040
1041 def update_system_info(self):
1042 '''Updates system info.'''
1043 system_info = system.SystemInfo()
1044 self.state_instance.set_shared_data('system_info', system_info.__dict__)
1045 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
1046 system_info=system_info.__dict__))
1047 logging.info('System info: %r', system_info.__dict__)
1048
Jon Salzeb42f0d2012-07-27 19:14:04 +08001049 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
1050 '''Commences updating factory software.
1051
1052 Args:
1053 auto_run_on_restart: Auto-run when the machine comes back up.
1054 post_update_hook: Code to call after update but immediately before
1055 restart.
1056
1057 Returns:
1058 Never if the update was successful (we just reboot).
1059 False if the update was unnecessary (no update available).
1060 '''
Jon Salz6dc031d2013-06-19 13:06:23 +08001061 self.kill_active_tests(False, reason='Factory software update')
Jon Salza6711d72012-07-18 14:33:03 +08001062 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001063
Jon Salz5c344f62012-07-13 14:31:16 +08001064 def pre_update_hook():
1065 if auto_run_on_restart:
1066 self.state_instance.set_shared_data('tests_after_shutdown',
1067 FORCE_AUTO_RUN)
1068 self.state_instance.close()
1069
Jon Salzeb42f0d2012-07-27 19:14:04 +08001070 if updater.TryUpdate(pre_update_hook=pre_update_hook):
1071 if post_update_hook:
1072 post_update_hook()
1073 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +08001074
Jon Salzcef132a2012-08-30 04:58:08 +08001075 def handle_sigint(self, dummy_signum, dummy_frame):
Jon Salz77c151e2012-08-28 07:20:37 +08001076 logging.error('Received SIGINT')
1077 self.run_queue.put(None)
1078 raise KeyboardInterrupt()
1079
Jon Salz0697cbf2012-07-04 15:14:04 +08001080 def init(self, args=None, env=None):
1081 '''Initializes Goofy.
1082
1083 Args:
1084 args: A list of command-line arguments. Uses sys.argv if
1085 args is None.
1086 env: An Environment instance to use (or None to choose
1087 FakeChrootEnvironment or DUTEnvironment as appropriate).
1088 '''
Jon Salz77c151e2012-08-28 07:20:37 +08001089 signal.signal(signal.SIGINT, self.handle_sigint)
1090
Jon Salz0697cbf2012-07-04 15:14:04 +08001091 parser = OptionParser()
1092 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001093 action='store_true',
1094 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001095 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001096 metavar='FILE',
1097 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001098 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001099 action='store_true',
1100 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001101 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +08001102 choices=['none', 'gtk', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001103 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001104 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001105 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001106 type='int', default=1,
1107 help=('Factor by which to scale UI '
1108 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001109 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001110 metavar='FILE',
1111 help='Use FILE as test list')
Jon Salzc79a9982012-08-30 04:42:01 +08001112 parser.add_option('--dummy_shopfloor', action='store_true',
1113 help='Use a dummy shopfloor server')
chungyiafe8f772012-08-15 19:36:29 +08001114 parser.add_option('--automation', dest='automation',
1115 action='store_true',
1116 help='Enable automation on running factory test')
Ricky Liang09216dc2013-02-22 17:26:45 +08001117 parser.add_option('--one_pixel_less', dest='one_pixel_less',
1118 action='store_true',
1119 help=('Start Chrome one pixel less than the full screen.'
1120 'Needed by Exynos platform to run GTK.'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001121 (self.options, self.args) = parser.parse_args(args)
1122
Jon Salz46b89562012-07-05 11:49:22 +08001123 # Make sure factory directories exist.
1124 factory.get_log_root()
1125 factory.get_state_root()
1126 factory.get_test_data_root()
1127
Jon Salz0697cbf2012-07-04 15:14:04 +08001128 global _inited_logging # pylint: disable=W0603
1129 if not _inited_logging:
1130 factory.init_logging('goofy', verbose=self.options.verbose)
1131 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001132
Jon Salz0f996602012-10-03 15:26:48 +08001133 if self.options.print_test_list:
1134 print factory.read_test_list(
1135 self.options.print_test_list).__repr__(recursive=True)
1136 sys.exit(0)
1137
Jon Salzee85d522012-07-17 14:34:46 +08001138 event_log.IncrementBootSequence()
Jon Salzd15bbcf2013-05-21 17:33:57 +08001139 # Don't defer logging the initial event, so we can make sure
1140 # that device_id, reimage_id, etc. are all set up.
1141 self.event_log = EventLog('goofy', defer=False)
Jon Salz0697cbf2012-07-04 15:14:04 +08001142
1143 if (not suppress_chroot_warning and
1144 factory.in_chroot() and
1145 self.options.ui == 'gtk' and
1146 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
1147 # That's not going to work! Tell the user how to run
1148 # this way.
1149 logging.warn(GOOFY_IN_CHROOT_WARNING)
1150 time.sleep(1)
1151
1152 if env:
1153 self.env = env
1154 elif factory.in_chroot():
1155 self.env = test_environment.FakeChrootEnvironment()
1156 logging.warn(
1157 'Using chroot environment: will not actually run autotests')
1158 else:
1159 self.env = test_environment.DUTEnvironment()
1160 self.env.goofy = self
1161
1162 if self.options.restart:
1163 state.clear_state()
1164
Jon Salz0697cbf2012-07-04 15:14:04 +08001165 if self.options.ui_scale_factor != 1 and utils.in_qemu():
1166 logging.warn(
1167 'In QEMU; ignoring ui_scale_factor argument')
1168 self.options.ui_scale_factor = 1
1169
1170 logging.info('Started')
1171
1172 self.start_state_server()
1173 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1174 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001175 self.options.ui_scale_factor)
1176 self.state_instance.set_shared_data('one_pixel_less',
1177 self.options.one_pixel_less)
Jon Salz0697cbf2012-07-04 15:14:04 +08001178 self.last_shutdown_time = (
1179 self.state_instance.get_shared_data('shutdown_time', optional=True))
1180 self.state_instance.del_shared_data('shutdown_time', optional=True)
1181
Jon Salzb19ea072013-02-07 16:35:00 +08001182 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001183 if not self.options.test_list:
1184 self.options.test_list = find_test_list()
Jon Salzb19ea072013-02-07 16:35:00 +08001185 if self.options.test_list:
Jon Salz0697cbf2012-07-04 15:14:04 +08001186 logging.info('Using test list %s', self.options.test_list)
Jon Salzb19ea072013-02-07 16:35:00 +08001187 try:
1188 self.test_list = factory.read_test_list(
1189 self.options.test_list,
1190 self.state_instance)
1191 except: # pylint: disable=W0702
1192 logging.exception('Unable to read test list %r', self.options.test_list)
1193 self.state_instance.set_shared_data('startup_error',
1194 'Unable to read test list %s\n%s' % (
1195 self.options.test_list,
1196 traceback.format_exc()))
1197 else:
1198 logging.error('No test list found.')
1199 self.state_instance.set_shared_data('startup_error',
1200 'No test list found.')
Jon Salz0697cbf2012-07-04 15:14:04 +08001201
Jon Salzb19ea072013-02-07 16:35:00 +08001202 if not self.test_list:
1203 if self.options.ui == 'chrome':
1204 # Create an empty test list with default options so that the rest of
1205 # startup can proceed.
1206 self.test_list = factory.FactoryTestList(
1207 [], self.state_instance, factory.Options())
1208 else:
1209 # Bail with an error; no point in starting up.
1210 sys.exit('No valid test list; exiting.')
1211
Jon Salz822838b2013-03-25 17:32:33 +08001212 if self.test_list.options.clear_state_on_start:
1213 self.state_instance.clear_test_state()
1214
Vic Yang3e1cf5d2013-06-05 18:50:24 +08001215 if system.SystemInfo().firmware_version is None and not utils.in_chroot():
Vic Yang9bd4f772013-06-04 17:34:00 +08001216 self.state_instance.set_shared_data('startup_error',
1217 'Netboot firmware detected\n'
1218 'Connect Ethernet and reboot to re-image.\n'
1219 u'侦测到网路开机固件\n'
1220 u'请连接乙太网并重启')
1221
Jon Salz0697cbf2012-07-04 15:14:04 +08001222 if not self.state_instance.has_shared_data('ui_lang'):
1223 self.state_instance.set_shared_data('ui_lang',
1224 self.test_list.options.ui_lang)
1225 self.state_instance.set_shared_data(
1226 'test_list_options',
1227 self.test_list.options.__dict__)
1228 self.state_instance.test_list = self.test_list
1229
Jon Salz83ef34b2012-11-01 19:46:35 +08001230 if not utils.in_chroot() and self.test_list.options.disable_log_rotation:
1231 open('/var/lib/cleanup_logs_paused', 'w').close()
1232
Jon Salz23926422012-09-01 03:38:13 +08001233 if self.options.dummy_shopfloor:
1234 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1235 'http://localhost:%d/' % shopfloor.DEFAULT_SERVER_PORT)
1236 self.dummy_shopfloor = Spawn(
1237 [os.path.join(factory.FACTORY_PATH, 'bin', 'shopfloor_server'),
1238 '--dummy'])
1239 elif self.test_list.options.shopfloor_server_url:
1240 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001241 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001242
Jon Salz0f996602012-10-03 15:26:48 +08001243 if self.test_list.options.time_sanitizer and not utils.in_chroot():
Jon Salz8fa8e832012-07-13 19:04:09 +08001244 self.time_sanitizer = time_sanitizer.TimeSanitizer(
1245 base_time=time_sanitizer.GetBaseTimeFromFile(
1246 # lsb-factory is written by the factory install shim during
1247 # installation, so it should have a good time obtained from
Jon Salz54882d02012-08-31 01:57:54 +08001248 # the mini-Omaha server. If it's not available, we'll use
1249 # /etc/lsb-factory (which will be much older, but reasonably
1250 # sane) and rely on a shopfloor sync to set a more accurate
1251 # time.
1252 '/usr/local/etc/lsb-factory',
1253 '/etc/lsb-release'))
Jon Salz8fa8e832012-07-13 19:04:09 +08001254 self.time_sanitizer.RunOnce()
1255
Jon Salz0697cbf2012-07-04 15:14:04 +08001256 self.init_states()
1257 self.start_event_server()
1258 self.connection_manager = self.env.create_connection_manager(
Tai-Hsu Lin371351a2012-08-27 14:17:14 +08001259 self.test_list.options.wlans,
1260 self.test_list.options.scan_wifi_period_secs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001261 # Note that we create a log watcher even if
1262 # sync_event_log_period_secs isn't set (no background
1263 # syncing), since we may use it to flush event logs as well.
1264 self.log_watcher = EventLogWatcher(
1265 self.test_list.options.sync_event_log_period_secs,
Jon Salzd15bbcf2013-05-21 17:33:57 +08001266 event_log_db_file=None,
Jon Salz16d10542012-07-23 12:18:45 +08001267 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001268 if self.test_list.options.sync_event_log_period_secs:
1269 self.log_watcher.StartWatchThread()
1270
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001271 # Note that we create a system log manager even if
1272 # sync_log_period_secs isn't set (no background
1273 # syncing), since we may kick it to sync logs in its
1274 # thread.
1275 self.system_log_manager = SystemLogManager(
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +08001276 sync_log_paths=self.test_list.options.sync_log_paths,
1277 sync_period_sec=self.test_list.options.sync_log_period_secs,
1278 clear_log_paths=self.test_list.options.clear_log_paths)
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001279 self.system_log_manager.StartSyncThread()
1280
Jon Salz0697cbf2012-07-04 15:14:04 +08001281 self.update_system_info()
1282
Vic Yang4953fc12012-07-26 16:19:53 +08001283 assert ((self.test_list.options.min_charge_pct is None) ==
1284 (self.test_list.options.max_charge_pct is None))
Vic Yange83d9a12013-04-19 20:00:20 +08001285 if utils.in_chroot():
1286 logging.info('In chroot, ignoring charge manager and charge state')
1287 elif self.test_list.options.min_charge_pct is not None:
Vic Yang4953fc12012-07-26 16:19:53 +08001288 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1289 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001290 system.SystemStatus.charge_manager = self.charge_manager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +08001291 else:
1292 # Goofy should set charger state to charge if charge_manager is disabled.
1293 try:
1294 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
1295 except BoardException:
1296 logging.exception('Unable to set charge state on this board')
Vic Yang4953fc12012-07-26 16:19:53 +08001297
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001298 self.core_dump_manager = CoreDumpManager(
1299 self.test_list.options.core_dump_watchlist)
1300
Jon Salz0697cbf2012-07-04 15:14:04 +08001301 os.environ['CROS_FACTORY'] = '1'
1302 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1303
1304 # Set CROS_UI since some behaviors in ui.py depend on the
1305 # particular UI in use. TODO(jsalz): Remove this (and all
1306 # places it is used) when the GTK UI is removed.
1307 os.environ['CROS_UI'] = self.options.ui
1308
Jon Salz416f9cc2013-05-10 18:32:50 +08001309 # Initialize hooks.
1310 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1311 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
1312 assert isinstance(self.hooks, factory.Hooks), (
1313 "hooks should be of type Hooks but is %r" % type(self.hooks))
1314 self.hooks.test_list = self.test_list
1315
Jon Salzce6a7f82013-06-10 18:22:54 +08001316 if not utils.in_chroot():
Jon Salzddf0d052013-06-18 12:52:44 +08001317 self.cpufreq_manager = CpufreqManager(event_log=self.event_log)
Jon Salzce6a7f82013-06-10 18:22:54 +08001318
Jon Salz416f9cc2013-05-10 18:32:50 +08001319 # Call startup hook.
1320 self.hooks.OnStartup()
1321
Jon Salz0697cbf2012-07-04 15:14:04 +08001322 if self.options.ui == 'chrome':
1323 self.env.launch_chrome()
1324 logging.info('Waiting for a web socket connection')
Cheng-Yi Chiangfd8ed392013-03-08 21:37:31 +08001325 self.web_socket_manager.wait()
Jon Salz0697cbf2012-07-04 15:14:04 +08001326
1327 # Wait for the test widget size to be set; this is done in
1328 # an asynchronous RPC so there is a small chance that the
1329 # web socket might be opened first.
1330 for _ in range(100): # 10 s
1331 try:
1332 if self.state_instance.get_shared_data('test_widget_size'):
1333 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001334 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001335 pass # Retry
1336 time.sleep(0.1) # 100 ms
1337 else:
1338 logging.warn('Never received test_widget_size from UI')
Jon Salz45297282013-05-18 14:31:47 +08001339
1340 # Send Chrome a Tab to get focus to the factory UI
1341 # (http://crosbug.com/p/19444). TODO(jsalz): remove this hack
1342 # and figure out the right way to get the focus to Chrome.
1343 if not utils.in_chroot():
1344 Spawn(
1345 [os.path.join(factory.FACTORY_PATH, 'bin', 'send_key'), 'Tab'],
1346 check_call=True, log=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001347 elif self.options.ui == 'gtk':
1348 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001349
Ricky Liang650f6bf2012-09-28 13:22:54 +08001350 # Create download path for autotest beforehand or autotests run at
1351 # the same time might fail due to race condition.
1352 if not factory.in_chroot():
1353 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1354 'download'))
1355
Jon Salz0697cbf2012-07-04 15:14:04 +08001356 def state_change_callback(test, test_state):
1357 self.event_client.post_event(
1358 Event(Event.Type.STATE_CHANGE,
1359 path=test.path, state=test_state))
1360 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001361
Jon Salza6711d72012-07-18 14:33:03 +08001362 for handler in self.on_ui_startup:
1363 handler()
1364
1365 self.prespawner = Prespawner()
1366 self.prespawner.start()
1367
Jon Salz0697cbf2012-07-04 15:14:04 +08001368 try:
1369 tests_after_shutdown = self.state_instance.get_shared_data(
1370 'tests_after_shutdown')
1371 except KeyError:
1372 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001373
Jon Salz5c344f62012-07-13 14:31:16 +08001374 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1375 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001376 logging.info('Resuming tests after shutdown: %s',
1377 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001378 self.tests_to_run.extend(
1379 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1380 self.run_queue.put(self.run_next_test)
1381 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001382 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001383 self.run_queue.put(
1384 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001385 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001386
Dean Liao592e4d52013-01-10 20:06:39 +08001387 self.may_disable_cros_shortcut_keys()
1388
1389 def may_disable_cros_shortcut_keys(self):
1390 test_options = self.test_list.options
1391 if test_options.disable_cros_shortcut_keys:
1392 logging.info('Filter ChromeOS shortcut keys.')
1393 self.key_filter = KeyFilter(
1394 unmap_caps_lock=test_options.disable_caps_lock,
1395 caps_lock_keycode=test_options.caps_lock_keycode)
1396 self.key_filter.Start()
1397
Jon Salz0697cbf2012-07-04 15:14:04 +08001398 def run(self):
1399 '''Runs Goofy.'''
1400 # Process events forever.
1401 while self.run_once(True):
1402 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001403
Jon Salz0697cbf2012-07-04 15:14:04 +08001404 def run_once(self, block=False):
1405 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001406
Jon Salz0697cbf2012-07-04 15:14:04 +08001407 Args:
1408 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001409
Jon Salz0697cbf2012-07-04 15:14:04 +08001410 Returns:
1411 True to keep going or False to shut down.
1412 '''
1413 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001414 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001415 # Nothing on the run queue.
1416 self._run_queue_idle()
1417 if block:
1418 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001419 try:
1420 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1421 except Queue.Empty:
1422 # Keep going (calling _run_queue_idle() again at the top of
1423 # the loop)
1424 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001425 # ...and grab anything else that showed up at the same
1426 # time.
1427 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001428 else:
1429 break
Jon Salz51528e12012-07-02 18:54:45 +08001430
Jon Salz0697cbf2012-07-04 15:14:04 +08001431 for event in events:
1432 if not event:
1433 # Shutdown request.
1434 self.run_queue.task_done()
1435 return False
Jon Salz51528e12012-07-02 18:54:45 +08001436
Jon Salz0697cbf2012-07-04 15:14:04 +08001437 try:
1438 event()
Jon Salz85a39882012-07-05 16:45:04 +08001439 except: # pylint: disable=W0702
1440 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001441 self.record_exception(traceback.format_exception_only(
1442 *sys.exc_info()[:2]))
1443 # But keep going
1444 finally:
1445 self.run_queue.task_done()
1446 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001447
Jon Salz0e6532d2012-10-25 16:30:11 +08001448 def _should_sync_time(self, foreground=False):
1449 '''Returns True if we should attempt syncing time with shopfloor.
1450
1451 Args:
1452 foreground: If True, synchronizes even if background syncing
1453 is disabled (e.g., in explicit sync requests from the
1454 SyncShopfloor test).
1455 '''
1456 return ((foreground or
1457 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001458 self.time_sanitizer and
1459 (not self.time_synced) and
1460 (not factory.in_chroot()))
1461
Jon Salz0e6532d2012-10-25 16:30:11 +08001462 def sync_time_with_shopfloor_server(self, foreground=False):
Jon Salz54882d02012-08-31 01:57:54 +08001463 '''Syncs time with shopfloor server, if not yet synced.
1464
Jon Salz0e6532d2012-10-25 16:30:11 +08001465 Args:
1466 foreground: If True, synchronizes even if background syncing
1467 is disabled (e.g., in explicit sync requests from the
1468 SyncShopfloor test).
1469
Jon Salz54882d02012-08-31 01:57:54 +08001470 Returns:
1471 False if no time sanitizer is available, or True if this sync (or a
1472 previous sync) succeeded.
1473
1474 Raises:
1475 Exception if unable to contact the shopfloor server.
1476 '''
Jon Salz0e6532d2012-10-25 16:30:11 +08001477 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001478 self.time_sanitizer.SyncWithShopfloor()
1479 self.time_synced = True
1480 return self.time_synced
1481
Jon Salzb92c5112012-09-21 15:40:11 +08001482 def log_disk_space_stats(self):
Jon Salz18e0e022013-06-11 17:13:39 +08001483 if (utils.in_chroot() or
1484 not self.test_list.options.log_disk_space_period_secs):
Jon Salzb92c5112012-09-21 15:40:11 +08001485 return
1486
1487 now = time.time()
1488 if (self.last_log_disk_space_time and
1489 now - self.last_log_disk_space_time <
1490 self.test_list.options.log_disk_space_period_secs):
1491 return
1492 self.last_log_disk_space_time = now
1493
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001494 # Upload event if stateful partition usage is above threshold.
1495 # Stateful partition is mounted on /usr/local, while
1496 # encrypted stateful partition is mounted on /var.
1497 # If there are too much logs in the factory process,
1498 # these two partitions might get full.
Jon Salzb92c5112012-09-21 15:40:11 +08001499 try:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001500 vfs_infos = disk_space.GetAllVFSInfo()
1501 stateful_info, encrypted_info = None, None
1502 for vfs_info in vfs_infos.values():
1503 if '/usr/local' in vfs_info.mount_points:
1504 stateful_info = vfs_info
1505 if '/var' in vfs_info.mount_points:
1506 encrypted_info = vfs_info
1507
1508 stateful = disk_space.GetPartitionUsage(stateful_info)
1509 encrypted = disk_space.GetPartitionUsage(encrypted_info)
1510
1511 above_threshold = (
1512 self.test_list.options.stateful_usage_threshold and
1513 max(stateful.bytes_used_pct,
1514 stateful.inodes_used_pct,
1515 encrypted.bytes_used_pct,
1516 encrypted.inodes_used_pct) >
1517 self.test_list.options.stateful_usage_threshold)
1518
1519 if above_threshold:
1520 self.event_log.Log('stateful_partition_usage',
1521 partitions={
1522 'stateful': {
1523 'bytes_used_pct': FloatDigit(stateful.bytes_used_pct, 2),
1524 'inodes_used_pct': FloatDigit(stateful.inodes_used_pct, 2)},
1525 'encrypted_stateful': {
1526 'bytes_used_pct': FloatDigit(encrypted.bytes_used_pct, 2),
1527 'inodes_used_pct': FloatDigit(encrypted.inodes_used_pct, 2)}
1528 })
1529 self.log_watcher.ScanEventLogs()
1530
1531 message = disk_space.FormatSpaceUsedAll(vfs_infos)
Jon Salz3c493bb2013-02-07 17:24:58 +08001532 if message != self.last_log_disk_space_message:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001533 if above_threshold:
1534 logging.warning(message)
1535 else:
1536 logging.info(message)
Jon Salz3c493bb2013-02-07 17:24:58 +08001537 self.last_log_disk_space_message = message
Jon Salzb92c5112012-09-21 15:40:11 +08001538 except: # pylint: disable=W0702
1539 logging.exception('Unable to get disk space used')
1540
Justin Chuang83813982013-05-13 01:26:32 +08001541 def check_battery(self):
1542 '''Checks the current battery status.
1543
1544 Logs current battery charging level and status to log. If the battery level
1545 is lower below warning_low_battery_pct, send warning event to shopfloor.
1546 If the battery level is lower below critical_low_battery_pct, flush disks.
1547 '''
1548 if not self.test_list.options.check_battery_period_secs:
1549 return
1550
1551 now = time.time()
1552 if (self.last_check_battery_time and
1553 now - self.last_check_battery_time <
1554 self.test_list.options.check_battery_period_secs):
1555 return
1556 self.last_check_battery_time = now
1557
1558 message = ''
1559 log_level = logging.INFO
1560 try:
1561 power = system.GetBoard().power
1562 if not power.CheckBatteryPresent():
1563 message = 'Battery is not present'
1564 else:
1565 ac_present = power.CheckACPresent()
1566 charge_pct = power.GetChargePct(get_float=True)
1567 message = ('Current battery level %.1f%%, AC charger is %s' %
1568 (charge_pct, 'connected' if ac_present else 'disconnected'))
1569
1570 if charge_pct > self.test_list.options.critical_low_battery_pct:
1571 critical_low_battery = False
1572 else:
1573 critical_low_battery = True
1574 # Only sync disks when battery level is still above minimum
1575 # value. This can be used for offline analysis when shopfloor cannot
1576 # be connected.
1577 if charge_pct > MIN_BATTERY_LEVEL_FOR_DISK_SYNC:
1578 logging.warning('disk syncing for critical low battery situation')
1579 os.system('sync; sync; sync')
1580 else:
1581 logging.warning('disk syncing is cancelled '
1582 'because battery level is lower than %.1f',
1583 MIN_BATTERY_LEVEL_FOR_DISK_SYNC)
1584
1585 # Notify shopfloor server
1586 if (critical_low_battery or
1587 (not ac_present and
1588 charge_pct <= self.test_list.options.warning_low_battery_pct)):
1589 log_level = logging.WARNING
1590
1591 self.event_log.Log('low_battery',
1592 battery_level=charge_pct,
1593 charger_connected=ac_present,
1594 critical=critical_low_battery)
1595 self.log_watcher.KickWatchThread()
1596 self.system_log_manager.KickSyncThread()
1597 except: # pylint: disable=W0702
1598 logging.exception('Unable to check battery or notify shopfloor')
1599 finally:
1600 if message != self.last_check_battery_message:
1601 logging.log(log_level, message)
1602 self.last_check_battery_message = message
1603
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001604 def check_core_dump(self):
1605 '''Checks if there is any core dumped file.
1606
1607 Removes unwanted core dump files immediately.
1608 Syncs those files matching watch list to server with a delay between
1609 each sync. After the files have been synced to server, deletes the files.
1610 '''
1611 core_dump_files = self.core_dump_manager.ScanFiles()
1612 if core_dump_files:
1613 now = time.time()
1614 if (self.last_kick_sync_time and now - self.last_kick_sync_time <
1615 self.test_list.options.kick_sync_min_interval_secs):
1616 return
1617 self.last_kick_sync_time = now
1618
1619 # Sends event to server
1620 self.event_log.Log('core_dumped', files=core_dump_files)
1621 self.log_watcher.KickWatchThread()
1622
1623 # Syncs files to server
1624 self.system_log_manager.KickSyncThread(
1625 core_dump_files, self.core_dump_manager.ClearFiles)
1626
Jon Salz8fa8e832012-07-13 19:04:09 +08001627 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001628 '''Writes out current time and tries to sync with shopfloor server.'''
1629 if not self.time_sanitizer:
1630 return
1631
1632 # Write out the current time.
1633 self.time_sanitizer.SaveTime()
1634
Jon Salz54882d02012-08-31 01:57:54 +08001635 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001636 return
1637
1638 now = time.time()
1639 if self.last_sync_time and (
1640 now - self.last_sync_time <
1641 self.test_list.options.sync_time_period_secs):
1642 # Not yet time for another check.
1643 return
1644 self.last_sync_time = now
1645
1646 def target():
1647 try:
Jon Salz54882d02012-08-31 01:57:54 +08001648 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001649 except: # pylint: disable=W0702
1650 # Oh well. Log an error (but no trace)
1651 logging.info(
1652 'Unable to get time from shopfloor server: %s',
1653 utils.FormatExceptionOnly())
1654
1655 thread = threading.Thread(target=target)
1656 thread.daemon = True
1657 thread.start()
1658
Jon Salz0697cbf2012-07-04 15:14:04 +08001659 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001660 '''Invoked when the run queue has no events.
1661
1662 This method must not raise exception.
1663 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001664 now = time.time()
1665 if (self.last_idle and
1666 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1667 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1668 # 1) seconds.
1669 return
1670
1671 self.last_idle = now
1672
Vic Yang311ddb82012-09-26 12:08:28 +08001673 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001674 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001675 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001676 self.log_disk_space_stats()
Justin Chuang83813982013-05-13 01:26:32 +08001677 self.check_battery()
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001678 self.check_core_dump()
Jon Salz57717ca2012-04-04 16:47:25 +08001679
Jon Salzd15bbcf2013-05-21 17:33:57 +08001680 def handle_event_logs(self, chunks):
Jon Salz0697cbf2012-07-04 15:14:04 +08001681 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001682
Jon Salz0697cbf2012-07-04 15:14:04 +08001683 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001684
1685 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001686 chunks: A list of Chunk objects.
Jon Salz0697cbf2012-07-04 15:14:04 +08001687 '''
Vic Yang93027612013-05-06 02:42:49 +08001688 first_exception = None
1689 exception_count = 0
1690
Jon Salzd15bbcf2013-05-21 17:33:57 +08001691 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001692 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001693 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001694 start_time = time.time()
1695 shopfloor_client = shopfloor.get_instance(
1696 detect=True,
1697 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzd15bbcf2013-05-21 17:33:57 +08001698 shopfloor_client.UploadEvent(chunk.log_name + "." +
1699 event_log.GetReimageId(),
1700 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001701 logging.info(
1702 'Successfully synced %s in %.03f s',
1703 description, time.time() - start_time)
1704 except: # pylint: disable=W0702
Jon Salzd15bbcf2013-05-21 17:33:57 +08001705 first_exception = (first_exception or (chunk.log_name + ': ' +
Vic Yang93027612013-05-06 02:42:49 +08001706 utils.FormatExceptionOnly()))
1707 exception_count += 1
1708
1709 if exception_count:
1710 if exception_count == 1:
1711 msg = 'Log upload failed: %s' % first_exception
1712 else:
1713 msg = '%d log upload failed; first is: %s' % (
1714 exception_count, first_exception)
1715 raise Exception(msg)
1716
Jon Salz57717ca2012-04-04 16:47:25 +08001717
Jon Salz0697cbf2012-07-04 15:14:04 +08001718 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1719 root=None):
1720 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001721
Jon Salz0697cbf2012-07-04 15:14:04 +08001722 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001723
Jon Salz0697cbf2012-07-04 15:14:04 +08001724 Args:
1725 starting_at: If provided, only auto-runs tests beginning with
1726 this test.
1727 '''
1728 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001729
Jon Salz0697cbf2012-07-04 15:14:04 +08001730 if starting_at:
1731 # Make sure they passed a test, not a string.
1732 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001733
Jon Salz0697cbf2012-07-04 15:14:04 +08001734 tests_to_reset = []
1735 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001736
Jon Salz0697cbf2012-07-04 15:14:04 +08001737 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001738
Jon Salz0697cbf2012-07-04 15:14:04 +08001739 for test in root.get_top_level_tests():
1740 if starting_at:
1741 if test == starting_at:
1742 # We've found starting_at; do auto-run on all
1743 # subsequent tests.
1744 found_starting_at = True
1745 if not found_starting_at:
1746 # Don't start this guy yet
1747 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001748
Jon Salz0697cbf2012-07-04 15:14:04 +08001749 status = test.get_state().status
1750 if status == TestState.ACTIVE or status in statuses_to_run:
1751 # Reset the test (later; we will need to abort
1752 # all active tests first).
1753 tests_to_reset.append(test)
1754 if status in statuses_to_run:
1755 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001756
Jon Salz6dc031d2013-06-19 13:06:23 +08001757 self.abort_active_tests('Operator requested run/re-run of certain tests')
Jon Salz258a40c2012-04-19 12:34:01 +08001758
Jon Salz0697cbf2012-07-04 15:14:04 +08001759 # Reset all statuses of the tests to run (in case any tests were active;
1760 # we want them to be run again).
1761 for test_to_reset in tests_to_reset:
1762 for test in test_to_reset.walk():
1763 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001764
Jon Salz0697cbf2012-07-04 15:14:04 +08001765 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001766
Jon Salz0697cbf2012-07-04 15:14:04 +08001767 def restart_tests(self, root=None):
1768 '''Restarts all tests.'''
1769 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001770
Jon Salz6dc031d2013-06-19 13:06:23 +08001771 self.abort_active_tests('Operator requested restart of certain tests')
Jon Salz0697cbf2012-07-04 15:14:04 +08001772 for test in root.walk():
1773 test.update_state(status=TestState.UNTESTED)
1774 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001775
Jon Salz0697cbf2012-07-04 15:14:04 +08001776 def auto_run(self, starting_at=None, root=None):
1777 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001778
Jon Salz0697cbf2012-07-04 15:14:04 +08001779 Args:
1780 starting_at: If provide, only auto-runs tests beginning with
1781 this test.
1782 '''
1783 root = root or self.test_list
1784 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1785 starting_at=starting_at,
1786 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001787
Jon Salz0697cbf2012-07-04 15:14:04 +08001788 def re_run_failed(self, root=None):
1789 '''Re-runs failed tests.'''
1790 root = root or self.test_list
1791 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001792
Jon Salz0697cbf2012-07-04 15:14:04 +08001793 def show_review_information(self):
1794 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001795
Jon Salz0697cbf2012-07-04 15:14:04 +08001796 The information screene is rendered by main UI program (ui.py), so in
1797 goofy we only need to kill all active tests, set them as untested, and
1798 clear remaining tests.
1799 '''
1800 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001801 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001802
Jon Salz0697cbf2012-07-04 15:14:04 +08001803 def handle_switch_test(self, event):
1804 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001805
Jon Salz0697cbf2012-07-04 15:14:04 +08001806 @param event: The SWITCH_TEST event.
1807 '''
1808 test = self.test_list.lookup_path(event.path)
1809 if not test:
1810 logging.error('Unknown test %r', event.key)
1811 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001812
Jon Salz0697cbf2012-07-04 15:14:04 +08001813 invoc = self.invocations.get(test)
1814 if invoc and test.backgroundable:
1815 # Already running: just bring to the front if it
1816 # has a UI.
1817 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001818 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001819 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001820
Jon Salz6dc031d2013-06-19 13:06:23 +08001821 self.abort_active_tests('Operator requested abort (switch_test)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001822 for t in test.walk():
1823 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001824
Jon Salz0697cbf2012-07-04 15:14:04 +08001825 if self.test_list.options.auto_run_on_keypress:
1826 self.auto_run(starting_at=test)
1827 else:
1828 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001829
Jon Salz0697cbf2012-07-04 15:14:04 +08001830 def wait(self):
1831 '''Waits for all pending invocations.
1832
1833 Useful for testing.
1834 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001835 while self.invocations:
1836 for k, v in self.invocations.iteritems():
1837 logging.info('Waiting for %s to complete...', k)
1838 v.thread.join()
1839 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001840
1841 def check_exceptions(self):
1842 '''Raises an error if any exceptions have occurred in
1843 invocation threads.'''
1844 if self.exceptions:
1845 raise RuntimeError('Exception in invocation thread: %r' %
1846 self.exceptions)
1847
1848 def record_exception(self, msg):
1849 '''Records an exception in an invocation thread.
1850
1851 An exception with the given message will be rethrown when
1852 Goofy is destroyed.'''
1853 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001854
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001855
1856if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001857 goofy = Goofy()
1858 try:
1859 goofy.main()
Jon Salz0f996602012-10-03 15:26:48 +08001860 except SystemExit:
1861 # Propagate SystemExit without logging.
1862 raise
Jon Salz31373eb2012-09-21 16:19:49 +08001863 except:
Jon Salz0f996602012-10-03 15:26:48 +08001864 # Log the error before trying to shut down (unless it's a graceful
1865 # exit).
Jon Salz31373eb2012-09-21 16:19:49 +08001866 logging.exception('Error in main loop')
1867 raise
Jon Salz77c151e2012-08-28 07:20:37 +08001868 finally:
1869 goofy.destroy()