blob: b4988e411fd40af458d9989164774f6056c80a3f [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 Salze12c2b32013-06-25 16:24:34 +080012import glob
Jon Salz0405ab52012-03-16 15:26:52 +080013import logging
14import os
Jon Salz73e0fd02012-04-04 11:46:38 +080015import Queue
Jon Salze12c2b32013-06-25 16:24:34 +080016import shutil
Jon Salz77c151e2012-08-28 07:20:37 +080017import signal
Jon Salz0405ab52012-03-16 15:26:52 +080018import sys
Jon Salzeff94182013-06-19 15:06:28 +080019import syslog
Jon Salz0405ab52012-03-16 15:26:52 +080020import threading
21import time
22import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080023import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080024from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080025from collections import deque
26from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080027
Jon Salz0697cbf2012-07-04 15:14:04 +080028import factory_common # pylint: disable=W0611
jcliangcd688182012-08-20 21:01:26 +080029from cros.factory import event_log
30from cros.factory import system
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +080031from cros.factory.event_log import EventLog, FloatDigit
Tom Wai-Hong Tamd33723e2013-04-10 21:14:37 +080032from cros.factory.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080033from cros.factory.goofy import test_environment
34from cros.factory.goofy import time_sanitizer
Jon Salz83591782012-06-26 11:09:58 +080035from cros.factory.goofy import updater
jcliangcd688182012-08-20 21:01:26 +080036from cros.factory.goofy.goofy_rpc import GoofyRPC
37from cros.factory.goofy.invocation import TestInvocation
38from cros.factory.goofy.prespawner import Prespawner
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +080039from cros.factory.goofy.system_log_manager import SystemLogManager
jcliangcd688182012-08-20 21:01:26 +080040from cros.factory.goofy.web_socket_manager import WebSocketManager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +080041from cros.factory.system.board import Board, BoardException
jcliangcd688182012-08-20 21:01:26 +080042from cros.factory.system.charge_manager import ChargeManager
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +080043from cros.factory.system.core_dump_manager import CoreDumpManager
Jon Salzce6a7f82013-06-10 18:22:54 +080044from cros.factory.system.cpufreq_manager import CpufreqManager
Jon Salzb92c5112012-09-21 15:40:11 +080045from cros.factory.system import disk_space
jcliangcd688182012-08-20 21:01:26 +080046from cros.factory.test import factory
47from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080048from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080049from cros.factory.test import utils
50from cros.factory.test.event import Event
51from cros.factory.test.event import EventClient
52from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080053from cros.factory.test.factory import TestState
Dean Liao592e4d52013-01-10 20:06:39 +080054from cros.factory.tools.key_filter import KeyFilter
Jon Salz2af235d2013-06-24 14:47:21 +080055from cros.factory.utils import file_utils
Jon Salz78c32392012-07-25 14:18:29 +080056from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080057
58
Jon Salz2f757d42012-06-27 17:06:42 +080059CUSTOM_DIR = os.path.join(factory.FACTORY_PATH, 'custom')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080060HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Chun-ta Lin279e7e92013-02-19 17:40:39 +080061CACHES_DIR = os.path.join(factory.get_state_root(), "caches")
Hung-Te Linf2f78f72012-02-08 19:27:11 +080062
Jon Salz8796e362012-05-24 11:39:09 +080063# File that suppresses reboot if present (e.g., for development).
64NO_REBOOT_FILE = '/var/log/factory.noreboot'
65
Jon Salz5c344f62012-07-13 14:31:16 +080066# Value for tests_after_shutdown that forces auto-run (e.g., after
67# a factory update, when the available set of tests might change).
68FORCE_AUTO_RUN = 'force_auto_run'
69
cychiang21886742012-07-05 15:16:32 +080070RUN_QUEUE_TIMEOUT_SECS = 10
71
Justin Chuang83813982013-05-13 01:26:32 +080072# Sync disks when battery level is higher than this value.
73# Otherwise, power loss during disk sync operation may incur even worse outcome.
74MIN_BATTERY_LEVEL_FOR_DISK_SYNC = 1.0
75
Jon Salze12c2b32013-06-25 16:24:34 +080076MAX_CRASH_FILE_SIZE = 64*1024
77
Jon Salz758e6cc2012-04-03 15:47:07 +080078GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
79You are running Goofy inside the chroot. Autotests are not supported.
80
81To use Goofy in the chroot, first install an Xvnc server:
82
Jon Salz0697cbf2012-07-04 15:14:04 +080083 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080084
85...and then start a VNC X server outside the chroot:
86
Jon Salz0697cbf2012-07-04 15:14:04 +080087 vncserver :10 &
88 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080089
90...and run Goofy as follows:
91
Jon Salz0697cbf2012-07-04 15:14:04 +080092 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080093''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080094suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080095
96def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +080097 '''
98 Returns the HWID config tag, or an empty string if none can be found.
99 '''
100 if 'CROS_HWID' in os.environ:
101 return os.environ['CROS_HWID']
102 if os.path.exists(HWID_CFG_PATH):
103 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
104 return hwid_cfg_handle.read().strip()
105 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800106
107
108def find_test_list():
Jon Salz0697cbf2012-07-04 15:14:04 +0800109 '''
110 Returns the path to the active test list, based on the HWID config tag.
Jon Salzfb615892013-02-01 18:04:35 +0800111
112 The algorithm is:
113
114 - Try $FACTORY/test_lists/active (the symlink reflecting the option chosen
115 in the UI).
116 - For each of $FACTORY/custom, $FACTORY/test_lists (and
117 autotest/site_tests/suite_Factory for backward compatibility):
118 - Try test_list_${hwid_cfg} (if hwid_cfg is set)
119 - Try test_list
120 - Try test_list.generic
Jon Salz0697cbf2012-07-04 15:14:04 +0800121 '''
Jon Salzfb615892013-02-01 18:04:35 +0800122 # If the 'active' symlink is present, that trumps everything else.
123 if os.path.lexists(factory.ACTIVE_TEST_LIST_SYMLINK):
124 return os.path.realpath(factory.ACTIVE_TEST_LIST_SYMLINK)
125
Jon Salz0697cbf2012-07-04 15:14:04 +0800126 hwid_cfg = get_hwid_cfg()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800127
Jon Salzfb615892013-02-01 18:04:35 +0800128 search_dirs = [CUSTOM_DIR, factory.TEST_LISTS_PATH]
Jon Salz4be56b02012-12-22 07:30:46 +0800129 if not utils.in_chroot():
130 # Also look in suite_Factory. For backward compatibility only;
131 # new boards should just put the test list in the "test_lists"
132 # directory.
133 search_dirs.insert(0, os.path.join(
134 os.path.dirname(factory.FACTORY_PATH),
135 'autotest', 'site_tests', 'suite_Factory'))
Jon Salz2f757d42012-06-27 17:06:42 +0800136
Jon Salzfb615892013-02-01 18:04:35 +0800137
138 search_files = []
Jon Salz0697cbf2012-07-04 15:14:04 +0800139 if hwid_cfg:
Jon Salzfb615892013-02-01 18:04:35 +0800140 search_files += [hwid_cfg]
141 search_files += ['test_list', 'test_list.generic']
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800142
Jon Salz0697cbf2012-07-04 15:14:04 +0800143 for d in search_dirs:
144 for f in search_files:
145 test_list = os.path.join(d, f)
146 if os.path.exists(test_list):
147 return test_list
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800148
Jon Salz0697cbf2012-07-04 15:14:04 +0800149 logging.warn('Cannot find test lists named any of %s in any of %s',
150 search_files, search_dirs)
151 return None
Jon Salz73e0fd02012-04-04 11:46:38 +0800152
Jon Salzfb615892013-02-01 18:04:35 +0800153
Jon Salz73e0fd02012-04-04 11:46:38 +0800154_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800155
156class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800157 '''
158 The main factory flow.
159
160 Note that all methods in this class must be invoked from the main
161 (event) thread. Other threads, such as callbacks and TestInvocation
162 methods, should instead post events on the run queue.
163
164 TODO: Unit tests. (chrome-os-partner:7409)
165
166 Properties:
167 uuid: A unique UUID for this invocation of Goofy.
168 state_instance: An instance of FactoryState.
169 state_server: The FactoryState XML/RPC server.
170 state_server_thread: A thread running state_server.
171 event_server: The EventServer socket server.
172 event_server_thread: A thread running event_server.
173 event_client: A client to the event server.
174 connection_manager: The connection_manager object.
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800175 system_log_manager: The SystemLogManager object.
176 core_dump_manager: The CoreDumpManager object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800177 ui_process: The factory ui process object.
178 run_queue: A queue of callbacks to invoke from the main thread.
179 invocations: A map from FactoryTest objects to the corresponding
180 TestInvocations objects representing active tests.
181 tests_to_run: A deque of tests that should be run when the current
182 test(s) complete.
183 options: Command-line options.
184 args: Command-line args.
185 test_list: The test list.
186 event_handlers: Map of Event.Type to the method used to handle that
187 event. If the method has an 'event' argument, the event is passed
188 to the handler.
189 exceptions: Exceptions encountered in invocation threads.
Jon Salz3c493bb2013-02-07 17:24:58 +0800190 last_log_disk_space_message: The last message we logged about disk space
191 (to avoid duplication).
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800192 last_kick_sync_time: The last time to kick system_log_manager to sync
193 because of core dump files (to avoid kicking too soon then abort the
194 sync.)
Jon Salz416f9cc2013-05-10 18:32:50 +0800195 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salz0697cbf2012-07-04 15:14:04 +0800196 '''
197 def __init__(self):
198 self.uuid = str(uuid.uuid4())
199 self.state_instance = None
200 self.state_server = None
201 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800202 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800203 self.event_server = None
204 self.event_server_thread = None
205 self.event_client = None
206 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800207 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800208 self.time_sanitizer = None
209 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800210 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800211 self.system_log_manager = None
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800212 self.core_dump_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800213 self.event_log = None
214 self.prespawner = None
215 self.ui_process = None
Jon Salzc79a9982012-08-30 04:42:01 +0800216 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800217 self.run_queue = Queue.Queue()
218 self.invocations = {}
219 self.tests_to_run = deque()
220 self.visible_test = None
221 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800222 self.hooks = None
Vic Yangd8990da2013-06-27 16:57:43 +0800223 self.cpu_usage_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800224
225 self.options = None
226 self.args = None
227 self.test_list = None
228 self.on_ui_startup = []
229 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800230 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800231 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800232 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800233 self.last_sync_time = None
Jon Salzb92c5112012-09-21 15:40:11 +0800234 self.last_log_disk_space_time = None
Jon Salz3c493bb2013-02-07 17:24:58 +0800235 self.last_log_disk_space_message = None
Justin Chuang83813982013-05-13 01:26:32 +0800236 self.last_check_battery_time = None
237 self.last_check_battery_message = None
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800238 self.last_kick_sync_time = None
Vic Yang311ddb82012-09-26 12:08:28 +0800239 self.exclusive_items = set()
Jon Salz0f996602012-10-03 15:26:48 +0800240 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800241 self.key_filter = None
Jon Salzce6a7f82013-06-10 18:22:54 +0800242 self.cpufreq_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800243
Jon Salz85a39882012-07-05 16:45:04 +0800244 def test_or_root(event, parent_or_group=True):
245 '''Returns the test affected by a particular event.
246
247 Args:
248 event: The event containing an optional 'path' attribute.
249 parent_on_group: If True, returns the top-level parent for a test (the
250 root node of the tests that need to be run together if the given test
251 path is to be run).
252 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800253 try:
254 path = event.path
255 except AttributeError:
256 path = None
257
258 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800259 test = self.test_list.lookup_path(path)
260 if parent_or_group:
261 test = test.get_top_level_parent_or_group()
262 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800263 else:
264 return self.test_list
265
266 self.event_handlers = {
267 Event.Type.SWITCH_TEST: self.handle_switch_test,
268 Event.Type.SHOW_NEXT_ACTIVE_TEST:
269 lambda event: self.show_next_active_test(),
270 Event.Type.RESTART_TESTS:
271 lambda event: self.restart_tests(root=test_or_root(event)),
272 Event.Type.AUTO_RUN:
273 lambda event: self.auto_run(root=test_or_root(event)),
274 Event.Type.RE_RUN_FAILED:
275 lambda event: self.re_run_failed(root=test_or_root(event)),
276 Event.Type.RUN_TESTS_WITH_STATUS:
277 lambda event: self.run_tests_with_status(
278 event.status,
279 root=test_or_root(event)),
280 Event.Type.REVIEW:
281 lambda event: self.show_review_information(),
282 Event.Type.UPDATE_SYSTEM_INFO:
283 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800284 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800285 lambda event: self.stop(root=test_or_root(event, False),
Jon Salz6dc031d2013-06-19 13:06:23 +0800286 fail=getattr(event, 'fail', False),
287 reason=getattr(event, 'reason', None)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800288 Event.Type.SET_VISIBLE_TEST:
289 lambda event: self.set_visible_test(
290 self.test_list.lookup_path(event.path)),
Jon Salz4712ac72013-02-07 17:12:05 +0800291 Event.Type.CLEAR_STATE:
292 lambda event: self.clear_state(self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800293 }
294
295 self.exceptions = []
296 self.web_socket_manager = None
297
298 def destroy(self):
299 if self.chrome:
300 self.chrome.kill()
301 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800302 if self.dummy_shopfloor:
303 self.dummy_shopfloor.kill()
304 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800305 if self.ui_process:
306 utils.kill_process_tree(self.ui_process, 'ui')
307 self.ui_process = None
308 if self.web_socket_manager:
309 logging.info('Stopping web sockets')
310 self.web_socket_manager.close()
311 self.web_socket_manager = None
312 if self.state_server_thread:
313 logging.info('Stopping state server')
314 self.state_server.shutdown()
315 self.state_server_thread.join()
316 self.state_server.server_close()
317 self.state_server_thread = None
318 if self.state_instance:
319 self.state_instance.close()
320 if self.event_server_thread:
321 logging.info('Stopping event server')
322 self.event_server.shutdown() # pylint: disable=E1101
323 self.event_server_thread.join()
324 self.event_server.server_close()
325 self.event_server_thread = None
326 if self.log_watcher:
327 if self.log_watcher.IsThreadStarted():
328 self.log_watcher.StopWatchThread()
329 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800330 if self.system_log_manager:
331 if self.system_log_manager.IsThreadRunning():
332 self.system_log_manager.StopSyncThread()
333 self.system_log_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800334 if self.prespawner:
335 logging.info('Stopping prespawner')
336 self.prespawner.stop()
337 self.prespawner = None
338 if self.event_client:
339 logging.info('Closing event client')
340 self.event_client.close()
341 self.event_client = None
Jon Salzddf0d052013-06-18 12:52:44 +0800342 if self.cpufreq_manager:
343 self.cpufreq_manager.Stop()
Jon Salz0697cbf2012-07-04 15:14:04 +0800344 if self.event_log:
345 self.event_log.Close()
346 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800347 if self.key_filter:
348 self.key_filter.Stop()
Vic Yangd8990da2013-06-27 16:57:43 +0800349 if self.cpu_usage_watcher:
350 self.cpu_usage_watcher.terminate()
Dean Liao592e4d52013-01-10 20:06:39 +0800351
Jon Salz0697cbf2012-07-04 15:14:04 +0800352 self.check_exceptions()
353 logging.info('Done destroying Goofy')
354
355 def start_state_server(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800356 # Before starting state server, remount stateful partitions with
357 # no commit flag. The default commit time (commit=600) makes corruption
358 # too likely.
359 file_utils.ResetCommitTime()
360
Jon Salz0697cbf2012-07-04 15:14:04 +0800361 self.state_instance, self.state_server = (
362 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800363 self.goofy_rpc = GoofyRPC(self)
364 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800365 logging.info('Starting state server')
366 self.state_server_thread = threading.Thread(
367 target=self.state_server.serve_forever,
368 name='StateServer')
369 self.state_server_thread.start()
370
371 def start_event_server(self):
372 self.event_server = EventServer()
373 logging.info('Starting factory event server')
374 self.event_server_thread = threading.Thread(
375 target=self.event_server.serve_forever,
376 name='EventServer') # pylint: disable=E1101
377 self.event_server_thread.start()
378
379 self.event_client = EventClient(
380 callback=self.handle_event, event_loop=self.run_queue)
381
382 self.web_socket_manager = WebSocketManager(self.uuid)
383 self.state_server.add_handler("/event",
384 self.web_socket_manager.handle_web_socket)
385
386 def start_ui(self):
387 ui_proc_args = [
388 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
389 self.options.test_list]
390 if self.options.verbose:
391 ui_proc_args.append('-v')
392 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800393 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800394 logging.info('Waiting for UI to come up...')
395 self.event_client.wait(
396 lambda event: event.type == Event.Type.UI_READY)
397 logging.info('UI has started')
398
399 def set_visible_test(self, test):
400 if self.visible_test == test:
401 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800402 if test and not test.has_ui:
403 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800404
405 if test:
406 test.update_state(visible=True)
407 if self.visible_test:
408 self.visible_test.update_state(visible=False)
409 self.visible_test = test
410
Jon Salzd4306c82012-11-30 15:16:36 +0800411 def _log_startup_messages(self):
412 '''Logs the tail of var/log/messages and mosys and EC console logs.'''
413 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
414 # for factory-3004.B only. Consolidate and merge back to ToT.
415 if utils.in_chroot():
416 return
417
418 try:
419 var_log_messages = (
420 utils.var_log_messages_before_reboot())
421 logging.info(
422 'Tail of /var/log/messages before last reboot:\n'
423 '%s', ('\n'.join(
424 ' ' + x for x in var_log_messages)))
425 except: # pylint: disable=W0702
426 logging.exception('Unable to grok /var/log/messages')
427
428 try:
429 mosys_log = utils.Spawn(
430 ['mosys', 'eventlog', 'list'],
431 read_stdout=True, log_stderr_on_error=True).stdout_data
432 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
433 except: # pylint: disable=W0702
434 logging.exception('Unable to read mosys eventlog')
435
436 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800437 board = system.GetBoard()
438 ec_console_log = board.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800439 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
440 except: # pylint: disable=W0702
441 logging.exception('Error retrieving EC console log')
442
Vic Yang079f9872013-07-01 11:32:00 +0800443 try:
444 board = system.GetBoard()
445 ec_panic_info = board.GetECPanicInfo()
446 logging.info('EC panic info after reboot:\n%s\n', ec_panic_info)
447 except: # pylint: disable=W0702
448 logging.exception('Error retrieving EC panic info')
449
Jon Salz0697cbf2012-07-04 15:14:04 +0800450 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800451 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800452 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800453
Jon Salz0697cbf2012-07-04 15:14:04 +0800454 @param test: The ShutdownStep.
455 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800456 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800457 test_state = test.update_state(increment_shutdown_count=1)
458 logging.info('Detected shutdown (%d of %d)',
459 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800460
Jon Salz0697cbf2012-07-04 15:14:04 +0800461 def log_and_update_state(status, error_msg, **kw):
462 self.event_log.Log('rebooted',
463 status=status, error_msg=error_msg, **kw)
Jon Salzd4306c82012-11-30 15:16:36 +0800464 logging.info('Rebooted: status=%s, %s', status,
465 (('error_msg=%s' % error_msg) if error_msg else None))
Jon Salz0697cbf2012-07-04 15:14:04 +0800466 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800467
Jon Salz0697cbf2012-07-04 15:14:04 +0800468 if not self.last_shutdown_time:
469 log_and_update_state(status=TestState.FAILED,
470 error_msg='Unable to read shutdown_time')
471 return
Jon Salz258a40c2012-04-19 12:34:01 +0800472
Jon Salz0697cbf2012-07-04 15:14:04 +0800473 now = time.time()
474 logging.info('%.03f s passed since reboot',
475 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800476
Jon Salz0697cbf2012-07-04 15:14:04 +0800477 if self.last_shutdown_time > now:
478 test.update_state(status=TestState.FAILED,
479 error_msg='Time moved backward during reboot')
480 elif (isinstance(test, factory.RebootStep) and
481 self.test_list.options.max_reboot_time_secs and
482 (now - self.last_shutdown_time >
483 self.test_list.options.max_reboot_time_secs)):
484 # A reboot took too long; fail. (We don't check this for
485 # HaltSteps, because the machine could be halted for a
486 # very long time, and even unplugged with battery backup,
487 # thus hosing the clock.)
488 log_and_update_state(
489 status=TestState.FAILED,
490 error_msg=('More than %d s elapsed during reboot '
491 '(%.03f s, from %s to %s)' % (
492 self.test_list.options.max_reboot_time_secs,
493 now - self.last_shutdown_time,
494 utils.TimeString(self.last_shutdown_time),
495 utils.TimeString(now))),
496 duration=(now-self.last_shutdown_time))
Jon Salzd4306c82012-11-30 15:16:36 +0800497 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800498 elif test_state.shutdown_count == test.iterations:
499 # Good!
500 log_and_update_state(status=TestState.PASSED,
501 duration=(now - self.last_shutdown_time),
502 error_msg='')
503 elif test_state.shutdown_count > test.iterations:
504 # Shut down too many times
505 log_and_update_state(status=TestState.FAILED,
506 error_msg='Too many shutdowns')
Jon Salzd4306c82012-11-30 15:16:36 +0800507 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800508 elif utils.are_shift_keys_depressed():
509 logging.info('Shift keys are depressed; cancelling restarts')
510 # Abort shutdown
511 log_and_update_state(
512 status=TestState.FAILED,
513 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800514 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 else:
516 def handler():
517 if self._prompt_cancel_shutdown(
518 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800519 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800520 log_and_update_state(
521 status=TestState.FAILED,
522 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800523 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800524 return
Jon Salz0405ab52012-03-16 15:26:52 +0800525
Jon Salz0697cbf2012-07-04 15:14:04 +0800526 # Time to shutdown again
527 log_and_update_state(
528 status=TestState.ACTIVE,
529 error_msg='',
530 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800531
Jon Salz0697cbf2012-07-04 15:14:04 +0800532 self.event_log.Log('shutdown', operation='reboot')
533 self.state_instance.set_shared_data('shutdown_time',
534 time.time())
535 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800536
Jon Salz0697cbf2012-07-04 15:14:04 +0800537 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800538
Jon Salz0697cbf2012-07-04 15:14:04 +0800539 def _prompt_cancel_shutdown(self, test, iteration):
540 if self.options.ui != 'chrome':
541 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800542
Jon Salz0697cbf2012-07-04 15:14:04 +0800543 pending_shutdown_data = {
544 'delay_secs': test.delay_secs,
545 'time': time.time() + test.delay_secs,
546 'operation': test.operation,
547 'iteration': iteration,
548 'iterations': test.iterations,
549 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800550
Jon Salz0697cbf2012-07-04 15:14:04 +0800551 # Create a new (threaded) event client since we
552 # don't want to use the event loop for this.
553 with EventClient() as event_client:
554 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
555 **pending_shutdown_data))
556 aborted = event_client.wait(
557 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
558 timeout=test.delay_secs) is not None
559 if aborted:
560 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
561 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800562
Jon Salz0697cbf2012-07-04 15:14:04 +0800563 def init_states(self):
564 '''
565 Initializes all states on startup.
566 '''
567 for test in self.test_list.get_all_tests():
568 # Make sure the state server knows about all the tests,
569 # defaulting to an untested state.
570 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800571
Jon Salz0697cbf2012-07-04 15:14:04 +0800572 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800573 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800574 ec_console_log = None
Vic Yang079f9872013-07-01 11:32:00 +0800575 ec_panic_info = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800576
Jon Salz0697cbf2012-07-04 15:14:04 +0800577 # Any 'active' tests should be marked as failed now.
578 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800579 if not test.is_leaf():
580 # Don't bother with parents; they will be updated when their
581 # children are updated.
582 continue
583
Jon Salz0697cbf2012-07-04 15:14:04 +0800584 test_state = test.get_state()
585 if test_state.status != TestState.ACTIVE:
586 continue
587 if isinstance(test, factory.ShutdownStep):
588 # Shutdown while the test was active - that's good.
589 self.handle_shutdown_complete(test, test_state)
590 else:
591 # Unexpected shutdown. Grab /var/log/messages for context.
592 if var_log_messages is None:
593 try:
594 var_log_messages = (
595 utils.var_log_messages_before_reboot())
596 # Write it to the log, to make it easier to
597 # correlate with /var/log/messages.
598 logging.info(
599 'Unexpected shutdown. '
600 'Tail of /var/log/messages before last reboot:\n'
601 '%s', ('\n'.join(
602 ' ' + x for x in var_log_messages)))
603 except: # pylint: disable=W0702
604 logging.exception('Unable to grok /var/log/messages')
605 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800606
Jon Salz008f4ea2012-08-28 05:39:45 +0800607 if mosys_log is None and not utils.in_chroot():
608 try:
609 mosys_log = utils.Spawn(
610 ['mosys', 'eventlog', 'list'],
611 read_stdout=True, log_stderr_on_error=True).stdout_data
612 # Write it to the log also.
613 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
614 except: # pylint: disable=W0702
615 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800616
Vic Yange4c275d2012-08-28 01:50:20 +0800617 if ec_console_log is None:
618 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800619 board = system.GetBoard()
620 ec_console_log = board.GetECConsoleLog()
Vic Yange4c275d2012-08-28 01:50:20 +0800621 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Jon Salzfe1f6652012-09-07 05:40:14 +0800622 except: # pylint: disable=W0702
Vic Yange4c275d2012-08-28 01:50:20 +0800623 logging.exception('Error retrieving EC console log')
624
Vic Yang079f9872013-07-01 11:32:00 +0800625 if ec_panic_info is None:
626 try:
627 board = system.GetBoard()
628 ec_panic_info = board.GetECPanicInfo()
629 logging.info('EC panic info after reboot:\n%s\n', ec_panic_info)
630 except: # pylint: disable=W0702
631 logging.exception('Error retrieving EC panic info')
632
Jon Salz0697cbf2012-07-04 15:14:04 +0800633 error_msg = 'Unexpected shutdown while test was running'
634 self.event_log.Log('end_test',
635 path=test.path,
636 status=TestState.FAILED,
637 invocation=test.get_state().invocation,
638 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800639 var_log_messages='\n'.join(var_log_messages),
640 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800641 test.update_state(
642 status=TestState.FAILED,
643 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800644
Jon Salz50efe942012-07-26 11:54:10 +0800645 if not test.never_fails:
646 # For "never_fails" tests (such as "Start"), don't cancel
647 # pending tests, since reboot is expected.
648 factory.console.info('Unexpected shutdown while test %s '
649 'running; cancelling any pending tests',
650 test.path)
651 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800652
Jon Salz008f4ea2012-08-28 05:39:45 +0800653 self.update_skipped_tests()
654
655 def update_skipped_tests(self):
656 '''
657 Updates skipped states based on run_if.
658 '''
659 for t in self.test_list.walk():
660 if t.is_leaf() and t.run_if_table_name:
661 skip = False
662 try:
663 aux = shopfloor.get_selected_aux_data(t.run_if_table_name)
664 value = aux.get(t.run_if_col)
665 if value is not None:
666 skip = (not value) ^ t.run_if_not
667 except ValueError:
668 # Not available; assume it shouldn't be skipped
669 pass
670
671 test_state = t.get_state()
672 if ((not skip) and
673 (test_state.status == TestState.PASSED) and
674 (test_state.error_msg == TestState.SKIPPED_MSG)):
675 # It was marked as skipped before, but now we need to run it.
676 # Mark as untested.
677 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
678 else:
679 t.update_state(skip=skip)
680
Jon Salz0697cbf2012-07-04 15:14:04 +0800681 def show_next_active_test(self):
682 '''
683 Rotates to the next visible active test.
684 '''
685 self.reap_completed_tests()
686 active_tests = [
687 t for t in self.test_list.walk()
688 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
689 if not active_tests:
690 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800691
Jon Salz0697cbf2012-07-04 15:14:04 +0800692 try:
693 next_test = active_tests[
694 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
695 except ValueError: # visible_test not present in active_tests
696 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800697
Jon Salz0697cbf2012-07-04 15:14:04 +0800698 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800699
Jon Salz0697cbf2012-07-04 15:14:04 +0800700 def handle_event(self, event):
701 '''
702 Handles an event from the event server.
703 '''
704 handler = self.event_handlers.get(event.type)
705 if handler:
706 handler(event)
707 else:
708 # We don't register handlers for all event types - just ignore
709 # this event.
710 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800711
Vic Yangaabf9fd2013-04-09 18:56:13 +0800712 def check_critical_factory_note(self):
713 '''
714 Returns True if the last factory note is critical.
715 '''
716 notes = self.state_instance.get_shared_data('factory_note', True)
717 return notes and notes[-1]['level'] == 'CRITICAL'
718
Jon Salz0697cbf2012-07-04 15:14:04 +0800719 def run_next_test(self):
720 '''
721 Runs the next eligible test (or tests) in self.tests_to_run.
722 '''
723 self.reap_completed_tests()
Vic Yangaabf9fd2013-04-09 18:56:13 +0800724 if self.tests_to_run and self.check_critical_factory_note():
725 self.tests_to_run.clear()
726 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800727 while self.tests_to_run:
728 logging.debug('Tests to run: %s',
729 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800730
Jon Salz0697cbf2012-07-04 15:14:04 +0800731 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800732
Jon Salz0697cbf2012-07-04 15:14:04 +0800733 if test in self.invocations:
734 logging.info('Next test %s is already running', test.path)
735 self.tests_to_run.popleft()
736 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800737
Jon Salza1412922012-07-23 16:04:17 +0800738 for requirement in test.require_run:
739 for i in requirement.test.walk():
740 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800741 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800742 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800743 return
744
Jon Salz0697cbf2012-07-04 15:14:04 +0800745 if self.invocations and not (test.backgroundable and all(
746 [x.backgroundable for x in self.invocations])):
747 logging.debug('Waiting for non-backgroundable tests to '
748 'complete before running %s', test.path)
749 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800750
Jon Salz3e6f5202012-10-15 15:08:29 +0800751 if test.get_state().skip:
752 factory.console.info('Skipping test %s', test.path)
753 test.update_state(status=TestState.PASSED,
754 error_msg=TestState.SKIPPED_MSG)
755 self.tests_to_run.popleft()
756 continue
757
Jon Salz0697cbf2012-07-04 15:14:04 +0800758 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800759
Jon Salz304a75d2012-07-06 11:14:15 +0800760 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800761 for requirement in test.require_run:
762 for i in requirement.test.walk():
763 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800764 # We've hit this test itself; stop checking
765 break
Jon Salza1412922012-07-23 16:04:17 +0800766 if ((i.get_state().status == TestState.UNTESTED) or
767 (requirement.passed and i.get_state().status !=
768 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800769 # Found an untested test; move on to the next
770 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800771 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800772 break
773
774 if untested:
775 untested_paths = ', '.join(sorted([x.path for x in untested]))
776 if self.state_instance.get_shared_data('engineering_mode',
777 optional=True):
778 # In engineering mode, we'll let it go.
779 factory.console.warn('In engineering mode; running '
780 '%s even though required tests '
781 '[%s] have not completed',
782 test.path, untested_paths)
783 else:
784 # Not in engineering mode; mark it failed.
785 error_msg = ('Required tests [%s] have not been run yet'
786 % untested_paths)
787 factory.console.error('Not running %s: %s',
788 test.path, error_msg)
789 test.update_state(status=TestState.FAILED,
790 error_msg=error_msg)
791 continue
792
Jon Salz0697cbf2012-07-04 15:14:04 +0800793 if isinstance(test, factory.ShutdownStep):
794 if os.path.exists(NO_REBOOT_FILE):
795 test.update_state(
796 status=TestState.FAILED, increment_count=1,
797 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800798 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800799 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800800
Jon Salz0697cbf2012-07-04 15:14:04 +0800801 test.update_state(status=TestState.ACTIVE, increment_count=1,
802 error_msg='', shutdown_count=0)
803 if self._prompt_cancel_shutdown(test, 1):
804 self.event_log.Log('reboot_cancelled')
805 test.update_state(
806 status=TestState.FAILED, increment_count=1,
807 error_msg='Shutdown aborted by operator',
808 shutdown_count=0)
chungyiafe8f772012-08-15 19:36:29 +0800809 continue
Jon Salz2f757d42012-06-27 17:06:42 +0800810
Jon Salz0697cbf2012-07-04 15:14:04 +0800811 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800812 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800813 'tests_after_shutdown',
814 [t.path for t in self.tests_to_run])
815 # Save shutdown time
816 self.state_instance.set_shared_data('shutdown_time',
817 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800818
Jon Salz0697cbf2012-07-04 15:14:04 +0800819 with self.env.lock:
820 self.event_log.Log('shutdown', operation=test.operation)
821 shutdown_result = self.env.shutdown(test.operation)
822 if shutdown_result:
823 # That's all, folks!
824 self.run_queue.put(None)
825 return
826 else:
827 # Just pass (e.g., in the chroot).
828 test.update_state(status=TestState.PASSED)
829 self.state_instance.set_shared_data(
830 'tests_after_shutdown', None)
831 # Send event with no fields to indicate that there is no
832 # longer a pending shutdown.
833 self.event_client.post_event(Event(
834 Event.Type.PENDING_SHUTDOWN))
835 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800836
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800837 self._run_test(test, test.iterations, test.retries)
Jon Salz1acc8742012-07-17 17:45:55 +0800838
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800839 def _run_test(self, test, iterations_left=None, retries_left=None):
Jon Salz1acc8742012-07-17 17:45:55 +0800840 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
841 new_state = test.update_state(
842 status=TestState.ACTIVE, increment_count=1, error_msg='',
Jon Salzbd42ce12012-09-18 08:03:59 +0800843 invocation=invoc.uuid, iterations_left=iterations_left,
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800844 retries_left=retries_left,
Jon Salzbd42ce12012-09-18 08:03:59 +0800845 visible=(self.visible_test == test))
Jon Salz1acc8742012-07-17 17:45:55 +0800846 invoc.count = new_state.count
847
848 self.invocations[test] = invoc
849 if self.visible_test is None and test.has_ui:
850 self.set_visible_test(test)
Vic Yang311ddb82012-09-26 12:08:28 +0800851 self.check_exclusive()
Jon Salz1acc8742012-07-17 17:45:55 +0800852 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800853
Vic Yang311ddb82012-09-26 12:08:28 +0800854 def check_exclusive(self):
Jon Salzce6a7f82013-06-10 18:22:54 +0800855 # alias since this is really long
856 EXCL_OPT = factory.FactoryTest.EXCLUSIVE_OPTIONS
857
Vic Yang311ddb82012-09-26 12:08:28 +0800858 current_exclusive_items = set([
Jon Salzce6a7f82013-06-10 18:22:54 +0800859 item for item in EXCL_OPT
Vic Yang311ddb82012-09-26 12:08:28 +0800860 if any([test.is_exclusive(item) for test in self.invocations])])
861
862 new_exclusive_items = current_exclusive_items - self.exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800863 if EXCL_OPT.NETWORKING in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800864 logging.info('Disabling network')
865 self.connection_manager.DisableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800866 if EXCL_OPT.CHARGER in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800867 logging.info('Stop controlling charger')
868
869 new_non_exclusive_items = self.exclusive_items - current_exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800870 if EXCL_OPT.NETWORKING in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800871 logging.info('Re-enabling network')
872 self.connection_manager.EnableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800873 if EXCL_OPT.CHARGER in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800874 logging.info('Start controlling charger')
875
Jon Salzce6a7f82013-06-10 18:22:54 +0800876 if self.cpufreq_manager:
877 enabled = EXCL_OPT.CPUFREQ not in current_exclusive_items
878 try:
879 self.cpufreq_manager.SetEnabled(enabled)
880 except: # pylint: disable=W0702
881 logging.exception('Unable to %s cpufreq services',
882 'enable' if enabled else 'disable')
883
Vic Yang311ddb82012-09-26 12:08:28 +0800884 # Only adjust charge state if not excluded
Jon Salzce6a7f82013-06-10 18:22:54 +0800885 if (EXCL_OPT.CHARGER not in current_exclusive_items and
886 not utils.in_chroot()):
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +0800887 if self.charge_manager:
888 self.charge_manager.AdjustChargeState()
889 else:
890 try:
891 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
892 except BoardException:
893 logging.exception('Unable to set charge state on this board')
Vic Yang311ddb82012-09-26 12:08:28 +0800894
895 self.exclusive_items = current_exclusive_items
Jon Salz5da61e62012-05-31 13:06:22 +0800896
cychiang21886742012-07-05 15:16:32 +0800897 def check_for_updates(self):
898 '''
899 Schedules an asynchronous check for updates if necessary.
900 '''
901 if not self.test_list.options.update_period_secs:
902 # Not enabled.
903 return
904
905 now = time.time()
906 if self.last_update_check and (
907 now - self.last_update_check <
908 self.test_list.options.update_period_secs):
909 # Not yet time for another check.
910 return
911
912 self.last_update_check = now
913
914 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
915 if reached_shopfloor:
916 new_update_md5sum = md5sum if needs_update else None
917 if system.SystemInfo.update_md5sum != new_update_md5sum:
918 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
919 system.SystemInfo.update_md5sum = new_update_md5sum
920 self.run_queue.put(self.update_system_info)
921
922 updater.CheckForUpdateAsync(
923 handle_check_for_update,
924 self.test_list.options.shopfloor_timeout_secs)
925
Jon Salza6711d72012-07-18 14:33:03 +0800926 def cancel_pending_tests(self):
927 '''Cancels any tests in the run queue.'''
928 self.run_tests([])
929
Jon Salz0697cbf2012-07-04 15:14:04 +0800930 def run_tests(self, subtrees, untested_only=False):
931 '''
932 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800933
Jon Salz0697cbf2012-07-04 15:14:04 +0800934 The tests are run in order unless one fails (then stops).
935 Backgroundable tests are run simultaneously; when a foreground test is
936 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800937
Jon Salz0697cbf2012-07-04 15:14:04 +0800938 @param subtrees: Node or nodes containing tests to run (may either be
939 a single test or a list). Duplicates will be ignored.
940 '''
941 if type(subtrees) != list:
942 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800943
Jon Salz0697cbf2012-07-04 15:14:04 +0800944 # Nodes we've seen so far, to avoid duplicates.
945 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800946
Jon Salz0697cbf2012-07-04 15:14:04 +0800947 self.tests_to_run = deque()
948 for subtree in subtrees:
949 for test in subtree.walk():
950 if test in seen:
951 continue
952 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800953
Jon Salz0697cbf2012-07-04 15:14:04 +0800954 if not test.is_leaf():
955 continue
956 if (untested_only and
957 test.get_state().status != TestState.UNTESTED):
958 continue
959 self.tests_to_run.append(test)
960 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800961
Jon Salz0697cbf2012-07-04 15:14:04 +0800962 def reap_completed_tests(self):
963 '''
964 Removes completed tests from the set of active tests.
965
966 Also updates the visible test if it was reaped.
967 '''
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800968 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800969 for t, v in dict(self.invocations).iteritems():
970 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800971 test_completed = True
Jon Salz1acc8742012-07-17 17:45:55 +0800972 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800973 del self.invocations[t]
974
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800975 # Stop on failure if flag is true.
976 if (self.test_list.options.stop_on_failure and
977 new_state.status == TestState.FAILED):
978 # Clean all the tests to cause goofy to stop.
979 self.tests_to_run = []
980 factory.console.info("Stop on failure triggered. Empty the queue.")
981
Jon Salz1acc8742012-07-17 17:45:55 +0800982 if new_state.iterations_left and new_state.status == TestState.PASSED:
983 # Play it again, Sam!
984 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800985 # new_state.retries_left is obtained after update.
986 # For retries_left == 0, test can still be run for the last time.
987 elif (new_state.retries_left >= 0 and
988 new_state.status == TestState.FAILED):
989 # Still have to retry, Sam!
990 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +0800991
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800992 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800993 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800994
Jon Salz0697cbf2012-07-04 15:14:04 +0800995 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800996 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800997 self.set_visible_test(None)
998 # Make the first running test, if any, the visible test
999 for t in self.test_list.walk():
1000 if t in self.invocations:
1001 self.set_visible_test(t)
1002 break
1003
Jon Salz6dc031d2013-06-19 13:06:23 +08001004 def kill_active_tests(self, abort, root=None, reason=None):
Jon Salz0697cbf2012-07-04 15:14:04 +08001005 '''
1006 Kills and waits for all active tests.
1007
Jon Salz85a39882012-07-05 16:45:04 +08001008 Args:
1009 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +08001010 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +08001011 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +08001012 '''
1013 self.reap_completed_tests()
1014 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +08001015 if root and not test.has_ancestor(root):
1016 continue
1017
Jon Salz0697cbf2012-07-04 15:14:04 +08001018 factory.console.info('Killing active test %s...' % test.path)
Jon Salz6dc031d2013-06-19 13:06:23 +08001019 invoc.abort_and_join(reason)
Jon Salz0697cbf2012-07-04 15:14:04 +08001020 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +08001021 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +08001022 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +08001023
Jon Salz0697cbf2012-07-04 15:14:04 +08001024 if not abort:
1025 test.update_state(status=TestState.UNTESTED)
1026 self.reap_completed_tests()
1027
Jon Salz6dc031d2013-06-19 13:06:23 +08001028 def stop(self, root=None, fail=False, reason=None):
1029 self.kill_active_tests(fail, root, reason)
Jon Salz85a39882012-07-05 16:45:04 +08001030 # Remove any tests in the run queue under the root.
1031 self.tests_to_run = deque([x for x in self.tests_to_run
1032 if root and not x.has_ancestor(root)])
1033 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +08001034
Jon Salz4712ac72013-02-07 17:12:05 +08001035 def clear_state(self, root=None):
Jon Salz6dc031d2013-06-19 13:06:23 +08001036 self.stop(root, reason='Clearing test state')
Jon Salz4712ac72013-02-07 17:12:05 +08001037 for f in root.walk():
1038 if f.is_leaf():
1039 f.update_state(status=TestState.UNTESTED)
1040
Jon Salz6dc031d2013-06-19 13:06:23 +08001041 def abort_active_tests(self, reason=None):
1042 self.kill_active_tests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +08001043
1044 def main(self):
Jon Salzeff94182013-06-19 15:06:28 +08001045 syslog.openlog('goofy')
1046
Jon Salz0697cbf2012-07-04 15:14:04 +08001047 try:
1048 self.init()
1049 self.event_log.Log('goofy_init',
1050 success=True)
1051 except:
1052 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001053 try:
Jon Salz0697cbf2012-07-04 15:14:04 +08001054 self.event_log.Log('goofy_init',
1055 success=False,
1056 trace=traceback.format_exc())
1057 except: # pylint: disable=W0702
1058 pass
1059 raise
1060
Jon Salzeff94182013-06-19 15:06:28 +08001061 syslog.syslog('Goofy (factory test harness) starting')
Jon Salz0697cbf2012-07-04 15:14:04 +08001062 self.run()
1063
1064 def update_system_info(self):
1065 '''Updates system info.'''
1066 system_info = system.SystemInfo()
1067 self.state_instance.set_shared_data('system_info', system_info.__dict__)
1068 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
1069 system_info=system_info.__dict__))
1070 logging.info('System info: %r', system_info.__dict__)
1071
Jon Salzeb42f0d2012-07-27 19:14:04 +08001072 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
1073 '''Commences updating factory software.
1074
1075 Args:
1076 auto_run_on_restart: Auto-run when the machine comes back up.
1077 post_update_hook: Code to call after update but immediately before
1078 restart.
1079
1080 Returns:
1081 Never if the update was successful (we just reboot).
1082 False if the update was unnecessary (no update available).
1083 '''
Jon Salz6dc031d2013-06-19 13:06:23 +08001084 self.kill_active_tests(False, reason='Factory software update')
Jon Salza6711d72012-07-18 14:33:03 +08001085 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001086
Jon Salz5c344f62012-07-13 14:31:16 +08001087 def pre_update_hook():
1088 if auto_run_on_restart:
1089 self.state_instance.set_shared_data('tests_after_shutdown',
1090 FORCE_AUTO_RUN)
1091 self.state_instance.close()
1092
Jon Salzeb42f0d2012-07-27 19:14:04 +08001093 if updater.TryUpdate(pre_update_hook=pre_update_hook):
1094 if post_update_hook:
1095 post_update_hook()
1096 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +08001097
Jon Salzcef132a2012-08-30 04:58:08 +08001098 def handle_sigint(self, dummy_signum, dummy_frame):
Jon Salz77c151e2012-08-28 07:20:37 +08001099 logging.error('Received SIGINT')
1100 self.run_queue.put(None)
1101 raise KeyboardInterrupt()
1102
Jon Salze12c2b32013-06-25 16:24:34 +08001103 def find_kcrashes(self):
1104 """Finds kcrash files, logs them, and marks them as seen."""
1105 seen_crashes = set(
1106 self.state_instance.get_shared_data('seen_crashes', optional=True)
1107 or [])
1108
1109 for path in glob.glob('/var/spool/crash/*'):
1110 if not os.path.isfile(path):
1111 continue
1112 if path in seen_crashes:
1113 continue
1114 try:
1115 stat = os.stat(path)
1116 mtime = utils.TimeString(stat.st_mtime)
1117 logging.info(
1118 'Found new crash file %s (%d bytes at %s)',
1119 path, stat.st_size, mtime)
1120 extra_log_args = {}
1121
1122 try:
1123 _, ext = os.path.splitext(path)
1124 if ext in ['.kcrash', '.meta']:
1125 ext = ext.replace('.', '')
1126 with open(path) as f:
1127 data = f.read(MAX_CRASH_FILE_SIZE)
1128 tell = f.tell()
1129 logging.info(
1130 'Contents of %s%s:%s',
1131 path,
1132 ('' if tell == stat.st_size
1133 else '(truncated to %d bytes)' % MAX_CRASH_FILE_SIZE),
1134 ('\n' + data).replace('\n', '\n ' + ext + '> '))
1135 extra_log_args['data'] = data
1136
1137 # Copy to /var/factory/kcrash for posterity
1138 kcrash_dir = factory.get_factory_root('kcrash')
1139 utils.TryMakeDirs(kcrash_dir)
1140 shutil.copy(path, kcrash_dir)
1141 logging.info('Copied to %s',
1142 os.path.join(kcrash_dir, os.path.basename(path)))
1143 finally:
1144 # Even if something goes wrong with the above, still try to
1145 # log to event log
1146 self.event_log.Log('crash_file',
1147 path=path, size=stat.st_size, mtime=mtime,
1148 **extra_log_args)
1149 except: # pylint: disable=W0702
1150 logging.exception('Unable to handle crash files %s', path)
1151 seen_crashes.add(path)
1152
1153 self.state_instance.set_shared_data('seen_crashes', list(seen_crashes))
1154
Jon Salz0697cbf2012-07-04 15:14:04 +08001155 def init(self, args=None, env=None):
1156 '''Initializes Goofy.
1157
1158 Args:
1159 args: A list of command-line arguments. Uses sys.argv if
1160 args is None.
1161 env: An Environment instance to use (or None to choose
1162 FakeChrootEnvironment or DUTEnvironment as appropriate).
1163 '''
Jon Salz77c151e2012-08-28 07:20:37 +08001164 signal.signal(signal.SIGINT, self.handle_sigint)
1165
Jon Salz0697cbf2012-07-04 15:14:04 +08001166 parser = OptionParser()
1167 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001168 action='store_true',
1169 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001170 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001171 metavar='FILE',
1172 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001173 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001174 action='store_true',
1175 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001176 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +08001177 choices=['none', 'gtk', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001178 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001179 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001180 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001181 type='int', default=1,
1182 help=('Factor by which to scale UI '
1183 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001184 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001185 metavar='FILE',
1186 help='Use FILE as test list')
Jon Salzc79a9982012-08-30 04:42:01 +08001187 parser.add_option('--dummy_shopfloor', action='store_true',
1188 help='Use a dummy shopfloor server')
chungyiafe8f772012-08-15 19:36:29 +08001189 parser.add_option('--automation', dest='automation',
1190 action='store_true',
1191 help='Enable automation on running factory test')
Ricky Liang09216dc2013-02-22 17:26:45 +08001192 parser.add_option('--one_pixel_less', dest='one_pixel_less',
1193 action='store_true',
1194 help=('Start Chrome one pixel less than the full screen.'
1195 'Needed by Exynos platform to run GTK.'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001196 (self.options, self.args) = parser.parse_args(args)
1197
Jon Salz46b89562012-07-05 11:49:22 +08001198 # Make sure factory directories exist.
1199 factory.get_log_root()
1200 factory.get_state_root()
1201 factory.get_test_data_root()
1202
Jon Salz0697cbf2012-07-04 15:14:04 +08001203 global _inited_logging # pylint: disable=W0603
1204 if not _inited_logging:
1205 factory.init_logging('goofy', verbose=self.options.verbose)
1206 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001207
Jon Salz0f996602012-10-03 15:26:48 +08001208 if self.options.print_test_list:
1209 print factory.read_test_list(
1210 self.options.print_test_list).__repr__(recursive=True)
1211 sys.exit(0)
1212
Jon Salzee85d522012-07-17 14:34:46 +08001213 event_log.IncrementBootSequence()
Jon Salzd15bbcf2013-05-21 17:33:57 +08001214 # Don't defer logging the initial event, so we can make sure
1215 # that device_id, reimage_id, etc. are all set up.
1216 self.event_log = EventLog('goofy', defer=False)
Jon Salz0697cbf2012-07-04 15:14:04 +08001217
1218 if (not suppress_chroot_warning and
1219 factory.in_chroot() and
1220 self.options.ui == 'gtk' and
1221 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
1222 # That's not going to work! Tell the user how to run
1223 # this way.
1224 logging.warn(GOOFY_IN_CHROOT_WARNING)
1225 time.sleep(1)
1226
1227 if env:
1228 self.env = env
1229 elif factory.in_chroot():
1230 self.env = test_environment.FakeChrootEnvironment()
1231 logging.warn(
1232 'Using chroot environment: will not actually run autotests')
1233 else:
1234 self.env = test_environment.DUTEnvironment()
1235 self.env.goofy = self
1236
1237 if self.options.restart:
1238 state.clear_state()
1239
Jon Salz0697cbf2012-07-04 15:14:04 +08001240 if self.options.ui_scale_factor != 1 and utils.in_qemu():
1241 logging.warn(
1242 'In QEMU; ignoring ui_scale_factor argument')
1243 self.options.ui_scale_factor = 1
1244
1245 logging.info('Started')
1246
1247 self.start_state_server()
1248 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1249 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001250 self.options.ui_scale_factor)
1251 self.state_instance.set_shared_data('one_pixel_less',
1252 self.options.one_pixel_less)
Jon Salz0697cbf2012-07-04 15:14:04 +08001253 self.last_shutdown_time = (
1254 self.state_instance.get_shared_data('shutdown_time', optional=True))
1255 self.state_instance.del_shared_data('shutdown_time', optional=True)
1256
Jon Salzb19ea072013-02-07 16:35:00 +08001257 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001258 if not self.options.test_list:
1259 self.options.test_list = find_test_list()
Jon Salzb19ea072013-02-07 16:35:00 +08001260 if self.options.test_list:
Jon Salz0697cbf2012-07-04 15:14:04 +08001261 logging.info('Using test list %s', self.options.test_list)
Jon Salzb19ea072013-02-07 16:35:00 +08001262 try:
1263 self.test_list = factory.read_test_list(
1264 self.options.test_list,
1265 self.state_instance)
1266 except: # pylint: disable=W0702
1267 logging.exception('Unable to read test list %r', self.options.test_list)
1268 self.state_instance.set_shared_data('startup_error',
1269 'Unable to read test list %s\n%s' % (
1270 self.options.test_list,
1271 traceback.format_exc()))
1272 else:
1273 logging.error('No test list found.')
1274 self.state_instance.set_shared_data('startup_error',
1275 'No test list found.')
Jon Salz0697cbf2012-07-04 15:14:04 +08001276
Jon Salzb19ea072013-02-07 16:35:00 +08001277 if not self.test_list:
1278 if self.options.ui == 'chrome':
1279 # Create an empty test list with default options so that the rest of
1280 # startup can proceed.
1281 self.test_list = factory.FactoryTestList(
1282 [], self.state_instance, factory.Options())
1283 else:
1284 # Bail with an error; no point in starting up.
1285 sys.exit('No valid test list; exiting.')
1286
Jon Salz822838b2013-03-25 17:32:33 +08001287 if self.test_list.options.clear_state_on_start:
1288 self.state_instance.clear_test_state()
1289
Vic Yang3e1cf5d2013-06-05 18:50:24 +08001290 if system.SystemInfo().firmware_version is None and not utils.in_chroot():
Vic Yang9bd4f772013-06-04 17:34:00 +08001291 self.state_instance.set_shared_data('startup_error',
1292 'Netboot firmware detected\n'
1293 'Connect Ethernet and reboot to re-image.\n'
1294 u'侦测到网路开机固件\n'
1295 u'请连接乙太网并重启')
1296
Jon Salz0697cbf2012-07-04 15:14:04 +08001297 if not self.state_instance.has_shared_data('ui_lang'):
1298 self.state_instance.set_shared_data('ui_lang',
1299 self.test_list.options.ui_lang)
1300 self.state_instance.set_shared_data(
1301 'test_list_options',
1302 self.test_list.options.__dict__)
1303 self.state_instance.test_list = self.test_list
1304
Jon Salz83ef34b2012-11-01 19:46:35 +08001305 if not utils.in_chroot() and self.test_list.options.disable_log_rotation:
1306 open('/var/lib/cleanup_logs_paused', 'w').close()
1307
Jon Salz23926422012-09-01 03:38:13 +08001308 if self.options.dummy_shopfloor:
1309 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1310 'http://localhost:%d/' % shopfloor.DEFAULT_SERVER_PORT)
1311 self.dummy_shopfloor = Spawn(
1312 [os.path.join(factory.FACTORY_PATH, 'bin', 'shopfloor_server'),
1313 '--dummy'])
1314 elif self.test_list.options.shopfloor_server_url:
1315 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001316 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001317
Jon Salz0f996602012-10-03 15:26:48 +08001318 if self.test_list.options.time_sanitizer and not utils.in_chroot():
Jon Salz8fa8e832012-07-13 19:04:09 +08001319 self.time_sanitizer = time_sanitizer.TimeSanitizer(
1320 base_time=time_sanitizer.GetBaseTimeFromFile(
1321 # lsb-factory is written by the factory install shim during
1322 # installation, so it should have a good time obtained from
Jon Salz54882d02012-08-31 01:57:54 +08001323 # the mini-Omaha server. If it's not available, we'll use
1324 # /etc/lsb-factory (which will be much older, but reasonably
1325 # sane) and rely on a shopfloor sync to set a more accurate
1326 # time.
1327 '/usr/local/etc/lsb-factory',
1328 '/etc/lsb-release'))
Jon Salz8fa8e832012-07-13 19:04:09 +08001329 self.time_sanitizer.RunOnce()
1330
Vic Yangd8990da2013-06-27 16:57:43 +08001331 if self.test_list.options.check_cpu_usage_period_secs:
1332 self.cpu_usage_watcher = Spawn(['py/tools/cpu_usage_monitor.py',
1333 '-p', str(self.test_list.options.check_cpu_usage_period_secs)],
1334 cwd=factory.FACTORY_PATH)
1335
Jon Salz0697cbf2012-07-04 15:14:04 +08001336 self.init_states()
1337 self.start_event_server()
1338 self.connection_manager = self.env.create_connection_manager(
Tai-Hsu Lin371351a2012-08-27 14:17:14 +08001339 self.test_list.options.wlans,
1340 self.test_list.options.scan_wifi_period_secs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001341 # Note that we create a log watcher even if
1342 # sync_event_log_period_secs isn't set (no background
1343 # syncing), since we may use it to flush event logs as well.
1344 self.log_watcher = EventLogWatcher(
1345 self.test_list.options.sync_event_log_period_secs,
Jon Salzd15bbcf2013-05-21 17:33:57 +08001346 event_log_db_file=None,
Jon Salz16d10542012-07-23 12:18:45 +08001347 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001348 if self.test_list.options.sync_event_log_period_secs:
1349 self.log_watcher.StartWatchThread()
1350
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001351 # Note that we create a system log manager even if
1352 # sync_log_period_secs isn't set (no background
1353 # syncing), since we may kick it to sync logs in its
1354 # thread.
1355 self.system_log_manager = SystemLogManager(
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +08001356 sync_log_paths=self.test_list.options.sync_log_paths,
1357 sync_period_sec=self.test_list.options.sync_log_period_secs,
1358 clear_log_paths=self.test_list.options.clear_log_paths)
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001359 self.system_log_manager.StartSyncThread()
1360
Jon Salz0697cbf2012-07-04 15:14:04 +08001361 self.update_system_info()
1362
Vic Yang4953fc12012-07-26 16:19:53 +08001363 assert ((self.test_list.options.min_charge_pct is None) ==
1364 (self.test_list.options.max_charge_pct is None))
Vic Yange83d9a12013-04-19 20:00:20 +08001365 if utils.in_chroot():
1366 logging.info('In chroot, ignoring charge manager and charge state')
1367 elif self.test_list.options.min_charge_pct is not None:
Vic Yang4953fc12012-07-26 16:19:53 +08001368 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1369 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001370 system.SystemStatus.charge_manager = self.charge_manager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +08001371 else:
1372 # Goofy should set charger state to charge if charge_manager is disabled.
1373 try:
1374 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
1375 except BoardException:
1376 logging.exception('Unable to set charge state on this board')
Vic Yang4953fc12012-07-26 16:19:53 +08001377
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001378 self.core_dump_manager = CoreDumpManager(
1379 self.test_list.options.core_dump_watchlist)
1380
Jon Salz0697cbf2012-07-04 15:14:04 +08001381 os.environ['CROS_FACTORY'] = '1'
1382 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1383
1384 # Set CROS_UI since some behaviors in ui.py depend on the
1385 # particular UI in use. TODO(jsalz): Remove this (and all
1386 # places it is used) when the GTK UI is removed.
1387 os.environ['CROS_UI'] = self.options.ui
1388
Jon Salz416f9cc2013-05-10 18:32:50 +08001389 # Initialize hooks.
1390 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1391 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
1392 assert isinstance(self.hooks, factory.Hooks), (
1393 "hooks should be of type Hooks but is %r" % type(self.hooks))
1394 self.hooks.test_list = self.test_list
1395
Jon Salzce6a7f82013-06-10 18:22:54 +08001396 if not utils.in_chroot():
Jon Salzddf0d052013-06-18 12:52:44 +08001397 self.cpufreq_manager = CpufreqManager(event_log=self.event_log)
Jon Salzce6a7f82013-06-10 18:22:54 +08001398
Jon Salz416f9cc2013-05-10 18:32:50 +08001399 # Call startup hook.
1400 self.hooks.OnStartup()
1401
Jon Salze12c2b32013-06-25 16:24:34 +08001402 self.find_kcrashes()
1403
Jon Salz0697cbf2012-07-04 15:14:04 +08001404 if self.options.ui == 'chrome':
1405 self.env.launch_chrome()
1406 logging.info('Waiting for a web socket connection')
Cheng-Yi Chiangfd8ed392013-03-08 21:37:31 +08001407 self.web_socket_manager.wait()
Jon Salz0697cbf2012-07-04 15:14:04 +08001408
1409 # Wait for the test widget size to be set; this is done in
1410 # an asynchronous RPC so there is a small chance that the
1411 # web socket might be opened first.
1412 for _ in range(100): # 10 s
1413 try:
1414 if self.state_instance.get_shared_data('test_widget_size'):
1415 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001416 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001417 pass # Retry
1418 time.sleep(0.1) # 100 ms
1419 else:
1420 logging.warn('Never received test_widget_size from UI')
Jon Salz45297282013-05-18 14:31:47 +08001421
1422 # Send Chrome a Tab to get focus to the factory UI
1423 # (http://crosbug.com/p/19444). TODO(jsalz): remove this hack
1424 # and figure out the right way to get the focus to Chrome.
1425 if not utils.in_chroot():
1426 Spawn(
1427 [os.path.join(factory.FACTORY_PATH, 'bin', 'send_key'), 'Tab'],
1428 check_call=True, log=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001429 elif self.options.ui == 'gtk':
1430 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001431
Ricky Liang650f6bf2012-09-28 13:22:54 +08001432 # Create download path for autotest beforehand or autotests run at
1433 # the same time might fail due to race condition.
1434 if not factory.in_chroot():
1435 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1436 'download'))
1437
Jon Salz0697cbf2012-07-04 15:14:04 +08001438 def state_change_callback(test, test_state):
1439 self.event_client.post_event(
1440 Event(Event.Type.STATE_CHANGE,
1441 path=test.path, state=test_state))
1442 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001443
Jon Salza6711d72012-07-18 14:33:03 +08001444 for handler in self.on_ui_startup:
1445 handler()
1446
1447 self.prespawner = Prespawner()
1448 self.prespawner.start()
1449
Jon Salz0697cbf2012-07-04 15:14:04 +08001450 try:
1451 tests_after_shutdown = self.state_instance.get_shared_data(
1452 'tests_after_shutdown')
1453 except KeyError:
1454 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001455
Jon Salz5c344f62012-07-13 14:31:16 +08001456 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1457 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001458 logging.info('Resuming tests after shutdown: %s',
1459 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001460 self.tests_to_run.extend(
1461 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1462 self.run_queue.put(self.run_next_test)
1463 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001464 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001465 self.run_queue.put(
1466 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001467 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001468
Dean Liao592e4d52013-01-10 20:06:39 +08001469 self.may_disable_cros_shortcut_keys()
1470
1471 def may_disable_cros_shortcut_keys(self):
1472 test_options = self.test_list.options
1473 if test_options.disable_cros_shortcut_keys:
1474 logging.info('Filter ChromeOS shortcut keys.')
1475 self.key_filter = KeyFilter(
1476 unmap_caps_lock=test_options.disable_caps_lock,
1477 caps_lock_keycode=test_options.caps_lock_keycode)
1478 self.key_filter.Start()
1479
Jon Salz0697cbf2012-07-04 15:14:04 +08001480 def run(self):
1481 '''Runs Goofy.'''
1482 # Process events forever.
1483 while self.run_once(True):
1484 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001485
Jon Salz0697cbf2012-07-04 15:14:04 +08001486 def run_once(self, block=False):
1487 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001488
Jon Salz0697cbf2012-07-04 15:14:04 +08001489 Args:
1490 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001491
Jon Salz0697cbf2012-07-04 15:14:04 +08001492 Returns:
1493 True to keep going or False to shut down.
1494 '''
1495 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001496 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001497 # Nothing on the run queue.
1498 self._run_queue_idle()
1499 if block:
1500 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001501 try:
1502 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1503 except Queue.Empty:
1504 # Keep going (calling _run_queue_idle() again at the top of
1505 # the loop)
1506 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001507 # ...and grab anything else that showed up at the same
1508 # time.
1509 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001510 else:
1511 break
Jon Salz51528e12012-07-02 18:54:45 +08001512
Jon Salz0697cbf2012-07-04 15:14:04 +08001513 for event in events:
1514 if not event:
1515 # Shutdown request.
1516 self.run_queue.task_done()
1517 return False
Jon Salz51528e12012-07-02 18:54:45 +08001518
Jon Salz0697cbf2012-07-04 15:14:04 +08001519 try:
1520 event()
Jon Salz85a39882012-07-05 16:45:04 +08001521 except: # pylint: disable=W0702
1522 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001523 self.record_exception(traceback.format_exception_only(
1524 *sys.exc_info()[:2]))
1525 # But keep going
1526 finally:
1527 self.run_queue.task_done()
1528 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001529
Jon Salz0e6532d2012-10-25 16:30:11 +08001530 def _should_sync_time(self, foreground=False):
1531 '''Returns True if we should attempt syncing time with shopfloor.
1532
1533 Args:
1534 foreground: If True, synchronizes even if background syncing
1535 is disabled (e.g., in explicit sync requests from the
1536 SyncShopfloor test).
1537 '''
1538 return ((foreground or
1539 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001540 self.time_sanitizer and
1541 (not self.time_synced) and
1542 (not factory.in_chroot()))
1543
Jon Salz0e6532d2012-10-25 16:30:11 +08001544 def sync_time_with_shopfloor_server(self, foreground=False):
Jon Salz54882d02012-08-31 01:57:54 +08001545 '''Syncs time with shopfloor server, if not yet synced.
1546
Jon Salz0e6532d2012-10-25 16:30:11 +08001547 Args:
1548 foreground: If True, synchronizes even if background syncing
1549 is disabled (e.g., in explicit sync requests from the
1550 SyncShopfloor test).
1551
Jon Salz54882d02012-08-31 01:57:54 +08001552 Returns:
1553 False if no time sanitizer is available, or True if this sync (or a
1554 previous sync) succeeded.
1555
1556 Raises:
1557 Exception if unable to contact the shopfloor server.
1558 '''
Jon Salz0e6532d2012-10-25 16:30:11 +08001559 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001560 self.time_sanitizer.SyncWithShopfloor()
1561 self.time_synced = True
1562 return self.time_synced
1563
Jon Salzb92c5112012-09-21 15:40:11 +08001564 def log_disk_space_stats(self):
Jon Salz18e0e022013-06-11 17:13:39 +08001565 if (utils.in_chroot() or
1566 not self.test_list.options.log_disk_space_period_secs):
Jon Salzb92c5112012-09-21 15:40:11 +08001567 return
1568
1569 now = time.time()
1570 if (self.last_log_disk_space_time and
1571 now - self.last_log_disk_space_time <
1572 self.test_list.options.log_disk_space_period_secs):
1573 return
1574 self.last_log_disk_space_time = now
1575
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001576 # Upload event if stateful partition usage is above threshold.
1577 # Stateful partition is mounted on /usr/local, while
1578 # encrypted stateful partition is mounted on /var.
1579 # If there are too much logs in the factory process,
1580 # these two partitions might get full.
Jon Salzb92c5112012-09-21 15:40:11 +08001581 try:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001582 vfs_infos = disk_space.GetAllVFSInfo()
1583 stateful_info, encrypted_info = None, None
1584 for vfs_info in vfs_infos.values():
1585 if '/usr/local' in vfs_info.mount_points:
1586 stateful_info = vfs_info
1587 if '/var' in vfs_info.mount_points:
1588 encrypted_info = vfs_info
1589
1590 stateful = disk_space.GetPartitionUsage(stateful_info)
1591 encrypted = disk_space.GetPartitionUsage(encrypted_info)
1592
1593 above_threshold = (
1594 self.test_list.options.stateful_usage_threshold and
1595 max(stateful.bytes_used_pct,
1596 stateful.inodes_used_pct,
1597 encrypted.bytes_used_pct,
1598 encrypted.inodes_used_pct) >
1599 self.test_list.options.stateful_usage_threshold)
1600
1601 if above_threshold:
1602 self.event_log.Log('stateful_partition_usage',
1603 partitions={
1604 'stateful': {
1605 'bytes_used_pct': FloatDigit(stateful.bytes_used_pct, 2),
1606 'inodes_used_pct': FloatDigit(stateful.inodes_used_pct, 2)},
1607 'encrypted_stateful': {
1608 'bytes_used_pct': FloatDigit(encrypted.bytes_used_pct, 2),
1609 'inodes_used_pct': FloatDigit(encrypted.inodes_used_pct, 2)}
1610 })
1611 self.log_watcher.ScanEventLogs()
Cheng-Yi Chiang00798e72013-06-20 18:16:39 +08001612 if (not utils.in_chroot() and
1613 self.test_list.options.stateful_usage_above_threshold_action):
1614 Spawn(self.test_list.options.stateful_usage_above_threshold_action,
1615 call=True)
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001616
1617 message = disk_space.FormatSpaceUsedAll(vfs_infos)
Jon Salz3c493bb2013-02-07 17:24:58 +08001618 if message != self.last_log_disk_space_message:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001619 if above_threshold:
1620 logging.warning(message)
1621 else:
1622 logging.info(message)
Jon Salz3c493bb2013-02-07 17:24:58 +08001623 self.last_log_disk_space_message = message
Jon Salzb92c5112012-09-21 15:40:11 +08001624 except: # pylint: disable=W0702
1625 logging.exception('Unable to get disk space used')
1626
Justin Chuang83813982013-05-13 01:26:32 +08001627 def check_battery(self):
1628 '''Checks the current battery status.
1629
1630 Logs current battery charging level and status to log. If the battery level
1631 is lower below warning_low_battery_pct, send warning event to shopfloor.
1632 If the battery level is lower below critical_low_battery_pct, flush disks.
1633 '''
1634 if not self.test_list.options.check_battery_period_secs:
1635 return
1636
1637 now = time.time()
1638 if (self.last_check_battery_time and
1639 now - self.last_check_battery_time <
1640 self.test_list.options.check_battery_period_secs):
1641 return
1642 self.last_check_battery_time = now
1643
1644 message = ''
1645 log_level = logging.INFO
1646 try:
1647 power = system.GetBoard().power
1648 if not power.CheckBatteryPresent():
1649 message = 'Battery is not present'
1650 else:
1651 ac_present = power.CheckACPresent()
1652 charge_pct = power.GetChargePct(get_float=True)
1653 message = ('Current battery level %.1f%%, AC charger is %s' %
1654 (charge_pct, 'connected' if ac_present else 'disconnected'))
1655
1656 if charge_pct > self.test_list.options.critical_low_battery_pct:
1657 critical_low_battery = False
1658 else:
1659 critical_low_battery = True
1660 # Only sync disks when battery level is still above minimum
1661 # value. This can be used for offline analysis when shopfloor cannot
1662 # be connected.
1663 if charge_pct > MIN_BATTERY_LEVEL_FOR_DISK_SYNC:
1664 logging.warning('disk syncing for critical low battery situation')
1665 os.system('sync; sync; sync')
1666 else:
1667 logging.warning('disk syncing is cancelled '
1668 'because battery level is lower than %.1f',
1669 MIN_BATTERY_LEVEL_FOR_DISK_SYNC)
1670
1671 # Notify shopfloor server
1672 if (critical_low_battery or
1673 (not ac_present and
1674 charge_pct <= self.test_list.options.warning_low_battery_pct)):
1675 log_level = logging.WARNING
1676
1677 self.event_log.Log('low_battery',
1678 battery_level=charge_pct,
1679 charger_connected=ac_present,
1680 critical=critical_low_battery)
1681 self.log_watcher.KickWatchThread()
1682 self.system_log_manager.KickSyncThread()
1683 except: # pylint: disable=W0702
1684 logging.exception('Unable to check battery or notify shopfloor')
1685 finally:
1686 if message != self.last_check_battery_message:
1687 logging.log(log_level, message)
1688 self.last_check_battery_message = message
1689
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001690 def check_core_dump(self):
1691 '''Checks if there is any core dumped file.
1692
1693 Removes unwanted core dump files immediately.
1694 Syncs those files matching watch list to server with a delay between
1695 each sync. After the files have been synced to server, deletes the files.
1696 '''
1697 core_dump_files = self.core_dump_manager.ScanFiles()
1698 if core_dump_files:
1699 now = time.time()
1700 if (self.last_kick_sync_time and now - self.last_kick_sync_time <
1701 self.test_list.options.kick_sync_min_interval_secs):
1702 return
1703 self.last_kick_sync_time = now
1704
1705 # Sends event to server
1706 self.event_log.Log('core_dumped', files=core_dump_files)
1707 self.log_watcher.KickWatchThread()
1708
1709 # Syncs files to server
1710 self.system_log_manager.KickSyncThread(
1711 core_dump_files, self.core_dump_manager.ClearFiles)
1712
Jon Salz8fa8e832012-07-13 19:04:09 +08001713 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001714 '''Writes out current time and tries to sync with shopfloor server.'''
1715 if not self.time_sanitizer:
1716 return
1717
1718 # Write out the current time.
1719 self.time_sanitizer.SaveTime()
1720
Jon Salz54882d02012-08-31 01:57:54 +08001721 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001722 return
1723
1724 now = time.time()
1725 if self.last_sync_time and (
1726 now - self.last_sync_time <
1727 self.test_list.options.sync_time_period_secs):
1728 # Not yet time for another check.
1729 return
1730 self.last_sync_time = now
1731
1732 def target():
1733 try:
Jon Salz54882d02012-08-31 01:57:54 +08001734 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001735 except: # pylint: disable=W0702
1736 # Oh well. Log an error (but no trace)
1737 logging.info(
1738 'Unable to get time from shopfloor server: %s',
1739 utils.FormatExceptionOnly())
1740
1741 thread = threading.Thread(target=target)
1742 thread.daemon = True
1743 thread.start()
1744
Jon Salz0697cbf2012-07-04 15:14:04 +08001745 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001746 '''Invoked when the run queue has no events.
1747
1748 This method must not raise exception.
1749 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001750 now = time.time()
1751 if (self.last_idle and
1752 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1753 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1754 # 1) seconds.
1755 return
1756
1757 self.last_idle = now
1758
Vic Yang311ddb82012-09-26 12:08:28 +08001759 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001760 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001761 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001762 self.log_disk_space_stats()
Justin Chuang83813982013-05-13 01:26:32 +08001763 self.check_battery()
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001764 self.check_core_dump()
Jon Salz57717ca2012-04-04 16:47:25 +08001765
Jon Salzd15bbcf2013-05-21 17:33:57 +08001766 def handle_event_logs(self, chunks):
Jon Salz0697cbf2012-07-04 15:14:04 +08001767 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001768
Jon Salz0697cbf2012-07-04 15:14:04 +08001769 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001770
1771 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001772 chunks: A list of Chunk objects.
Jon Salz0697cbf2012-07-04 15:14:04 +08001773 '''
Vic Yang93027612013-05-06 02:42:49 +08001774 first_exception = None
1775 exception_count = 0
1776
Jon Salzd15bbcf2013-05-21 17:33:57 +08001777 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001778 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001779 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001780 start_time = time.time()
1781 shopfloor_client = shopfloor.get_instance(
1782 detect=True,
1783 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzd15bbcf2013-05-21 17:33:57 +08001784 shopfloor_client.UploadEvent(chunk.log_name + "." +
1785 event_log.GetReimageId(),
1786 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001787 logging.info(
1788 'Successfully synced %s in %.03f s',
1789 description, time.time() - start_time)
1790 except: # pylint: disable=W0702
Jon Salzd15bbcf2013-05-21 17:33:57 +08001791 first_exception = (first_exception or (chunk.log_name + ': ' +
Vic Yang93027612013-05-06 02:42:49 +08001792 utils.FormatExceptionOnly()))
1793 exception_count += 1
1794
1795 if exception_count:
1796 if exception_count == 1:
1797 msg = 'Log upload failed: %s' % first_exception
1798 else:
1799 msg = '%d log upload failed; first is: %s' % (
1800 exception_count, first_exception)
1801 raise Exception(msg)
1802
Jon Salz57717ca2012-04-04 16:47:25 +08001803
Jon Salz0697cbf2012-07-04 15:14:04 +08001804 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1805 root=None):
1806 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001807
Jon Salz0697cbf2012-07-04 15:14:04 +08001808 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001809
Jon Salz0697cbf2012-07-04 15:14:04 +08001810 Args:
1811 starting_at: If provided, only auto-runs tests beginning with
1812 this test.
1813 '''
1814 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001815
Jon Salz0697cbf2012-07-04 15:14:04 +08001816 if starting_at:
1817 # Make sure they passed a test, not a string.
1818 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001819
Jon Salz0697cbf2012-07-04 15:14:04 +08001820 tests_to_reset = []
1821 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001822
Jon Salz0697cbf2012-07-04 15:14:04 +08001823 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001824
Jon Salz0697cbf2012-07-04 15:14:04 +08001825 for test in root.get_top_level_tests():
1826 if starting_at:
1827 if test == starting_at:
1828 # We've found starting_at; do auto-run on all
1829 # subsequent tests.
1830 found_starting_at = True
1831 if not found_starting_at:
1832 # Don't start this guy yet
1833 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001834
Jon Salz0697cbf2012-07-04 15:14:04 +08001835 status = test.get_state().status
1836 if status == TestState.ACTIVE or status in statuses_to_run:
1837 # Reset the test (later; we will need to abort
1838 # all active tests first).
1839 tests_to_reset.append(test)
1840 if status in statuses_to_run:
1841 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001842
Jon Salz6dc031d2013-06-19 13:06:23 +08001843 self.abort_active_tests('Operator requested run/re-run of certain tests')
Jon Salz258a40c2012-04-19 12:34:01 +08001844
Jon Salz0697cbf2012-07-04 15:14:04 +08001845 # Reset all statuses of the tests to run (in case any tests were active;
1846 # we want them to be run again).
1847 for test_to_reset in tests_to_reset:
1848 for test in test_to_reset.walk():
1849 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001850
Jon Salz0697cbf2012-07-04 15:14:04 +08001851 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001852
Jon Salz0697cbf2012-07-04 15:14:04 +08001853 def restart_tests(self, root=None):
1854 '''Restarts all tests.'''
1855 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001856
Jon Salz6dc031d2013-06-19 13:06:23 +08001857 self.abort_active_tests('Operator requested restart of certain tests')
Jon Salz0697cbf2012-07-04 15:14:04 +08001858 for test in root.walk():
1859 test.update_state(status=TestState.UNTESTED)
1860 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001861
Jon Salz0697cbf2012-07-04 15:14:04 +08001862 def auto_run(self, starting_at=None, root=None):
1863 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001864
Jon Salz0697cbf2012-07-04 15:14:04 +08001865 Args:
1866 starting_at: If provide, only auto-runs tests beginning with
1867 this test.
1868 '''
1869 root = root or self.test_list
1870 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1871 starting_at=starting_at,
1872 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001873
Jon Salz0697cbf2012-07-04 15:14:04 +08001874 def re_run_failed(self, root=None):
1875 '''Re-runs failed tests.'''
1876 root = root or self.test_list
1877 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001878
Jon Salz0697cbf2012-07-04 15:14:04 +08001879 def show_review_information(self):
1880 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001881
Jon Salz0697cbf2012-07-04 15:14:04 +08001882 The information screene is rendered by main UI program (ui.py), so in
1883 goofy we only need to kill all active tests, set them as untested, and
1884 clear remaining tests.
1885 '''
1886 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001887 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001888
Jon Salz0697cbf2012-07-04 15:14:04 +08001889 def handle_switch_test(self, event):
1890 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001891
Jon Salz0697cbf2012-07-04 15:14:04 +08001892 @param event: The SWITCH_TEST event.
1893 '''
1894 test = self.test_list.lookup_path(event.path)
1895 if not test:
1896 logging.error('Unknown test %r', event.key)
1897 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001898
Jon Salz0697cbf2012-07-04 15:14:04 +08001899 invoc = self.invocations.get(test)
1900 if invoc and test.backgroundable:
1901 # Already running: just bring to the front if it
1902 # has a UI.
1903 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001904 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001905 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001906
Jon Salz6dc031d2013-06-19 13:06:23 +08001907 self.abort_active_tests('Operator requested abort (switch_test)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001908 for t in test.walk():
1909 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001910
Jon Salz0697cbf2012-07-04 15:14:04 +08001911 if self.test_list.options.auto_run_on_keypress:
1912 self.auto_run(starting_at=test)
1913 else:
1914 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001915
Jon Salz0697cbf2012-07-04 15:14:04 +08001916 def wait(self):
1917 '''Waits for all pending invocations.
1918
1919 Useful for testing.
1920 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001921 while self.invocations:
1922 for k, v in self.invocations.iteritems():
1923 logging.info('Waiting for %s to complete...', k)
1924 v.thread.join()
1925 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001926
1927 def check_exceptions(self):
1928 '''Raises an error if any exceptions have occurred in
1929 invocation threads.'''
1930 if self.exceptions:
1931 raise RuntimeError('Exception in invocation thread: %r' %
1932 self.exceptions)
1933
1934 def record_exception(self, msg):
1935 '''Records an exception in an invocation thread.
1936
1937 An exception with the given message will be rethrown when
1938 Goofy is destroyed.'''
1939 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001940
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001941
1942if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001943 goofy = Goofy()
1944 try:
1945 goofy.main()
Jon Salz0f996602012-10-03 15:26:48 +08001946 except SystemExit:
1947 # Propagate SystemExit without logging.
1948 raise
Jon Salz31373eb2012-09-21 16:19:49 +08001949 except:
Jon Salz0f996602012-10-03 15:26:48 +08001950 # Log the error before trying to shut down (unless it's a graceful
1951 # exit).
Jon Salz31373eb2012-09-21 16:19:49 +08001952 logging.exception('Error in main loop')
1953 raise
Jon Salz77c151e2012-08-28 07:20:37 +08001954 finally:
1955 goofy.destroy()