blob: 4f2cb2820438b681a3fbef8d7baf97fd72b3d07c [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
Jon Salz128b0932013-07-03 16:55:26 +080050from cros.factory.test.test_lists import test_lists
Jon Salz83591782012-06-26 11:09:58 +080051from cros.factory.test.event import Event
52from cros.factory.test.event import EventClient
53from cros.factory.test.event import EventServer
jcliangcd688182012-08-20 21:01:26 +080054from cros.factory.test.factory import TestState
Jon Salzd7550792013-07-12 05:49:27 +080055from cros.factory.test.utils import Enum
Dean Liao592e4d52013-01-10 20:06:39 +080056from cros.factory.tools.key_filter import KeyFilter
Jon Salz2af235d2013-06-24 14:47:21 +080057from cros.factory.utils import file_utils
Jon Salz78c32392012-07-25 14:18:29 +080058from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080059
60
Hung-Te Linf2f78f72012-02-08 19:27:11 +080061HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Chun-ta Lin279e7e92013-02-19 17:40:39 +080062CACHES_DIR = os.path.join(factory.get_state_root(), "caches")
Hung-Te Linf2f78f72012-02-08 19:27:11 +080063
Jon Salz8796e362012-05-24 11:39:09 +080064# File that suppresses reboot if present (e.g., for development).
65NO_REBOOT_FILE = '/var/log/factory.noreboot'
66
Jon Salz5c344f62012-07-13 14:31:16 +080067# Value for tests_after_shutdown that forces auto-run (e.g., after
68# a factory update, when the available set of tests might change).
69FORCE_AUTO_RUN = 'force_auto_run'
70
cychiang21886742012-07-05 15:16:32 +080071RUN_QUEUE_TIMEOUT_SECS = 10
72
Justin Chuang83813982013-05-13 01:26:32 +080073# Sync disks when battery level is higher than this value.
74# Otherwise, power loss during disk sync operation may incur even worse outcome.
75MIN_BATTERY_LEVEL_FOR_DISK_SYNC = 1.0
76
Jon Salze12c2b32013-06-25 16:24:34 +080077MAX_CRASH_FILE_SIZE = 64*1024
78
Jon Salz758e6cc2012-04-03 15:47:07 +080079GOOFY_IN_CHROOT_WARNING = '\n' + ('*' * 70) + '''
80You are running Goofy inside the chroot. Autotests are not supported.
81
82To use Goofy in the chroot, first install an Xvnc server:
83
Jon Salz0697cbf2012-07-04 15:14:04 +080084 sudo apt-get install tightvncserver
Jon Salz758e6cc2012-04-03 15:47:07 +080085
86...and then start a VNC X server outside the chroot:
87
Jon Salz0697cbf2012-07-04 15:14:04 +080088 vncserver :10 &
89 vncviewer :10
Jon Salz758e6cc2012-04-03 15:47:07 +080090
91...and run Goofy as follows:
92
Jon Salz0697cbf2012-07-04 15:14:04 +080093 env --unset=XAUTHORITY DISPLAY=localhost:10 python goofy.py
Jon Salz758e6cc2012-04-03 15:47:07 +080094''' + ('*' * 70)
Jon Salz73e0fd02012-04-04 11:46:38 +080095suppress_chroot_warning = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080096
Jon Salzd7550792013-07-12 05:49:27 +080097Status = Enum(['UNINITIALIZED', 'INITIALIZING', 'RUNNING',
98 'TERMINATING', 'TERMINATED'])
99
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800100def get_hwid_cfg():
Jon Salz0697cbf2012-07-04 15:14:04 +0800101 '''
102 Returns the HWID config tag, or an empty string if none can be found.
103 '''
104 if 'CROS_HWID' in os.environ:
105 return os.environ['CROS_HWID']
106 if os.path.exists(HWID_CFG_PATH):
107 with open(HWID_CFG_PATH, 'rt') as hwid_cfg_handle:
108 return hwid_cfg_handle.read().strip()
109 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800110
111
Jon Salz73e0fd02012-04-04 11:46:38 +0800112_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800113
114class Goofy(object):
Jon Salz0697cbf2012-07-04 15:14:04 +0800115 '''
116 The main factory flow.
117
118 Note that all methods in this class must be invoked from the main
119 (event) thread. Other threads, such as callbacks and TestInvocation
120 methods, should instead post events on the run queue.
121
122 TODO: Unit tests. (chrome-os-partner:7409)
123
124 Properties:
125 uuid: A unique UUID for this invocation of Goofy.
126 state_instance: An instance of FactoryState.
127 state_server: The FactoryState XML/RPC server.
128 state_server_thread: A thread running state_server.
129 event_server: The EventServer socket server.
130 event_server_thread: A thread running event_server.
131 event_client: A client to the event server.
132 connection_manager: The connection_manager object.
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800133 system_log_manager: The SystemLogManager object.
134 core_dump_manager: The CoreDumpManager object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800135 ui_process: The factory ui process object.
136 run_queue: A queue of callbacks to invoke from the main thread.
137 invocations: A map from FactoryTest objects to the corresponding
138 TestInvocations objects representing active tests.
139 tests_to_run: A deque of tests that should be run when the current
140 test(s) complete.
141 options: Command-line options.
142 args: Command-line args.
143 test_list: The test list.
Jon Salz128b0932013-07-03 16:55:26 +0800144 test_lists: All new-style test lists.
Jon Salz0697cbf2012-07-04 15:14:04 +0800145 event_handlers: Map of Event.Type to the method used to handle that
146 event. If the method has an 'event' argument, the event is passed
147 to the handler.
148 exceptions: Exceptions encountered in invocation threads.
Jon Salz3c493bb2013-02-07 17:24:58 +0800149 last_log_disk_space_message: The last message we logged about disk space
150 (to avoid duplication).
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800151 last_kick_sync_time: The last time to kick system_log_manager to sync
152 because of core dump files (to avoid kicking too soon then abort the
153 sync.)
Jon Salz416f9cc2013-05-10 18:32:50 +0800154 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salzd7550792013-07-12 05:49:27 +0800155 status: The current Goofy status (a member of the Status enum).
Jon Salz0697cbf2012-07-04 15:14:04 +0800156 '''
157 def __init__(self):
158 self.uuid = str(uuid.uuid4())
159 self.state_instance = None
160 self.state_server = None
161 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800162 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800163 self.event_server = None
164 self.event_server_thread = None
165 self.event_client = None
166 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800167 self.charge_manager = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800168 self.time_sanitizer = None
169 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800170 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800171 self.system_log_manager = None
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800172 self.core_dump_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800173 self.event_log = None
174 self.prespawner = None
175 self.ui_process = None
Jon Salzc79a9982012-08-30 04:42:01 +0800176 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800177 self.run_queue = Queue.Queue()
178 self.invocations = {}
179 self.tests_to_run = deque()
180 self.visible_test = None
181 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800182 self.hooks = None
Vic Yangd8990da2013-06-27 16:57:43 +0800183 self.cpu_usage_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800184
185 self.options = None
186 self.args = None
187 self.test_list = None
Jon Salz128b0932013-07-03 16:55:26 +0800188 self.test_lists = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800189 self.on_ui_startup = []
190 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800191 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800192 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800193 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800194 self.last_sync_time = None
Jon Salzb92c5112012-09-21 15:40:11 +0800195 self.last_log_disk_space_time = None
Jon Salz3c493bb2013-02-07 17:24:58 +0800196 self.last_log_disk_space_message = None
Justin Chuang83813982013-05-13 01:26:32 +0800197 self.last_check_battery_time = None
198 self.last_check_battery_message = None
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800199 self.last_kick_sync_time = None
Vic Yang311ddb82012-09-26 12:08:28 +0800200 self.exclusive_items = set()
Jon Salz0f996602012-10-03 15:26:48 +0800201 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800202 self.key_filter = None
Jon Salzce6a7f82013-06-10 18:22:54 +0800203 self.cpufreq_manager = None
Jon Salzd7550792013-07-12 05:49:27 +0800204 self.status = Status.UNINITIALIZED
Jon Salz0697cbf2012-07-04 15:14:04 +0800205
Jon Salz85a39882012-07-05 16:45:04 +0800206 def test_or_root(event, parent_or_group=True):
207 '''Returns the test affected by a particular event.
208
209 Args:
210 event: The event containing an optional 'path' attribute.
211 parent_on_group: If True, returns the top-level parent for a test (the
212 root node of the tests that need to be run together if the given test
213 path is to be run).
214 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800215 try:
216 path = event.path
217 except AttributeError:
218 path = None
219
220 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800221 test = self.test_list.lookup_path(path)
222 if parent_or_group:
223 test = test.get_top_level_parent_or_group()
224 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800225 else:
226 return self.test_list
227
228 self.event_handlers = {
229 Event.Type.SWITCH_TEST: self.handle_switch_test,
230 Event.Type.SHOW_NEXT_ACTIVE_TEST:
231 lambda event: self.show_next_active_test(),
232 Event.Type.RESTART_TESTS:
233 lambda event: self.restart_tests(root=test_or_root(event)),
234 Event.Type.AUTO_RUN:
235 lambda event: self.auto_run(root=test_or_root(event)),
236 Event.Type.RE_RUN_FAILED:
237 lambda event: self.re_run_failed(root=test_or_root(event)),
238 Event.Type.RUN_TESTS_WITH_STATUS:
239 lambda event: self.run_tests_with_status(
240 event.status,
241 root=test_or_root(event)),
242 Event.Type.REVIEW:
243 lambda event: self.show_review_information(),
244 Event.Type.UPDATE_SYSTEM_INFO:
245 lambda event: self.update_system_info(),
Jon Salz0697cbf2012-07-04 15:14:04 +0800246 Event.Type.STOP:
Jon Salz85a39882012-07-05 16:45:04 +0800247 lambda event: self.stop(root=test_or_root(event, False),
Jon Salz6dc031d2013-06-19 13:06:23 +0800248 fail=getattr(event, 'fail', False),
249 reason=getattr(event, 'reason', None)),
Jon Salz36fbbb52012-07-05 13:45:06 +0800250 Event.Type.SET_VISIBLE_TEST:
251 lambda event: self.set_visible_test(
252 self.test_list.lookup_path(event.path)),
Jon Salz4712ac72013-02-07 17:12:05 +0800253 Event.Type.CLEAR_STATE:
254 lambda event: self.clear_state(self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800255 }
256
257 self.exceptions = []
258 self.web_socket_manager = None
259
260 def destroy(self):
Jon Salzd7550792013-07-12 05:49:27 +0800261 self.status = Status.TERMINATING
Jon Salz0697cbf2012-07-04 15:14:04 +0800262 if self.chrome:
263 self.chrome.kill()
264 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800265 if self.dummy_shopfloor:
266 self.dummy_shopfloor.kill()
267 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800268 if self.ui_process:
269 utils.kill_process_tree(self.ui_process, 'ui')
270 self.ui_process = None
271 if self.web_socket_manager:
272 logging.info('Stopping web sockets')
273 self.web_socket_manager.close()
274 self.web_socket_manager = None
275 if self.state_server_thread:
276 logging.info('Stopping state server')
277 self.state_server.shutdown()
278 self.state_server_thread.join()
279 self.state_server.server_close()
280 self.state_server_thread = None
281 if self.state_instance:
282 self.state_instance.close()
283 if self.event_server_thread:
284 logging.info('Stopping event server')
285 self.event_server.shutdown() # pylint: disable=E1101
286 self.event_server_thread.join()
287 self.event_server.server_close()
288 self.event_server_thread = None
289 if self.log_watcher:
290 if self.log_watcher.IsThreadStarted():
291 self.log_watcher.StopWatchThread()
292 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800293 if self.system_log_manager:
294 if self.system_log_manager.IsThreadRunning():
295 self.system_log_manager.StopSyncThread()
296 self.system_log_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800297 if self.prespawner:
298 logging.info('Stopping prespawner')
299 self.prespawner.stop()
300 self.prespawner = None
301 if self.event_client:
302 logging.info('Closing event client')
303 self.event_client.close()
304 self.event_client = None
Jon Salzddf0d052013-06-18 12:52:44 +0800305 if self.cpufreq_manager:
306 self.cpufreq_manager.Stop()
Jon Salz0697cbf2012-07-04 15:14:04 +0800307 if self.event_log:
308 self.event_log.Close()
309 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800310 if self.key_filter:
311 self.key_filter.Stop()
Vic Yangd8990da2013-06-27 16:57:43 +0800312 if self.cpu_usage_watcher:
313 self.cpu_usage_watcher.terminate()
Dean Liao592e4d52013-01-10 20:06:39 +0800314
Jon Salz0697cbf2012-07-04 15:14:04 +0800315 self.check_exceptions()
316 logging.info('Done destroying Goofy')
Jon Salzd7550792013-07-12 05:49:27 +0800317 self.status = Status.TERMINATED
Jon Salz0697cbf2012-07-04 15:14:04 +0800318
319 def start_state_server(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800320 # Before starting state server, remount stateful partitions with
321 # no commit flag. The default commit time (commit=600) makes corruption
322 # too likely.
323 file_utils.ResetCommitTime()
324
Jon Salz0697cbf2012-07-04 15:14:04 +0800325 self.state_instance, self.state_server = (
326 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800327 self.goofy_rpc = GoofyRPC(self)
328 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800329 logging.info('Starting state server')
330 self.state_server_thread = threading.Thread(
331 target=self.state_server.serve_forever,
332 name='StateServer')
333 self.state_server_thread.start()
334
335 def start_event_server(self):
336 self.event_server = EventServer()
337 logging.info('Starting factory event server')
338 self.event_server_thread = threading.Thread(
339 target=self.event_server.serve_forever,
340 name='EventServer') # pylint: disable=E1101
341 self.event_server_thread.start()
342
343 self.event_client = EventClient(
344 callback=self.handle_event, event_loop=self.run_queue)
345
346 self.web_socket_manager = WebSocketManager(self.uuid)
347 self.state_server.add_handler("/event",
348 self.web_socket_manager.handle_web_socket)
349
350 def start_ui(self):
351 ui_proc_args = [
352 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
353 self.options.test_list]
354 if self.options.verbose:
355 ui_proc_args.append('-v')
356 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800357 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800358 logging.info('Waiting for UI to come up...')
359 self.event_client.wait(
360 lambda event: event.type == Event.Type.UI_READY)
361 logging.info('UI has started')
362
363 def set_visible_test(self, test):
364 if self.visible_test == test:
365 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800366 if test and not test.has_ui:
367 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800368
369 if test:
370 test.update_state(visible=True)
371 if self.visible_test:
372 self.visible_test.update_state(visible=False)
373 self.visible_test = test
374
Jon Salzd4306c82012-11-30 15:16:36 +0800375 def _log_startup_messages(self):
376 '''Logs the tail of var/log/messages and mosys and EC console logs.'''
377 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
378 # for factory-3004.B only. Consolidate and merge back to ToT.
379 if utils.in_chroot():
380 return
381
382 try:
383 var_log_messages = (
384 utils.var_log_messages_before_reboot())
385 logging.info(
386 'Tail of /var/log/messages before last reboot:\n'
387 '%s', ('\n'.join(
388 ' ' + x for x in var_log_messages)))
389 except: # pylint: disable=W0702
390 logging.exception('Unable to grok /var/log/messages')
391
392 try:
393 mosys_log = utils.Spawn(
394 ['mosys', 'eventlog', 'list'],
395 read_stdout=True, log_stderr_on_error=True).stdout_data
396 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
397 except: # pylint: disable=W0702
398 logging.exception('Unable to read mosys eventlog')
399
400 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800401 board = system.GetBoard()
402 ec_console_log = board.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800403 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
404 except: # pylint: disable=W0702
405 logging.exception('Error retrieving EC console log')
406
Vic Yang079f9872013-07-01 11:32:00 +0800407 try:
408 board = system.GetBoard()
409 ec_panic_info = board.GetECPanicInfo()
410 logging.info('EC panic info after reboot:\n%s\n', ec_panic_info)
411 except: # pylint: disable=W0702
412 logging.exception('Error retrieving EC panic info')
413
Jon Salz0697cbf2012-07-04 15:14:04 +0800414 def handle_shutdown_complete(self, test, test_state):
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800415 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800416 Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800417
Jon Salz0697cbf2012-07-04 15:14:04 +0800418 @param test: The ShutdownStep.
419 @param test_state: The test state.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800420 '''
Jon Salz0697cbf2012-07-04 15:14:04 +0800421 test_state = test.update_state(increment_shutdown_count=1)
422 logging.info('Detected shutdown (%d of %d)',
423 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800424
Jon Salz0697cbf2012-07-04 15:14:04 +0800425 def log_and_update_state(status, error_msg, **kw):
426 self.event_log.Log('rebooted',
427 status=status, error_msg=error_msg, **kw)
Jon Salzd4306c82012-11-30 15:16:36 +0800428 logging.info('Rebooted: status=%s, %s', status,
429 (('error_msg=%s' % error_msg) if error_msg else None))
Jon Salz0697cbf2012-07-04 15:14:04 +0800430 test.update_state(status=status, error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800431
Jon Salz0697cbf2012-07-04 15:14:04 +0800432 if not self.last_shutdown_time:
433 log_and_update_state(status=TestState.FAILED,
434 error_msg='Unable to read shutdown_time')
435 return
Jon Salz258a40c2012-04-19 12:34:01 +0800436
Jon Salz0697cbf2012-07-04 15:14:04 +0800437 now = time.time()
438 logging.info('%.03f s passed since reboot',
439 now - self.last_shutdown_time)
Jon Salz258a40c2012-04-19 12:34:01 +0800440
Jon Salz0697cbf2012-07-04 15:14:04 +0800441 if self.last_shutdown_time > now:
442 test.update_state(status=TestState.FAILED,
443 error_msg='Time moved backward during reboot')
444 elif (isinstance(test, factory.RebootStep) and
445 self.test_list.options.max_reboot_time_secs and
446 (now - self.last_shutdown_time >
447 self.test_list.options.max_reboot_time_secs)):
448 # A reboot took too long; fail. (We don't check this for
449 # HaltSteps, because the machine could be halted for a
450 # very long time, and even unplugged with battery backup,
451 # thus hosing the clock.)
452 log_and_update_state(
453 status=TestState.FAILED,
454 error_msg=('More than %d s elapsed during reboot '
455 '(%.03f s, from %s to %s)' % (
456 self.test_list.options.max_reboot_time_secs,
457 now - self.last_shutdown_time,
458 utils.TimeString(self.last_shutdown_time),
459 utils.TimeString(now))),
460 duration=(now-self.last_shutdown_time))
Jon Salzd4306c82012-11-30 15:16:36 +0800461 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800462 elif test_state.shutdown_count == test.iterations:
463 # Good!
464 log_and_update_state(status=TestState.PASSED,
465 duration=(now - self.last_shutdown_time),
466 error_msg='')
467 elif test_state.shutdown_count > test.iterations:
468 # Shut down too many times
469 log_and_update_state(status=TestState.FAILED,
470 error_msg='Too many shutdowns')
Jon Salzd4306c82012-11-30 15:16:36 +0800471 self._log_startup_messages()
Jon Salz0697cbf2012-07-04 15:14:04 +0800472 elif utils.are_shift_keys_depressed():
473 logging.info('Shift keys are depressed; cancelling restarts')
474 # Abort shutdown
475 log_and_update_state(
476 status=TestState.FAILED,
477 error_msg='Shutdown aborted with double shift keys')
Jon Salza6711d72012-07-18 14:33:03 +0800478 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800479 else:
480 def handler():
481 if self._prompt_cancel_shutdown(
482 test, test_state.shutdown_count + 1):
Jon Salza6711d72012-07-18 14:33:03 +0800483 factory.console.info('Shutdown aborted by operator')
Jon Salz0697cbf2012-07-04 15:14:04 +0800484 log_and_update_state(
485 status=TestState.FAILED,
486 error_msg='Shutdown aborted by operator')
Jon Salza6711d72012-07-18 14:33:03 +0800487 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800488 return
Jon Salz0405ab52012-03-16 15:26:52 +0800489
Jon Salz0697cbf2012-07-04 15:14:04 +0800490 # Time to shutdown again
491 log_and_update_state(
492 status=TestState.ACTIVE,
493 error_msg='',
494 iteration=test_state.shutdown_count)
Jon Salz73e0fd02012-04-04 11:46:38 +0800495
Jon Salz0697cbf2012-07-04 15:14:04 +0800496 self.event_log.Log('shutdown', operation='reboot')
497 self.state_instance.set_shared_data('shutdown_time',
498 time.time())
499 self.env.shutdown('reboot')
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800500
Jon Salz0697cbf2012-07-04 15:14:04 +0800501 self.on_ui_startup.append(handler)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800502
Jon Salz0697cbf2012-07-04 15:14:04 +0800503 def _prompt_cancel_shutdown(self, test, iteration):
504 if self.options.ui != 'chrome':
505 return False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800506
Jon Salz0697cbf2012-07-04 15:14:04 +0800507 pending_shutdown_data = {
508 'delay_secs': test.delay_secs,
509 'time': time.time() + test.delay_secs,
510 'operation': test.operation,
511 'iteration': iteration,
512 'iterations': test.iterations,
513 }
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800514
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 # Create a new (threaded) event client since we
516 # don't want to use the event loop for this.
517 with EventClient() as event_client:
518 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN,
519 **pending_shutdown_data))
520 aborted = event_client.wait(
521 lambda event: event.type == Event.Type.CANCEL_SHUTDOWN,
522 timeout=test.delay_secs) is not None
523 if aborted:
524 event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
525 return aborted
Jon Salz258a40c2012-04-19 12:34:01 +0800526
Jon Salz0697cbf2012-07-04 15:14:04 +0800527 def init_states(self):
528 '''
529 Initializes all states on startup.
530 '''
531 for test in self.test_list.get_all_tests():
532 # Make sure the state server knows about all the tests,
533 # defaulting to an untested state.
534 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800535
Jon Salz0697cbf2012-07-04 15:14:04 +0800536 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800537 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800538 ec_console_log = None
Vic Yang079f9872013-07-01 11:32:00 +0800539 ec_panic_info = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800540
Jon Salz0697cbf2012-07-04 15:14:04 +0800541 # Any 'active' tests should be marked as failed now.
542 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800543 if not test.is_leaf():
544 # Don't bother with parents; they will be updated when their
545 # children are updated.
546 continue
547
Jon Salz0697cbf2012-07-04 15:14:04 +0800548 test_state = test.get_state()
549 if test_state.status != TestState.ACTIVE:
550 continue
551 if isinstance(test, factory.ShutdownStep):
552 # Shutdown while the test was active - that's good.
553 self.handle_shutdown_complete(test, test_state)
554 else:
555 # Unexpected shutdown. Grab /var/log/messages for context.
556 if var_log_messages is None:
557 try:
558 var_log_messages = (
559 utils.var_log_messages_before_reboot())
560 # Write it to the log, to make it easier to
561 # correlate with /var/log/messages.
562 logging.info(
563 'Unexpected shutdown. '
564 'Tail of /var/log/messages before last reboot:\n'
565 '%s', ('\n'.join(
566 ' ' + x for x in var_log_messages)))
567 except: # pylint: disable=W0702
568 logging.exception('Unable to grok /var/log/messages')
569 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800570
Jon Salz008f4ea2012-08-28 05:39:45 +0800571 if mosys_log is None and not utils.in_chroot():
572 try:
573 mosys_log = utils.Spawn(
574 ['mosys', 'eventlog', 'list'],
575 read_stdout=True, log_stderr_on_error=True).stdout_data
576 # Write it to the log also.
577 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
578 except: # pylint: disable=W0702
579 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800580
Vic Yange4c275d2012-08-28 01:50:20 +0800581 if ec_console_log is None:
582 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800583 board = system.GetBoard()
584 ec_console_log = board.GetECConsoleLog()
Vic Yange4c275d2012-08-28 01:50:20 +0800585 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Jon Salzfe1f6652012-09-07 05:40:14 +0800586 except: # pylint: disable=W0702
Vic Yange4c275d2012-08-28 01:50:20 +0800587 logging.exception('Error retrieving EC console log')
588
Vic Yang079f9872013-07-01 11:32:00 +0800589 if ec_panic_info is None:
590 try:
591 board = system.GetBoard()
592 ec_panic_info = board.GetECPanicInfo()
593 logging.info('EC panic info after reboot:\n%s\n', ec_panic_info)
594 except: # pylint: disable=W0702
595 logging.exception('Error retrieving EC panic info')
596
Jon Salz0697cbf2012-07-04 15:14:04 +0800597 error_msg = 'Unexpected shutdown while test was running'
598 self.event_log.Log('end_test',
599 path=test.path,
600 status=TestState.FAILED,
601 invocation=test.get_state().invocation,
602 error_msg=error_msg,
Vic Yanga9c32212012-08-16 20:07:54 +0800603 var_log_messages='\n'.join(var_log_messages),
604 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800605 test.update_state(
606 status=TestState.FAILED,
607 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800608
Jon Salz50efe942012-07-26 11:54:10 +0800609 if not test.never_fails:
610 # For "never_fails" tests (such as "Start"), don't cancel
611 # pending tests, since reboot is expected.
612 factory.console.info('Unexpected shutdown while test %s '
613 'running; cancelling any pending tests',
614 test.path)
615 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800616
Jon Salz008f4ea2012-08-28 05:39:45 +0800617 self.update_skipped_tests()
618
619 def update_skipped_tests(self):
620 '''
621 Updates skipped states based on run_if.
622 '''
623 for t in self.test_list.walk():
624 if t.is_leaf() and t.run_if_table_name:
625 skip = False
626 try:
627 aux = shopfloor.get_selected_aux_data(t.run_if_table_name)
628 value = aux.get(t.run_if_col)
629 if value is not None:
630 skip = (not value) ^ t.run_if_not
631 except ValueError:
632 # Not available; assume it shouldn't be skipped
633 pass
634
635 test_state = t.get_state()
636 if ((not skip) and
637 (test_state.status == TestState.PASSED) and
638 (test_state.error_msg == TestState.SKIPPED_MSG)):
639 # It was marked as skipped before, but now we need to run it.
640 # Mark as untested.
641 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
642 else:
643 t.update_state(skip=skip)
644
Jon Salz0697cbf2012-07-04 15:14:04 +0800645 def show_next_active_test(self):
646 '''
647 Rotates to the next visible active test.
648 '''
649 self.reap_completed_tests()
650 active_tests = [
651 t for t in self.test_list.walk()
652 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
653 if not active_tests:
654 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800655
Jon Salz0697cbf2012-07-04 15:14:04 +0800656 try:
657 next_test = active_tests[
658 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
659 except ValueError: # visible_test not present in active_tests
660 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800661
Jon Salz0697cbf2012-07-04 15:14:04 +0800662 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800663
Jon Salz0697cbf2012-07-04 15:14:04 +0800664 def handle_event(self, event):
665 '''
666 Handles an event from the event server.
667 '''
668 handler = self.event_handlers.get(event.type)
669 if handler:
670 handler(event)
671 else:
672 # We don't register handlers for all event types - just ignore
673 # this event.
674 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800675
Vic Yangaabf9fd2013-04-09 18:56:13 +0800676 def check_critical_factory_note(self):
677 '''
678 Returns True if the last factory note is critical.
679 '''
680 notes = self.state_instance.get_shared_data('factory_note', True)
681 return notes and notes[-1]['level'] == 'CRITICAL'
682
Jon Salz0697cbf2012-07-04 15:14:04 +0800683 def run_next_test(self):
684 '''
685 Runs the next eligible test (or tests) in self.tests_to_run.
686 '''
687 self.reap_completed_tests()
Vic Yangaabf9fd2013-04-09 18:56:13 +0800688 if self.tests_to_run and self.check_critical_factory_note():
689 self.tests_to_run.clear()
690 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800691 while self.tests_to_run:
692 logging.debug('Tests to run: %s',
693 [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800694
Jon Salz0697cbf2012-07-04 15:14:04 +0800695 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800696
Jon Salz0697cbf2012-07-04 15:14:04 +0800697 if test in self.invocations:
698 logging.info('Next test %s is already running', test.path)
699 self.tests_to_run.popleft()
700 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800701
Jon Salza1412922012-07-23 16:04:17 +0800702 for requirement in test.require_run:
703 for i in requirement.test.walk():
704 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800705 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800706 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800707 return
708
Jon Salz0697cbf2012-07-04 15:14:04 +0800709 if self.invocations and not (test.backgroundable and all(
710 [x.backgroundable for x in self.invocations])):
711 logging.debug('Waiting for non-backgroundable tests to '
712 'complete before running %s', test.path)
713 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800714
Jon Salz3e6f5202012-10-15 15:08:29 +0800715 if test.get_state().skip:
716 factory.console.info('Skipping test %s', test.path)
717 test.update_state(status=TestState.PASSED,
718 error_msg=TestState.SKIPPED_MSG)
719 self.tests_to_run.popleft()
720 continue
721
Jon Salz0697cbf2012-07-04 15:14:04 +0800722 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800723
Jon Salz304a75d2012-07-06 11:14:15 +0800724 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800725 for requirement in test.require_run:
726 for i in requirement.test.walk():
727 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800728 # We've hit this test itself; stop checking
729 break
Jon Salza1412922012-07-23 16:04:17 +0800730 if ((i.get_state().status == TestState.UNTESTED) or
731 (requirement.passed and i.get_state().status !=
732 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800733 # Found an untested test; move on to the next
734 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800735 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800736 break
737
738 if untested:
739 untested_paths = ', '.join(sorted([x.path for x in untested]))
740 if self.state_instance.get_shared_data('engineering_mode',
741 optional=True):
742 # In engineering mode, we'll let it go.
743 factory.console.warn('In engineering mode; running '
744 '%s even though required tests '
745 '[%s] have not completed',
746 test.path, untested_paths)
747 else:
748 # Not in engineering mode; mark it failed.
749 error_msg = ('Required tests [%s] have not been run yet'
750 % untested_paths)
751 factory.console.error('Not running %s: %s',
752 test.path, error_msg)
753 test.update_state(status=TestState.FAILED,
754 error_msg=error_msg)
755 continue
756
Jon Salz0697cbf2012-07-04 15:14:04 +0800757 if isinstance(test, factory.ShutdownStep):
758 if os.path.exists(NO_REBOOT_FILE):
759 test.update_state(
760 status=TestState.FAILED, increment_count=1,
761 error_msg=('Skipped shutdown since %s is present' %
Jon Salz304a75d2012-07-06 11:14:15 +0800762 NO_REBOOT_FILE))
Jon Salz0697cbf2012-07-04 15:14:04 +0800763 continue
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800764
Jon Salz0697cbf2012-07-04 15:14:04 +0800765 test.update_state(status=TestState.ACTIVE, increment_count=1,
766 error_msg='', shutdown_count=0)
767 if self._prompt_cancel_shutdown(test, 1):
768 self.event_log.Log('reboot_cancelled')
769 test.update_state(
770 status=TestState.FAILED, increment_count=1,
771 error_msg='Shutdown aborted by operator',
772 shutdown_count=0)
chungyiafe8f772012-08-15 19:36:29 +0800773 continue
Jon Salz2f757d42012-06-27 17:06:42 +0800774
Jon Salz0697cbf2012-07-04 15:14:04 +0800775 # Save pending test list in the state server
Jon Salzdbf398f2012-06-14 17:30:01 +0800776 self.state_instance.set_shared_data(
Jon Salz0697cbf2012-07-04 15:14:04 +0800777 'tests_after_shutdown',
778 [t.path for t in self.tests_to_run])
779 # Save shutdown time
780 self.state_instance.set_shared_data('shutdown_time',
781 time.time())
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800782
Jon Salz0697cbf2012-07-04 15:14:04 +0800783 with self.env.lock:
784 self.event_log.Log('shutdown', operation=test.operation)
785 shutdown_result = self.env.shutdown(test.operation)
786 if shutdown_result:
787 # That's all, folks!
788 self.run_queue.put(None)
789 return
790 else:
791 # Just pass (e.g., in the chroot).
792 test.update_state(status=TestState.PASSED)
793 self.state_instance.set_shared_data(
794 'tests_after_shutdown', None)
795 # Send event with no fields to indicate that there is no
796 # longer a pending shutdown.
797 self.event_client.post_event(Event(
798 Event.Type.PENDING_SHUTDOWN))
799 continue
Jon Salz258a40c2012-04-19 12:34:01 +0800800
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800801 self._run_test(test, test.iterations, test.retries)
Jon Salz1acc8742012-07-17 17:45:55 +0800802
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800803 def _run_test(self, test, iterations_left=None, retries_left=None):
Jon Salz1acc8742012-07-17 17:45:55 +0800804 invoc = TestInvocation(self, test, on_completion=self.run_next_test)
805 new_state = test.update_state(
806 status=TestState.ACTIVE, increment_count=1, error_msg='',
Jon Salzbd42ce12012-09-18 08:03:59 +0800807 invocation=invoc.uuid, iterations_left=iterations_left,
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800808 retries_left=retries_left,
Jon Salzbd42ce12012-09-18 08:03:59 +0800809 visible=(self.visible_test == test))
Jon Salz1acc8742012-07-17 17:45:55 +0800810 invoc.count = new_state.count
811
812 self.invocations[test] = invoc
813 if self.visible_test is None and test.has_ui:
814 self.set_visible_test(test)
Vic Yang311ddb82012-09-26 12:08:28 +0800815 self.check_exclusive()
Jon Salz1acc8742012-07-17 17:45:55 +0800816 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800817
Vic Yang311ddb82012-09-26 12:08:28 +0800818 def check_exclusive(self):
Jon Salzce6a7f82013-06-10 18:22:54 +0800819 # alias since this is really long
820 EXCL_OPT = factory.FactoryTest.EXCLUSIVE_OPTIONS
821
Vic Yang311ddb82012-09-26 12:08:28 +0800822 current_exclusive_items = set([
Jon Salzce6a7f82013-06-10 18:22:54 +0800823 item for item in EXCL_OPT
Vic Yang311ddb82012-09-26 12:08:28 +0800824 if any([test.is_exclusive(item) for test in self.invocations])])
825
826 new_exclusive_items = current_exclusive_items - self.exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800827 if EXCL_OPT.NETWORKING in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800828 logging.info('Disabling network')
829 self.connection_manager.DisableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800830 if EXCL_OPT.CHARGER in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800831 logging.info('Stop controlling charger')
832
833 new_non_exclusive_items = self.exclusive_items - current_exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800834 if EXCL_OPT.NETWORKING in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800835 logging.info('Re-enabling network')
836 self.connection_manager.EnableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800837 if EXCL_OPT.CHARGER in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800838 logging.info('Start controlling charger')
839
Jon Salzce6a7f82013-06-10 18:22:54 +0800840 if self.cpufreq_manager:
841 enabled = EXCL_OPT.CPUFREQ not in current_exclusive_items
842 try:
843 self.cpufreq_manager.SetEnabled(enabled)
844 except: # pylint: disable=W0702
845 logging.exception('Unable to %s cpufreq services',
846 'enable' if enabled else 'disable')
847
Vic Yang311ddb82012-09-26 12:08:28 +0800848 # Only adjust charge state if not excluded
Jon Salzce6a7f82013-06-10 18:22:54 +0800849 if (EXCL_OPT.CHARGER not in current_exclusive_items and
850 not utils.in_chroot()):
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +0800851 if self.charge_manager:
852 self.charge_manager.AdjustChargeState()
853 else:
854 try:
855 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
856 except BoardException:
857 logging.exception('Unable to set charge state on this board')
Vic Yang311ddb82012-09-26 12:08:28 +0800858
859 self.exclusive_items = current_exclusive_items
Jon Salz5da61e62012-05-31 13:06:22 +0800860
cychiang21886742012-07-05 15:16:32 +0800861 def check_for_updates(self):
862 '''
863 Schedules an asynchronous check for updates if necessary.
864 '''
865 if not self.test_list.options.update_period_secs:
866 # Not enabled.
867 return
868
869 now = time.time()
870 if self.last_update_check and (
871 now - self.last_update_check <
872 self.test_list.options.update_period_secs):
873 # Not yet time for another check.
874 return
875
876 self.last_update_check = now
877
878 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
879 if reached_shopfloor:
880 new_update_md5sum = md5sum if needs_update else None
881 if system.SystemInfo.update_md5sum != new_update_md5sum:
882 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
883 system.SystemInfo.update_md5sum = new_update_md5sum
884 self.run_queue.put(self.update_system_info)
885
886 updater.CheckForUpdateAsync(
887 handle_check_for_update,
888 self.test_list.options.shopfloor_timeout_secs)
889
Jon Salza6711d72012-07-18 14:33:03 +0800890 def cancel_pending_tests(self):
891 '''Cancels any tests in the run queue.'''
892 self.run_tests([])
893
Jon Salz0697cbf2012-07-04 15:14:04 +0800894 def run_tests(self, subtrees, untested_only=False):
895 '''
896 Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800897
Jon Salz0697cbf2012-07-04 15:14:04 +0800898 The tests are run in order unless one fails (then stops).
899 Backgroundable tests are run simultaneously; when a foreground test is
900 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800901
Jon Salz0697cbf2012-07-04 15:14:04 +0800902 @param subtrees: Node or nodes containing tests to run (may either be
903 a single test or a list). Duplicates will be ignored.
904 '''
905 if type(subtrees) != list:
906 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800907
Jon Salz0697cbf2012-07-04 15:14:04 +0800908 # Nodes we've seen so far, to avoid duplicates.
909 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800910
Jon Salz0697cbf2012-07-04 15:14:04 +0800911 self.tests_to_run = deque()
912 for subtree in subtrees:
913 for test in subtree.walk():
914 if test in seen:
915 continue
916 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800917
Jon Salz0697cbf2012-07-04 15:14:04 +0800918 if not test.is_leaf():
919 continue
920 if (untested_only and
921 test.get_state().status != TestState.UNTESTED):
922 continue
923 self.tests_to_run.append(test)
924 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800925
Jon Salz0697cbf2012-07-04 15:14:04 +0800926 def reap_completed_tests(self):
927 '''
928 Removes completed tests from the set of active tests.
929
930 Also updates the visible test if it was reaped.
931 '''
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800932 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800933 for t, v in dict(self.invocations).iteritems():
934 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800935 test_completed = True
Jon Salz1acc8742012-07-17 17:45:55 +0800936 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800937 del self.invocations[t]
938
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800939 # Stop on failure if flag is true.
940 if (self.test_list.options.stop_on_failure and
941 new_state.status == TestState.FAILED):
942 # Clean all the tests to cause goofy to stop.
943 self.tests_to_run = []
944 factory.console.info("Stop on failure triggered. Empty the queue.")
945
Jon Salz1acc8742012-07-17 17:45:55 +0800946 if new_state.iterations_left and new_state.status == TestState.PASSED:
947 # Play it again, Sam!
948 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800949 # new_state.retries_left is obtained after update.
950 # For retries_left == 0, test can still be run for the last time.
951 elif (new_state.retries_left >= 0 and
952 new_state.status == TestState.FAILED):
953 # Still have to retry, Sam!
954 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +0800955
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800956 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800957 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800958
Jon Salz0697cbf2012-07-04 15:14:04 +0800959 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800960 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800961 self.set_visible_test(None)
962 # Make the first running test, if any, the visible test
963 for t in self.test_list.walk():
964 if t in self.invocations:
965 self.set_visible_test(t)
966 break
967
Jon Salz6dc031d2013-06-19 13:06:23 +0800968 def kill_active_tests(self, abort, root=None, reason=None):
Jon Salz0697cbf2012-07-04 15:14:04 +0800969 '''
970 Kills and waits for all active tests.
971
Jon Salz85a39882012-07-05 16:45:04 +0800972 Args:
973 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800974 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800975 root: If set, only kills tests with root as an ancestor.
Jon Salz0697cbf2012-07-04 15:14:04 +0800976 '''
977 self.reap_completed_tests()
978 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +0800979 if root and not test.has_ancestor(root):
980 continue
981
Jon Salz0697cbf2012-07-04 15:14:04 +0800982 factory.console.info('Killing active test %s...' % test.path)
Jon Salz6dc031d2013-06-19 13:06:23 +0800983 invoc.abort_and_join(reason)
Jon Salz0697cbf2012-07-04 15:14:04 +0800984 factory.console.info('Killed %s' % test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800985 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800986 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800987
Jon Salz0697cbf2012-07-04 15:14:04 +0800988 if not abort:
989 test.update_state(status=TestState.UNTESTED)
990 self.reap_completed_tests()
991
Jon Salz6dc031d2013-06-19 13:06:23 +0800992 def stop(self, root=None, fail=False, reason=None):
993 self.kill_active_tests(fail, root, reason)
Jon Salz85a39882012-07-05 16:45:04 +0800994 # Remove any tests in the run queue under the root.
995 self.tests_to_run = deque([x for x in self.tests_to_run
996 if root and not x.has_ancestor(root)])
997 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800998
Jon Salz4712ac72013-02-07 17:12:05 +0800999 def clear_state(self, root=None):
Jon Salzd7550792013-07-12 05:49:27 +08001000 if root is None:
1001 root = self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +08001002 self.stop(root, reason='Clearing test state')
Jon Salz4712ac72013-02-07 17:12:05 +08001003 for f in root.walk():
1004 if f.is_leaf():
1005 f.update_state(status=TestState.UNTESTED)
1006
Jon Salz6dc031d2013-06-19 13:06:23 +08001007 def abort_active_tests(self, reason=None):
1008 self.kill_active_tests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +08001009
1010 def main(self):
Jon Salzeff94182013-06-19 15:06:28 +08001011 syslog.openlog('goofy')
1012
Jon Salz0697cbf2012-07-04 15:14:04 +08001013 try:
Jon Salzd7550792013-07-12 05:49:27 +08001014 self.status = Status.INITIALIZING
Jon Salz0697cbf2012-07-04 15:14:04 +08001015 self.init()
1016 self.event_log.Log('goofy_init',
1017 success=True)
1018 except:
1019 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001020 try:
Jon Salz0697cbf2012-07-04 15:14:04 +08001021 self.event_log.Log('goofy_init',
1022 success=False,
1023 trace=traceback.format_exc())
1024 except: # pylint: disable=W0702
1025 pass
1026 raise
1027
Jon Salzd7550792013-07-12 05:49:27 +08001028 self.status = Status.RUNNING
Jon Salzeff94182013-06-19 15:06:28 +08001029 syslog.syslog('Goofy (factory test harness) starting')
Jon Salz0697cbf2012-07-04 15:14:04 +08001030 self.run()
1031
1032 def update_system_info(self):
1033 '''Updates system info.'''
1034 system_info = system.SystemInfo()
1035 self.state_instance.set_shared_data('system_info', system_info.__dict__)
1036 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
1037 system_info=system_info.__dict__))
1038 logging.info('System info: %r', system_info.__dict__)
1039
Jon Salzeb42f0d2012-07-27 19:14:04 +08001040 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
1041 '''Commences updating factory software.
1042
1043 Args:
1044 auto_run_on_restart: Auto-run when the machine comes back up.
1045 post_update_hook: Code to call after update but immediately before
1046 restart.
1047
1048 Returns:
1049 Never if the update was successful (we just reboot).
1050 False if the update was unnecessary (no update available).
1051 '''
Jon Salz6dc031d2013-06-19 13:06:23 +08001052 self.kill_active_tests(False, reason='Factory software update')
Jon Salza6711d72012-07-18 14:33:03 +08001053 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001054
Jon Salz5c344f62012-07-13 14:31:16 +08001055 def pre_update_hook():
1056 if auto_run_on_restart:
1057 self.state_instance.set_shared_data('tests_after_shutdown',
1058 FORCE_AUTO_RUN)
1059 self.state_instance.close()
1060
Jon Salzeb42f0d2012-07-27 19:14:04 +08001061 if updater.TryUpdate(pre_update_hook=pre_update_hook):
1062 if post_update_hook:
1063 post_update_hook()
1064 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +08001065
Jon Salzcef132a2012-08-30 04:58:08 +08001066 def handle_sigint(self, dummy_signum, dummy_frame):
Jon Salz77c151e2012-08-28 07:20:37 +08001067 logging.error('Received SIGINT')
1068 self.run_queue.put(None)
1069 raise KeyboardInterrupt()
1070
Jon Salze12c2b32013-06-25 16:24:34 +08001071 def find_kcrashes(self):
1072 """Finds kcrash files, logs them, and marks them as seen."""
1073 seen_crashes = set(
1074 self.state_instance.get_shared_data('seen_crashes', optional=True)
1075 or [])
1076
1077 for path in glob.glob('/var/spool/crash/*'):
1078 if not os.path.isfile(path):
1079 continue
1080 if path in seen_crashes:
1081 continue
1082 try:
1083 stat = os.stat(path)
1084 mtime = utils.TimeString(stat.st_mtime)
1085 logging.info(
1086 'Found new crash file %s (%d bytes at %s)',
1087 path, stat.st_size, mtime)
1088 extra_log_args = {}
1089
1090 try:
1091 _, ext = os.path.splitext(path)
1092 if ext in ['.kcrash', '.meta']:
1093 ext = ext.replace('.', '')
1094 with open(path) as f:
1095 data = f.read(MAX_CRASH_FILE_SIZE)
1096 tell = f.tell()
1097 logging.info(
1098 'Contents of %s%s:%s',
1099 path,
1100 ('' if tell == stat.st_size
1101 else '(truncated to %d bytes)' % MAX_CRASH_FILE_SIZE),
1102 ('\n' + data).replace('\n', '\n ' + ext + '> '))
1103 extra_log_args['data'] = data
1104
1105 # Copy to /var/factory/kcrash for posterity
1106 kcrash_dir = factory.get_factory_root('kcrash')
1107 utils.TryMakeDirs(kcrash_dir)
1108 shutil.copy(path, kcrash_dir)
1109 logging.info('Copied to %s',
1110 os.path.join(kcrash_dir, os.path.basename(path)))
1111 finally:
1112 # Even if something goes wrong with the above, still try to
1113 # log to event log
1114 self.event_log.Log('crash_file',
1115 path=path, size=stat.st_size, mtime=mtime,
1116 **extra_log_args)
1117 except: # pylint: disable=W0702
1118 logging.exception('Unable to handle crash files %s', path)
1119 seen_crashes.add(path)
1120
1121 self.state_instance.set_shared_data('seen_crashes', list(seen_crashes))
1122
Jon Salz128b0932013-07-03 16:55:26 +08001123 def GetTestList(self, test_list_id):
1124 """Returns the test list with the given ID.
1125
1126 Raises:
1127 TestListError: The test list ID is not valid.
1128 """
1129 try:
1130 return self.test_lists[test_list_id]
1131 except KeyError:
1132 raise test_lists.TestListError(
1133 '%r is not a valid test list ID (available IDs are [%s])' % (
1134 test_list_id, ', '.join(sorted(self.test_lists.keys()))))
1135
1136 def InitTestLists(self):
1137 """Reads in all test lists and sets the active test list."""
1138 self.test_lists = test_lists.BuildAllTestLists()
Jon Salzd7550792013-07-12 05:49:27 +08001139 logging.info('Loaded test lists: [%s]',
1140 test_lists.DescribeTestLists(self.test_lists))
Jon Salz128b0932013-07-03 16:55:26 +08001141
1142 if not self.options.test_list:
1143 self.options.test_list = test_lists.GetActiveTestListId()
1144
1145 if os.sep in self.options.test_list:
1146 # It's a path pointing to an old-style test list; use it.
1147 self.test_list = factory.read_test_list(self.options.test_list)
1148 else:
1149 self.test_list = self.GetTestList(self.options.test_list)
1150
1151 logging.info('Active test list: %s', self.test_list.test_list_id)
1152
1153 if isinstance(self.test_list, test_lists.OldStyleTestList):
1154 # Actually load it in. (See OldStyleTestList for an explanation
1155 # of why this is necessary.)
1156 self.test_list = self.test_list.Load()
1157
1158 self.test_list.state_instance = self.state_instance
1159
Jon Salz0697cbf2012-07-04 15:14:04 +08001160 def init(self, args=None, env=None):
1161 '''Initializes Goofy.
1162
1163 Args:
1164 args: A list of command-line arguments. Uses sys.argv if
1165 args is None.
1166 env: An Environment instance to use (or None to choose
1167 FakeChrootEnvironment or DUTEnvironment as appropriate).
1168 '''
Jon Salz77c151e2012-08-28 07:20:37 +08001169 signal.signal(signal.SIGINT, self.handle_sigint)
1170
Jon Salz0697cbf2012-07-04 15:14:04 +08001171 parser = OptionParser()
1172 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001173 action='store_true',
1174 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001175 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001176 metavar='FILE',
1177 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001178 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001179 action='store_true',
1180 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001181 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz8fa8e832012-07-13 19:04:09 +08001182 choices=['none', 'gtk', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001183 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001184 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001185 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001186 type='int', default=1,
1187 help=('Factor by which to scale UI '
1188 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001189 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001190 metavar='FILE',
1191 help='Use FILE as test list')
Jon Salzc79a9982012-08-30 04:42:01 +08001192 parser.add_option('--dummy_shopfloor', action='store_true',
1193 help='Use a dummy shopfloor server')
chungyiafe8f772012-08-15 19:36:29 +08001194 parser.add_option('--automation', dest='automation',
1195 action='store_true',
1196 help='Enable automation on running factory test')
Ricky Liang09216dc2013-02-22 17:26:45 +08001197 parser.add_option('--one_pixel_less', dest='one_pixel_less',
1198 action='store_true',
1199 help=('Start Chrome one pixel less than the full screen.'
1200 'Needed by Exynos platform to run GTK.'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001201 (self.options, self.args) = parser.parse_args(args)
1202
Jon Salz46b89562012-07-05 11:49:22 +08001203 # Make sure factory directories exist.
1204 factory.get_log_root()
1205 factory.get_state_root()
1206 factory.get_test_data_root()
1207
Jon Salz0697cbf2012-07-04 15:14:04 +08001208 global _inited_logging # pylint: disable=W0603
1209 if not _inited_logging:
1210 factory.init_logging('goofy', verbose=self.options.verbose)
1211 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001212
Jon Salz0f996602012-10-03 15:26:48 +08001213 if self.options.print_test_list:
1214 print factory.read_test_list(
1215 self.options.print_test_list).__repr__(recursive=True)
1216 sys.exit(0)
1217
Jon Salzee85d522012-07-17 14:34:46 +08001218 event_log.IncrementBootSequence()
Jon Salzd15bbcf2013-05-21 17:33:57 +08001219 # Don't defer logging the initial event, so we can make sure
1220 # that device_id, reimage_id, etc. are all set up.
1221 self.event_log = EventLog('goofy', defer=False)
Jon Salz0697cbf2012-07-04 15:14:04 +08001222
1223 if (not suppress_chroot_warning and
1224 factory.in_chroot() and
1225 self.options.ui == 'gtk' and
1226 os.environ.get('DISPLAY') in [None, '', ':0', ':0.0']):
1227 # That's not going to work! Tell the user how to run
1228 # this way.
1229 logging.warn(GOOFY_IN_CHROOT_WARNING)
1230 time.sleep(1)
1231
1232 if env:
1233 self.env = env
1234 elif factory.in_chroot():
1235 self.env = test_environment.FakeChrootEnvironment()
1236 logging.warn(
1237 'Using chroot environment: will not actually run autotests')
1238 else:
1239 self.env = test_environment.DUTEnvironment()
1240 self.env.goofy = self
1241
1242 if self.options.restart:
1243 state.clear_state()
1244
Jon Salz0697cbf2012-07-04 15:14:04 +08001245 if self.options.ui_scale_factor != 1 and utils.in_qemu():
1246 logging.warn(
1247 'In QEMU; ignoring ui_scale_factor argument')
1248 self.options.ui_scale_factor = 1
1249
1250 logging.info('Started')
1251
1252 self.start_state_server()
1253 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1254 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001255 self.options.ui_scale_factor)
1256 self.state_instance.set_shared_data('one_pixel_less',
1257 self.options.one_pixel_less)
Jon Salz0697cbf2012-07-04 15:14:04 +08001258 self.last_shutdown_time = (
1259 self.state_instance.get_shared_data('shutdown_time', optional=True))
1260 self.state_instance.del_shared_data('shutdown_time', optional=True)
Jon Salzb19ea072013-02-07 16:35:00 +08001261 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001262
Jon Salz128b0932013-07-03 16:55:26 +08001263 try:
1264 self.InitTestLists()
1265 except: # pylint: disable=W0702
1266 logging.exception('Unable to initialize test lists')
1267 self.state_instance.set_shared_data(
1268 'startup_error',
1269 'Unable to initialize test lists\n%s' % (
1270 traceback.format_exc()))
Jon Salzb19ea072013-02-07 16:35:00 +08001271 if self.options.ui == 'chrome':
1272 # Create an empty test list with default options so that the rest of
1273 # startup can proceed.
1274 self.test_list = factory.FactoryTestList(
1275 [], self.state_instance, factory.Options())
1276 else:
1277 # Bail with an error; no point in starting up.
1278 sys.exit('No valid test list; exiting.')
1279
Jon Salz822838b2013-03-25 17:32:33 +08001280 if self.test_list.options.clear_state_on_start:
1281 self.state_instance.clear_test_state()
1282
Vic Yang3e1cf5d2013-06-05 18:50:24 +08001283 if system.SystemInfo().firmware_version is None and not utils.in_chroot():
Vic Yang9bd4f772013-06-04 17:34:00 +08001284 self.state_instance.set_shared_data('startup_error',
1285 'Netboot firmware detected\n'
1286 'Connect Ethernet and reboot to re-image.\n'
1287 u'侦测到网路开机固件\n'
1288 u'请连接乙太网并重启')
1289
Jon Salz0697cbf2012-07-04 15:14:04 +08001290 if not self.state_instance.has_shared_data('ui_lang'):
1291 self.state_instance.set_shared_data('ui_lang',
1292 self.test_list.options.ui_lang)
1293 self.state_instance.set_shared_data(
1294 'test_list_options',
1295 self.test_list.options.__dict__)
1296 self.state_instance.test_list = self.test_list
1297
Cheng-Yi Chiangeb398df2013-07-19 14:30:45 +08001298 if not utils.in_chroot():
1299 cleanup_logs_paused_path = '/var/lib/cleanup_logs_paused'
1300 if self.test_list.options.disable_log_rotation:
1301 open(cleanup_logs_paused_path, 'w').close()
1302 else:
1303 file_utils.TryUnlink(cleanup_logs_paused_path)
Jon Salz83ef34b2012-11-01 19:46:35 +08001304
Jon Salz23926422012-09-01 03:38:13 +08001305 if self.options.dummy_shopfloor:
1306 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1307 'http://localhost:%d/' % shopfloor.DEFAULT_SERVER_PORT)
1308 self.dummy_shopfloor = Spawn(
1309 [os.path.join(factory.FACTORY_PATH, 'bin', 'shopfloor_server'),
1310 '--dummy'])
1311 elif self.test_list.options.shopfloor_server_url:
1312 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001313 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001314
Jon Salz0f996602012-10-03 15:26:48 +08001315 if self.test_list.options.time_sanitizer and not utils.in_chroot():
Jon Salz8fa8e832012-07-13 19:04:09 +08001316 self.time_sanitizer = time_sanitizer.TimeSanitizer(
1317 base_time=time_sanitizer.GetBaseTimeFromFile(
1318 # lsb-factory is written by the factory install shim during
1319 # installation, so it should have a good time obtained from
Jon Salz54882d02012-08-31 01:57:54 +08001320 # the mini-Omaha server. If it's not available, we'll use
1321 # /etc/lsb-factory (which will be much older, but reasonably
1322 # sane) and rely on a shopfloor sync to set a more accurate
1323 # time.
1324 '/usr/local/etc/lsb-factory',
1325 '/etc/lsb-release'))
Jon Salz8fa8e832012-07-13 19:04:09 +08001326 self.time_sanitizer.RunOnce()
1327
Vic Yangd8990da2013-06-27 16:57:43 +08001328 if self.test_list.options.check_cpu_usage_period_secs:
1329 self.cpu_usage_watcher = Spawn(['py/tools/cpu_usage_monitor.py',
1330 '-p', str(self.test_list.options.check_cpu_usage_period_secs)],
1331 cwd=factory.FACTORY_PATH)
1332
Jon Salz0697cbf2012-07-04 15:14:04 +08001333 self.init_states()
1334 self.start_event_server()
1335 self.connection_manager = self.env.create_connection_manager(
Tai-Hsu Lin371351a2012-08-27 14:17:14 +08001336 self.test_list.options.wlans,
1337 self.test_list.options.scan_wifi_period_secs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001338 # Note that we create a log watcher even if
1339 # sync_event_log_period_secs isn't set (no background
1340 # syncing), since we may use it to flush event logs as well.
1341 self.log_watcher = EventLogWatcher(
1342 self.test_list.options.sync_event_log_period_secs,
Jon Salzd15bbcf2013-05-21 17:33:57 +08001343 event_log_db_file=None,
Jon Salz16d10542012-07-23 12:18:45 +08001344 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001345 if self.test_list.options.sync_event_log_period_secs:
1346 self.log_watcher.StartWatchThread()
1347
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001348 # Note that we create a system log manager even if
1349 # sync_log_period_secs isn't set (no background
1350 # syncing), since we may kick it to sync logs in its
1351 # thread.
Cheng-Yi Chiangd3516a32013-07-17 15:30:47 +08001352 if self.test_list.options.enable_sync_log:
1353 self.system_log_manager = SystemLogManager(
1354 sync_log_paths=self.test_list.options.sync_log_paths,
1355 sync_period_sec=self.test_list.options.sync_log_period_secs,
1356 clear_log_paths=self.test_list.options.clear_log_paths)
1357 self.system_log_manager.StartSyncThread()
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001358
Jon Salz0697cbf2012-07-04 15:14:04 +08001359 self.update_system_info()
1360
Vic Yang4953fc12012-07-26 16:19:53 +08001361 assert ((self.test_list.options.min_charge_pct is None) ==
1362 (self.test_list.options.max_charge_pct is None))
Vic Yange83d9a12013-04-19 20:00:20 +08001363 if utils.in_chroot():
1364 logging.info('In chroot, ignoring charge manager and charge state')
1365 elif self.test_list.options.min_charge_pct is not None:
Vic Yang4953fc12012-07-26 16:19:53 +08001366 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1367 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001368 system.SystemStatus.charge_manager = self.charge_manager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +08001369 else:
1370 # Goofy should set charger state to charge if charge_manager is disabled.
1371 try:
1372 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
1373 except BoardException:
1374 logging.exception('Unable to set charge state on this board')
Vic Yang4953fc12012-07-26 16:19:53 +08001375
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001376 self.core_dump_manager = CoreDumpManager(
1377 self.test_list.options.core_dump_watchlist)
1378
Jon Salz0697cbf2012-07-04 15:14:04 +08001379 os.environ['CROS_FACTORY'] = '1'
1380 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1381
1382 # Set CROS_UI since some behaviors in ui.py depend on the
1383 # particular UI in use. TODO(jsalz): Remove this (and all
1384 # places it is used) when the GTK UI is removed.
1385 os.environ['CROS_UI'] = self.options.ui
1386
Jon Salz416f9cc2013-05-10 18:32:50 +08001387 # Initialize hooks.
1388 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1389 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
1390 assert isinstance(self.hooks, factory.Hooks), (
1391 "hooks should be of type Hooks but is %r" % type(self.hooks))
1392 self.hooks.test_list = self.test_list
1393
Jon Salzce6a7f82013-06-10 18:22:54 +08001394 if not utils.in_chroot():
Jon Salzddf0d052013-06-18 12:52:44 +08001395 self.cpufreq_manager = CpufreqManager(event_log=self.event_log)
Jon Salzce6a7f82013-06-10 18:22:54 +08001396
Jon Salz416f9cc2013-05-10 18:32:50 +08001397 # Call startup hook.
1398 self.hooks.OnStartup()
Justin Chuang31b02432013-06-27 15:16:51 +08001399 # Startup hooks may want to skip some tests.
1400 self.update_skipped_tests()
Jon Salz416f9cc2013-05-10 18:32:50 +08001401
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()
Cheng-Yi Chiangd3516a32013-07-17 15:30:47 +08001682 if self.system_log_manager:
1683 self.system_log_manager.KickSyncThread()
Justin Chuang83813982013-05-13 01:26:32 +08001684 except: # pylint: disable=W0702
1685 logging.exception('Unable to check battery or notify shopfloor')
1686 finally:
1687 if message != self.last_check_battery_message:
1688 logging.log(log_level, message)
1689 self.last_check_battery_message = message
1690
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001691 def check_core_dump(self):
1692 '''Checks if there is any core dumped file.
1693
1694 Removes unwanted core dump files immediately.
1695 Syncs those files matching watch list to server with a delay between
1696 each sync. After the files have been synced to server, deletes the files.
1697 '''
1698 core_dump_files = self.core_dump_manager.ScanFiles()
1699 if core_dump_files:
1700 now = time.time()
1701 if (self.last_kick_sync_time and now - self.last_kick_sync_time <
1702 self.test_list.options.kick_sync_min_interval_secs):
1703 return
1704 self.last_kick_sync_time = now
1705
1706 # Sends event to server
1707 self.event_log.Log('core_dumped', files=core_dump_files)
1708 self.log_watcher.KickWatchThread()
1709
1710 # Syncs files to server
Cheng-Yi Chiangd3516a32013-07-17 15:30:47 +08001711 if self.system_log_manager:
1712 self.system_log_manager.KickSyncThread(
1713 core_dump_files, self.core_dump_manager.ClearFiles)
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001714
Jon Salz8fa8e832012-07-13 19:04:09 +08001715 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001716 '''Writes out current time and tries to sync with shopfloor server.'''
1717 if not self.time_sanitizer:
1718 return
1719
1720 # Write out the current time.
1721 self.time_sanitizer.SaveTime()
1722
Jon Salz54882d02012-08-31 01:57:54 +08001723 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001724 return
1725
1726 now = time.time()
1727 if self.last_sync_time and (
1728 now - self.last_sync_time <
1729 self.test_list.options.sync_time_period_secs):
1730 # Not yet time for another check.
1731 return
1732 self.last_sync_time = now
1733
1734 def target():
1735 try:
Jon Salz54882d02012-08-31 01:57:54 +08001736 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001737 except: # pylint: disable=W0702
1738 # Oh well. Log an error (but no trace)
1739 logging.info(
1740 'Unable to get time from shopfloor server: %s',
1741 utils.FormatExceptionOnly())
1742
1743 thread = threading.Thread(target=target)
1744 thread.daemon = True
1745 thread.start()
1746
Jon Salz0697cbf2012-07-04 15:14:04 +08001747 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001748 '''Invoked when the run queue has no events.
1749
1750 This method must not raise exception.
1751 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001752 now = time.time()
1753 if (self.last_idle and
1754 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1755 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1756 # 1) seconds.
1757 return
1758
1759 self.last_idle = now
1760
Vic Yang311ddb82012-09-26 12:08:28 +08001761 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001762 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001763 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001764 self.log_disk_space_stats()
Justin Chuang83813982013-05-13 01:26:32 +08001765 self.check_battery()
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001766 self.check_core_dump()
Jon Salz57717ca2012-04-04 16:47:25 +08001767
Jon Salzd15bbcf2013-05-21 17:33:57 +08001768 def handle_event_logs(self, chunks):
Jon Salz0697cbf2012-07-04 15:14:04 +08001769 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001770
Jon Salz0697cbf2012-07-04 15:14:04 +08001771 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001772
1773 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001774 chunks: A list of Chunk objects.
Jon Salz0697cbf2012-07-04 15:14:04 +08001775 '''
Vic Yang93027612013-05-06 02:42:49 +08001776 first_exception = None
1777 exception_count = 0
1778
Jon Salzd15bbcf2013-05-21 17:33:57 +08001779 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001780 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001781 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001782 start_time = time.time()
1783 shopfloor_client = shopfloor.get_instance(
1784 detect=True,
1785 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzd15bbcf2013-05-21 17:33:57 +08001786 shopfloor_client.UploadEvent(chunk.log_name + "." +
1787 event_log.GetReimageId(),
1788 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001789 logging.info(
1790 'Successfully synced %s in %.03f s',
1791 description, time.time() - start_time)
1792 except: # pylint: disable=W0702
Jon Salzd15bbcf2013-05-21 17:33:57 +08001793 first_exception = (first_exception or (chunk.log_name + ': ' +
Vic Yang93027612013-05-06 02:42:49 +08001794 utils.FormatExceptionOnly()))
1795 exception_count += 1
1796
1797 if exception_count:
1798 if exception_count == 1:
1799 msg = 'Log upload failed: %s' % first_exception
1800 else:
1801 msg = '%d log upload failed; first is: %s' % (
1802 exception_count, first_exception)
1803 raise Exception(msg)
1804
Jon Salz57717ca2012-04-04 16:47:25 +08001805
Jon Salz0697cbf2012-07-04 15:14:04 +08001806 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1807 root=None):
1808 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001809
Jon Salz0697cbf2012-07-04 15:14:04 +08001810 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001811
Jon Salz0697cbf2012-07-04 15:14:04 +08001812 Args:
1813 starting_at: If provided, only auto-runs tests beginning with
1814 this test.
1815 '''
1816 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001817
Jon Salz0697cbf2012-07-04 15:14:04 +08001818 if starting_at:
1819 # Make sure they passed a test, not a string.
1820 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001821
Jon Salz0697cbf2012-07-04 15:14:04 +08001822 tests_to_reset = []
1823 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001824
Jon Salz0697cbf2012-07-04 15:14:04 +08001825 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001826
Jon Salz0697cbf2012-07-04 15:14:04 +08001827 for test in root.get_top_level_tests():
1828 if starting_at:
1829 if test == starting_at:
1830 # We've found starting_at; do auto-run on all
1831 # subsequent tests.
1832 found_starting_at = True
1833 if not found_starting_at:
1834 # Don't start this guy yet
1835 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001836
Jon Salz0697cbf2012-07-04 15:14:04 +08001837 status = test.get_state().status
1838 if status == TestState.ACTIVE or status in statuses_to_run:
1839 # Reset the test (later; we will need to abort
1840 # all active tests first).
1841 tests_to_reset.append(test)
1842 if status in statuses_to_run:
1843 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001844
Jon Salz6dc031d2013-06-19 13:06:23 +08001845 self.abort_active_tests('Operator requested run/re-run of certain tests')
Jon Salz258a40c2012-04-19 12:34:01 +08001846
Jon Salz0697cbf2012-07-04 15:14:04 +08001847 # Reset all statuses of the tests to run (in case any tests were active;
1848 # we want them to be run again).
1849 for test_to_reset in tests_to_reset:
1850 for test in test_to_reset.walk():
1851 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001852
Jon Salz0697cbf2012-07-04 15:14:04 +08001853 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001854
Jon Salz0697cbf2012-07-04 15:14:04 +08001855 def restart_tests(self, root=None):
1856 '''Restarts all tests.'''
1857 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001858
Jon Salz6dc031d2013-06-19 13:06:23 +08001859 self.abort_active_tests('Operator requested restart of certain tests')
Jon Salz0697cbf2012-07-04 15:14:04 +08001860 for test in root.walk():
1861 test.update_state(status=TestState.UNTESTED)
1862 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001863
Jon Salz0697cbf2012-07-04 15:14:04 +08001864 def auto_run(self, starting_at=None, root=None):
1865 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001866
Jon Salz0697cbf2012-07-04 15:14:04 +08001867 Args:
1868 starting_at: If provide, only auto-runs tests beginning with
1869 this test.
1870 '''
1871 root = root or self.test_list
1872 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1873 starting_at=starting_at,
1874 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001875
Jon Salz0697cbf2012-07-04 15:14:04 +08001876 def re_run_failed(self, root=None):
1877 '''Re-runs failed tests.'''
1878 root = root or self.test_list
1879 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001880
Jon Salz0697cbf2012-07-04 15:14:04 +08001881 def show_review_information(self):
1882 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001883
Jon Salz0697cbf2012-07-04 15:14:04 +08001884 The information screene is rendered by main UI program (ui.py), so in
1885 goofy we only need to kill all active tests, set them as untested, and
1886 clear remaining tests.
1887 '''
1888 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001889 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001890
Jon Salz0697cbf2012-07-04 15:14:04 +08001891 def handle_switch_test(self, event):
1892 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001893
Jon Salz0697cbf2012-07-04 15:14:04 +08001894 @param event: The SWITCH_TEST event.
1895 '''
1896 test = self.test_list.lookup_path(event.path)
1897 if not test:
1898 logging.error('Unknown test %r', event.key)
1899 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001900
Jon Salz0697cbf2012-07-04 15:14:04 +08001901 invoc = self.invocations.get(test)
1902 if invoc and test.backgroundable:
1903 # Already running: just bring to the front if it
1904 # has a UI.
1905 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001906 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001907 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001908
Jon Salz6dc031d2013-06-19 13:06:23 +08001909 self.abort_active_tests('Operator requested abort (switch_test)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001910 for t in test.walk():
1911 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001912
Jon Salz0697cbf2012-07-04 15:14:04 +08001913 if self.test_list.options.auto_run_on_keypress:
1914 self.auto_run(starting_at=test)
1915 else:
1916 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001917
Jon Salz0697cbf2012-07-04 15:14:04 +08001918 def wait(self):
1919 '''Waits for all pending invocations.
1920
1921 Useful for testing.
1922 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001923 while self.invocations:
1924 for k, v in self.invocations.iteritems():
1925 logging.info('Waiting for %s to complete...', k)
1926 v.thread.join()
1927 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001928
1929 def check_exceptions(self):
1930 '''Raises an error if any exceptions have occurred in
1931 invocation threads.'''
1932 if self.exceptions:
1933 raise RuntimeError('Exception in invocation thread: %r' %
1934 self.exceptions)
1935
1936 def record_exception(self, msg):
1937 '''Records an exception in an invocation thread.
1938
1939 An exception with the given message will be rethrown when
1940 Goofy is destroyed.'''
1941 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001942
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001943
1944if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001945 goofy = Goofy()
1946 try:
1947 goofy.main()
Jon Salz0f996602012-10-03 15:26:48 +08001948 except SystemExit:
1949 # Propagate SystemExit without logging.
1950 raise
Jon Salz31373eb2012-09-21 16:19:49 +08001951 except:
Jon Salz0f996602012-10-03 15:26:48 +08001952 # Log the error before trying to shut down (unless it's a graceful
1953 # exit).
Jon Salz31373eb2012-09-21 16:19:49 +08001954 logging.exception('Error in main loop')
1955 raise
Jon Salz77c151e2012-08-28 07:20:37 +08001956 finally:
1957 goofy.destroy()