blob: 17fab6f0bb3b8d604a4e0c831fc2392fec77b349 [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()
Justin Chuang31b02432013-06-27 15:16:51 +08001401 # Startup hooks may want to skip some tests.
1402 self.update_skipped_tests()
Jon Salz416f9cc2013-05-10 18:32:50 +08001403
Jon Salze12c2b32013-06-25 16:24:34 +08001404 self.find_kcrashes()
1405
Jon Salz0697cbf2012-07-04 15:14:04 +08001406 if self.options.ui == 'chrome':
1407 self.env.launch_chrome()
1408 logging.info('Waiting for a web socket connection')
Cheng-Yi Chiangfd8ed392013-03-08 21:37:31 +08001409 self.web_socket_manager.wait()
Jon Salz0697cbf2012-07-04 15:14:04 +08001410
1411 # Wait for the test widget size to be set; this is done in
1412 # an asynchronous RPC so there is a small chance that the
1413 # web socket might be opened first.
1414 for _ in range(100): # 10 s
1415 try:
1416 if self.state_instance.get_shared_data('test_widget_size'):
1417 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001418 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001419 pass # Retry
1420 time.sleep(0.1) # 100 ms
1421 else:
1422 logging.warn('Never received test_widget_size from UI')
Jon Salz45297282013-05-18 14:31:47 +08001423
1424 # Send Chrome a Tab to get focus to the factory UI
1425 # (http://crosbug.com/p/19444). TODO(jsalz): remove this hack
1426 # and figure out the right way to get the focus to Chrome.
1427 if not utils.in_chroot():
1428 Spawn(
1429 [os.path.join(factory.FACTORY_PATH, 'bin', 'send_key'), 'Tab'],
1430 check_call=True, log=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001431 elif self.options.ui == 'gtk':
1432 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001433
Ricky Liang650f6bf2012-09-28 13:22:54 +08001434 # Create download path for autotest beforehand or autotests run at
1435 # the same time might fail due to race condition.
1436 if not factory.in_chroot():
1437 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1438 'download'))
1439
Jon Salz0697cbf2012-07-04 15:14:04 +08001440 def state_change_callback(test, test_state):
1441 self.event_client.post_event(
1442 Event(Event.Type.STATE_CHANGE,
1443 path=test.path, state=test_state))
1444 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001445
Jon Salza6711d72012-07-18 14:33:03 +08001446 for handler in self.on_ui_startup:
1447 handler()
1448
1449 self.prespawner = Prespawner()
1450 self.prespawner.start()
1451
Jon Salz0697cbf2012-07-04 15:14:04 +08001452 try:
1453 tests_after_shutdown = self.state_instance.get_shared_data(
1454 'tests_after_shutdown')
1455 except KeyError:
1456 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001457
Jon Salz5c344f62012-07-13 14:31:16 +08001458 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1459 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001460 logging.info('Resuming tests after shutdown: %s',
1461 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001462 self.tests_to_run.extend(
1463 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1464 self.run_queue.put(self.run_next_test)
1465 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001466 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001467 self.run_queue.put(
1468 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001469 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001470
Dean Liao592e4d52013-01-10 20:06:39 +08001471 self.may_disable_cros_shortcut_keys()
1472
1473 def may_disable_cros_shortcut_keys(self):
1474 test_options = self.test_list.options
1475 if test_options.disable_cros_shortcut_keys:
1476 logging.info('Filter ChromeOS shortcut keys.')
1477 self.key_filter = KeyFilter(
1478 unmap_caps_lock=test_options.disable_caps_lock,
1479 caps_lock_keycode=test_options.caps_lock_keycode)
1480 self.key_filter.Start()
1481
Jon Salz0697cbf2012-07-04 15:14:04 +08001482 def run(self):
1483 '''Runs Goofy.'''
1484 # Process events forever.
1485 while self.run_once(True):
1486 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001487
Jon Salz0697cbf2012-07-04 15:14:04 +08001488 def run_once(self, block=False):
1489 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001490
Jon Salz0697cbf2012-07-04 15:14:04 +08001491 Args:
1492 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001493
Jon Salz0697cbf2012-07-04 15:14:04 +08001494 Returns:
1495 True to keep going or False to shut down.
1496 '''
1497 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001498 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001499 # Nothing on the run queue.
1500 self._run_queue_idle()
1501 if block:
1502 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001503 try:
1504 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1505 except Queue.Empty:
1506 # Keep going (calling _run_queue_idle() again at the top of
1507 # the loop)
1508 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001509 # ...and grab anything else that showed up at the same
1510 # time.
1511 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001512 else:
1513 break
Jon Salz51528e12012-07-02 18:54:45 +08001514
Jon Salz0697cbf2012-07-04 15:14:04 +08001515 for event in events:
1516 if not event:
1517 # Shutdown request.
1518 self.run_queue.task_done()
1519 return False
Jon Salz51528e12012-07-02 18:54:45 +08001520
Jon Salz0697cbf2012-07-04 15:14:04 +08001521 try:
1522 event()
Jon Salz85a39882012-07-05 16:45:04 +08001523 except: # pylint: disable=W0702
1524 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001525 self.record_exception(traceback.format_exception_only(
1526 *sys.exc_info()[:2]))
1527 # But keep going
1528 finally:
1529 self.run_queue.task_done()
1530 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001531
Jon Salz0e6532d2012-10-25 16:30:11 +08001532 def _should_sync_time(self, foreground=False):
1533 '''Returns True if we should attempt syncing time with shopfloor.
1534
1535 Args:
1536 foreground: If True, synchronizes even if background syncing
1537 is disabled (e.g., in explicit sync requests from the
1538 SyncShopfloor test).
1539 '''
1540 return ((foreground or
1541 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001542 self.time_sanitizer and
1543 (not self.time_synced) and
1544 (not factory.in_chroot()))
1545
Jon Salz0e6532d2012-10-25 16:30:11 +08001546 def sync_time_with_shopfloor_server(self, foreground=False):
Jon Salz54882d02012-08-31 01:57:54 +08001547 '''Syncs time with shopfloor server, if not yet synced.
1548
Jon Salz0e6532d2012-10-25 16:30:11 +08001549 Args:
1550 foreground: If True, synchronizes even if background syncing
1551 is disabled (e.g., in explicit sync requests from the
1552 SyncShopfloor test).
1553
Jon Salz54882d02012-08-31 01:57:54 +08001554 Returns:
1555 False if no time sanitizer is available, or True if this sync (or a
1556 previous sync) succeeded.
1557
1558 Raises:
1559 Exception if unable to contact the shopfloor server.
1560 '''
Jon Salz0e6532d2012-10-25 16:30:11 +08001561 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001562 self.time_sanitizer.SyncWithShopfloor()
1563 self.time_synced = True
1564 return self.time_synced
1565
Jon Salzb92c5112012-09-21 15:40:11 +08001566 def log_disk_space_stats(self):
Jon Salz18e0e022013-06-11 17:13:39 +08001567 if (utils.in_chroot() or
1568 not self.test_list.options.log_disk_space_period_secs):
Jon Salzb92c5112012-09-21 15:40:11 +08001569 return
1570
1571 now = time.time()
1572 if (self.last_log_disk_space_time and
1573 now - self.last_log_disk_space_time <
1574 self.test_list.options.log_disk_space_period_secs):
1575 return
1576 self.last_log_disk_space_time = now
1577
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001578 # Upload event if stateful partition usage is above threshold.
1579 # Stateful partition is mounted on /usr/local, while
1580 # encrypted stateful partition is mounted on /var.
1581 # If there are too much logs in the factory process,
1582 # these two partitions might get full.
Jon Salzb92c5112012-09-21 15:40:11 +08001583 try:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001584 vfs_infos = disk_space.GetAllVFSInfo()
1585 stateful_info, encrypted_info = None, None
1586 for vfs_info in vfs_infos.values():
1587 if '/usr/local' in vfs_info.mount_points:
1588 stateful_info = vfs_info
1589 if '/var' in vfs_info.mount_points:
1590 encrypted_info = vfs_info
1591
1592 stateful = disk_space.GetPartitionUsage(stateful_info)
1593 encrypted = disk_space.GetPartitionUsage(encrypted_info)
1594
1595 above_threshold = (
1596 self.test_list.options.stateful_usage_threshold and
1597 max(stateful.bytes_used_pct,
1598 stateful.inodes_used_pct,
1599 encrypted.bytes_used_pct,
1600 encrypted.inodes_used_pct) >
1601 self.test_list.options.stateful_usage_threshold)
1602
1603 if above_threshold:
1604 self.event_log.Log('stateful_partition_usage',
1605 partitions={
1606 'stateful': {
1607 'bytes_used_pct': FloatDigit(stateful.bytes_used_pct, 2),
1608 'inodes_used_pct': FloatDigit(stateful.inodes_used_pct, 2)},
1609 'encrypted_stateful': {
1610 'bytes_used_pct': FloatDigit(encrypted.bytes_used_pct, 2),
1611 'inodes_used_pct': FloatDigit(encrypted.inodes_used_pct, 2)}
1612 })
1613 self.log_watcher.ScanEventLogs()
Cheng-Yi Chiang00798e72013-06-20 18:16:39 +08001614 if (not utils.in_chroot() and
1615 self.test_list.options.stateful_usage_above_threshold_action):
1616 Spawn(self.test_list.options.stateful_usage_above_threshold_action,
1617 call=True)
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001618
1619 message = disk_space.FormatSpaceUsedAll(vfs_infos)
Jon Salz3c493bb2013-02-07 17:24:58 +08001620 if message != self.last_log_disk_space_message:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001621 if above_threshold:
1622 logging.warning(message)
1623 else:
1624 logging.info(message)
Jon Salz3c493bb2013-02-07 17:24:58 +08001625 self.last_log_disk_space_message = message
Jon Salzb92c5112012-09-21 15:40:11 +08001626 except: # pylint: disable=W0702
1627 logging.exception('Unable to get disk space used')
1628
Justin Chuang83813982013-05-13 01:26:32 +08001629 def check_battery(self):
1630 '''Checks the current battery status.
1631
1632 Logs current battery charging level and status to log. If the battery level
1633 is lower below warning_low_battery_pct, send warning event to shopfloor.
1634 If the battery level is lower below critical_low_battery_pct, flush disks.
1635 '''
1636 if not self.test_list.options.check_battery_period_secs:
1637 return
1638
1639 now = time.time()
1640 if (self.last_check_battery_time and
1641 now - self.last_check_battery_time <
1642 self.test_list.options.check_battery_period_secs):
1643 return
1644 self.last_check_battery_time = now
1645
1646 message = ''
1647 log_level = logging.INFO
1648 try:
1649 power = system.GetBoard().power
1650 if not power.CheckBatteryPresent():
1651 message = 'Battery is not present'
1652 else:
1653 ac_present = power.CheckACPresent()
1654 charge_pct = power.GetChargePct(get_float=True)
1655 message = ('Current battery level %.1f%%, AC charger is %s' %
1656 (charge_pct, 'connected' if ac_present else 'disconnected'))
1657
1658 if charge_pct > self.test_list.options.critical_low_battery_pct:
1659 critical_low_battery = False
1660 else:
1661 critical_low_battery = True
1662 # Only sync disks when battery level is still above minimum
1663 # value. This can be used for offline analysis when shopfloor cannot
1664 # be connected.
1665 if charge_pct > MIN_BATTERY_LEVEL_FOR_DISK_SYNC:
1666 logging.warning('disk syncing for critical low battery situation')
1667 os.system('sync; sync; sync')
1668 else:
1669 logging.warning('disk syncing is cancelled '
1670 'because battery level is lower than %.1f',
1671 MIN_BATTERY_LEVEL_FOR_DISK_SYNC)
1672
1673 # Notify shopfloor server
1674 if (critical_low_battery or
1675 (not ac_present and
1676 charge_pct <= self.test_list.options.warning_low_battery_pct)):
1677 log_level = logging.WARNING
1678
1679 self.event_log.Log('low_battery',
1680 battery_level=charge_pct,
1681 charger_connected=ac_present,
1682 critical=critical_low_battery)
1683 self.log_watcher.KickWatchThread()
1684 self.system_log_manager.KickSyncThread()
1685 except: # pylint: disable=W0702
1686 logging.exception('Unable to check battery or notify shopfloor')
1687 finally:
1688 if message != self.last_check_battery_message:
1689 logging.log(log_level, message)
1690 self.last_check_battery_message = message
1691
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001692 def check_core_dump(self):
1693 '''Checks if there is any core dumped file.
1694
1695 Removes unwanted core dump files immediately.
1696 Syncs those files matching watch list to server with a delay between
1697 each sync. After the files have been synced to server, deletes the files.
1698 '''
1699 core_dump_files = self.core_dump_manager.ScanFiles()
1700 if core_dump_files:
1701 now = time.time()
1702 if (self.last_kick_sync_time and now - self.last_kick_sync_time <
1703 self.test_list.options.kick_sync_min_interval_secs):
1704 return
1705 self.last_kick_sync_time = now
1706
1707 # Sends event to server
1708 self.event_log.Log('core_dumped', files=core_dump_files)
1709 self.log_watcher.KickWatchThread()
1710
1711 # Syncs files to server
1712 self.system_log_manager.KickSyncThread(
1713 core_dump_files, self.core_dump_manager.ClearFiles)
1714
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()