blob: 327af1f0c2a2380be638433b1943e0e9f07772df [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.
Cheng-Yi Chiangd3516a32013-07-17 15:30:47 +08001355 if self.test_list.options.enable_sync_log:
1356 self.system_log_manager = SystemLogManager(
1357 sync_log_paths=self.test_list.options.sync_log_paths,
1358 sync_period_sec=self.test_list.options.sync_log_period_secs,
1359 clear_log_paths=self.test_list.options.clear_log_paths)
1360 self.system_log_manager.StartSyncThread()
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001361
Jon Salz0697cbf2012-07-04 15:14:04 +08001362 self.update_system_info()
1363
Vic Yang4953fc12012-07-26 16:19:53 +08001364 assert ((self.test_list.options.min_charge_pct is None) ==
1365 (self.test_list.options.max_charge_pct is None))
Vic Yange83d9a12013-04-19 20:00:20 +08001366 if utils.in_chroot():
1367 logging.info('In chroot, ignoring charge manager and charge state')
1368 elif self.test_list.options.min_charge_pct is not None:
Vic Yang4953fc12012-07-26 16:19:53 +08001369 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1370 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001371 system.SystemStatus.charge_manager = self.charge_manager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +08001372 else:
1373 # Goofy should set charger state to charge if charge_manager is disabled.
1374 try:
1375 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
1376 except BoardException:
1377 logging.exception('Unable to set charge state on this board')
Vic Yang4953fc12012-07-26 16:19:53 +08001378
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001379 self.core_dump_manager = CoreDumpManager(
1380 self.test_list.options.core_dump_watchlist)
1381
Jon Salz0697cbf2012-07-04 15:14:04 +08001382 os.environ['CROS_FACTORY'] = '1'
1383 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1384
1385 # Set CROS_UI since some behaviors in ui.py depend on the
1386 # particular UI in use. TODO(jsalz): Remove this (and all
1387 # places it is used) when the GTK UI is removed.
1388 os.environ['CROS_UI'] = self.options.ui
1389
Jon Salz416f9cc2013-05-10 18:32:50 +08001390 # Initialize hooks.
1391 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1392 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
1393 assert isinstance(self.hooks, factory.Hooks), (
1394 "hooks should be of type Hooks but is %r" % type(self.hooks))
1395 self.hooks.test_list = self.test_list
1396
Jon Salzce6a7f82013-06-10 18:22:54 +08001397 if not utils.in_chroot():
Jon Salzddf0d052013-06-18 12:52:44 +08001398 self.cpufreq_manager = CpufreqManager(event_log=self.event_log)
Jon Salzce6a7f82013-06-10 18:22:54 +08001399
Jon Salz416f9cc2013-05-10 18:32:50 +08001400 # Call startup hook.
1401 self.hooks.OnStartup()
Justin Chuang31b02432013-06-27 15:16:51 +08001402 # Startup hooks may want to skip some tests.
1403 self.update_skipped_tests()
Jon Salz416f9cc2013-05-10 18:32:50 +08001404
Jon Salze12c2b32013-06-25 16:24:34 +08001405 self.find_kcrashes()
1406
Jon Salz0697cbf2012-07-04 15:14:04 +08001407 if self.options.ui == 'chrome':
1408 self.env.launch_chrome()
1409 logging.info('Waiting for a web socket connection')
Cheng-Yi Chiangfd8ed392013-03-08 21:37:31 +08001410 self.web_socket_manager.wait()
Jon Salz0697cbf2012-07-04 15:14:04 +08001411
1412 # Wait for the test widget size to be set; this is done in
1413 # an asynchronous RPC so there is a small chance that the
1414 # web socket might be opened first.
1415 for _ in range(100): # 10 s
1416 try:
1417 if self.state_instance.get_shared_data('test_widget_size'):
1418 break
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001419 except KeyError:
Jon Salz0697cbf2012-07-04 15:14:04 +08001420 pass # Retry
1421 time.sleep(0.1) # 100 ms
1422 else:
1423 logging.warn('Never received test_widget_size from UI')
Jon Salz45297282013-05-18 14:31:47 +08001424
1425 # Send Chrome a Tab to get focus to the factory UI
1426 # (http://crosbug.com/p/19444). TODO(jsalz): remove this hack
1427 # and figure out the right way to get the focus to Chrome.
1428 if not utils.in_chroot():
1429 Spawn(
1430 [os.path.join(factory.FACTORY_PATH, 'bin', 'send_key'), 'Tab'],
1431 check_call=True, log=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001432 elif self.options.ui == 'gtk':
1433 self.start_ui()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001434
Ricky Liang650f6bf2012-09-28 13:22:54 +08001435 # Create download path for autotest beforehand or autotests run at
1436 # the same time might fail due to race condition.
1437 if not factory.in_chroot():
1438 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1439 'download'))
1440
Jon Salz0697cbf2012-07-04 15:14:04 +08001441 def state_change_callback(test, test_state):
1442 self.event_client.post_event(
1443 Event(Event.Type.STATE_CHANGE,
1444 path=test.path, state=test_state))
1445 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001446
Jon Salza6711d72012-07-18 14:33:03 +08001447 for handler in self.on_ui_startup:
1448 handler()
1449
1450 self.prespawner = Prespawner()
1451 self.prespawner.start()
1452
Jon Salz0697cbf2012-07-04 15:14:04 +08001453 try:
1454 tests_after_shutdown = self.state_instance.get_shared_data(
1455 'tests_after_shutdown')
1456 except KeyError:
1457 tests_after_shutdown = None
Jon Salz57717ca2012-04-04 16:47:25 +08001458
Jon Salz5c344f62012-07-13 14:31:16 +08001459 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1460 if not force_auto_run and tests_after_shutdown is not None:
Jon Salz0697cbf2012-07-04 15:14:04 +08001461 logging.info('Resuming tests after shutdown: %s',
1462 tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001463 self.tests_to_run.extend(
1464 self.test_list.lookup_path(t) for t in tests_after_shutdown)
1465 self.run_queue.put(self.run_next_test)
1466 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001467 if force_auto_run or self.test_list.options.auto_run_on_start:
Jon Salz0697cbf2012-07-04 15:14:04 +08001468 self.run_queue.put(
1469 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001470 self.state_instance.set_shared_data('tests_after_shutdown', None)
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001471
Dean Liao592e4d52013-01-10 20:06:39 +08001472 self.may_disable_cros_shortcut_keys()
1473
1474 def may_disable_cros_shortcut_keys(self):
1475 test_options = self.test_list.options
1476 if test_options.disable_cros_shortcut_keys:
1477 logging.info('Filter ChromeOS shortcut keys.')
1478 self.key_filter = KeyFilter(
1479 unmap_caps_lock=test_options.disable_caps_lock,
1480 caps_lock_keycode=test_options.caps_lock_keycode)
1481 self.key_filter.Start()
1482
Jon Salz0697cbf2012-07-04 15:14:04 +08001483 def run(self):
1484 '''Runs Goofy.'''
1485 # Process events forever.
1486 while self.run_once(True):
1487 pass
Jon Salz73e0fd02012-04-04 11:46:38 +08001488
Jon Salz0697cbf2012-07-04 15:14:04 +08001489 def run_once(self, block=False):
1490 '''Runs all items pending in the event loop.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001491
Jon Salz0697cbf2012-07-04 15:14:04 +08001492 Args:
1493 block: If true, block until at least one event is processed.
Jon Salz7c15e8b2012-06-19 17:10:37 +08001494
Jon Salz0697cbf2012-07-04 15:14:04 +08001495 Returns:
1496 True to keep going or False to shut down.
1497 '''
1498 events = utils.DrainQueue(self.run_queue)
cychiang21886742012-07-05 15:16:32 +08001499 while not events:
Jon Salz0697cbf2012-07-04 15:14:04 +08001500 # Nothing on the run queue.
1501 self._run_queue_idle()
1502 if block:
1503 # Block for at least one event...
cychiang21886742012-07-05 15:16:32 +08001504 try:
1505 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
1506 except Queue.Empty:
1507 # Keep going (calling _run_queue_idle() again at the top of
1508 # the loop)
1509 continue
Jon Salz0697cbf2012-07-04 15:14:04 +08001510 # ...and grab anything else that showed up at the same
1511 # time.
1512 events.extend(utils.DrainQueue(self.run_queue))
cychiang21886742012-07-05 15:16:32 +08001513 else:
1514 break
Jon Salz51528e12012-07-02 18:54:45 +08001515
Jon Salz0697cbf2012-07-04 15:14:04 +08001516 for event in events:
1517 if not event:
1518 # Shutdown request.
1519 self.run_queue.task_done()
1520 return False
Jon Salz51528e12012-07-02 18:54:45 +08001521
Jon Salz0697cbf2012-07-04 15:14:04 +08001522 try:
1523 event()
Jon Salz85a39882012-07-05 16:45:04 +08001524 except: # pylint: disable=W0702
1525 logging.exception('Error in event loop')
Jon Salz0697cbf2012-07-04 15:14:04 +08001526 self.record_exception(traceback.format_exception_only(
1527 *sys.exc_info()[:2]))
1528 # But keep going
1529 finally:
1530 self.run_queue.task_done()
1531 return True
Jon Salz0405ab52012-03-16 15:26:52 +08001532
Jon Salz0e6532d2012-10-25 16:30:11 +08001533 def _should_sync_time(self, foreground=False):
1534 '''Returns True if we should attempt syncing time with shopfloor.
1535
1536 Args:
1537 foreground: If True, synchronizes even if background syncing
1538 is disabled (e.g., in explicit sync requests from the
1539 SyncShopfloor test).
1540 '''
1541 return ((foreground or
1542 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001543 self.time_sanitizer and
1544 (not self.time_synced) and
1545 (not factory.in_chroot()))
1546
Jon Salz0e6532d2012-10-25 16:30:11 +08001547 def sync_time_with_shopfloor_server(self, foreground=False):
Jon Salz54882d02012-08-31 01:57:54 +08001548 '''Syncs time with shopfloor server, if not yet synced.
1549
Jon Salz0e6532d2012-10-25 16:30:11 +08001550 Args:
1551 foreground: If True, synchronizes even if background syncing
1552 is disabled (e.g., in explicit sync requests from the
1553 SyncShopfloor test).
1554
Jon Salz54882d02012-08-31 01:57:54 +08001555 Returns:
1556 False if no time sanitizer is available, or True if this sync (or a
1557 previous sync) succeeded.
1558
1559 Raises:
1560 Exception if unable to contact the shopfloor server.
1561 '''
Jon Salz0e6532d2012-10-25 16:30:11 +08001562 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001563 self.time_sanitizer.SyncWithShopfloor()
1564 self.time_synced = True
1565 return self.time_synced
1566
Jon Salzb92c5112012-09-21 15:40:11 +08001567 def log_disk_space_stats(self):
Jon Salz18e0e022013-06-11 17:13:39 +08001568 if (utils.in_chroot() or
1569 not self.test_list.options.log_disk_space_period_secs):
Jon Salzb92c5112012-09-21 15:40:11 +08001570 return
1571
1572 now = time.time()
1573 if (self.last_log_disk_space_time and
1574 now - self.last_log_disk_space_time <
1575 self.test_list.options.log_disk_space_period_secs):
1576 return
1577 self.last_log_disk_space_time = now
1578
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001579 # Upload event if stateful partition usage is above threshold.
1580 # Stateful partition is mounted on /usr/local, while
1581 # encrypted stateful partition is mounted on /var.
1582 # If there are too much logs in the factory process,
1583 # these two partitions might get full.
Jon Salzb92c5112012-09-21 15:40:11 +08001584 try:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001585 vfs_infos = disk_space.GetAllVFSInfo()
1586 stateful_info, encrypted_info = None, None
1587 for vfs_info in vfs_infos.values():
1588 if '/usr/local' in vfs_info.mount_points:
1589 stateful_info = vfs_info
1590 if '/var' in vfs_info.mount_points:
1591 encrypted_info = vfs_info
1592
1593 stateful = disk_space.GetPartitionUsage(stateful_info)
1594 encrypted = disk_space.GetPartitionUsage(encrypted_info)
1595
1596 above_threshold = (
1597 self.test_list.options.stateful_usage_threshold and
1598 max(stateful.bytes_used_pct,
1599 stateful.inodes_used_pct,
1600 encrypted.bytes_used_pct,
1601 encrypted.inodes_used_pct) >
1602 self.test_list.options.stateful_usage_threshold)
1603
1604 if above_threshold:
1605 self.event_log.Log('stateful_partition_usage',
1606 partitions={
1607 'stateful': {
1608 'bytes_used_pct': FloatDigit(stateful.bytes_used_pct, 2),
1609 'inodes_used_pct': FloatDigit(stateful.inodes_used_pct, 2)},
1610 'encrypted_stateful': {
1611 'bytes_used_pct': FloatDigit(encrypted.bytes_used_pct, 2),
1612 'inodes_used_pct': FloatDigit(encrypted.inodes_used_pct, 2)}
1613 })
1614 self.log_watcher.ScanEventLogs()
Cheng-Yi Chiang00798e72013-06-20 18:16:39 +08001615 if (not utils.in_chroot() and
1616 self.test_list.options.stateful_usage_above_threshold_action):
1617 Spawn(self.test_list.options.stateful_usage_above_threshold_action,
1618 call=True)
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001619
1620 message = disk_space.FormatSpaceUsedAll(vfs_infos)
Jon Salz3c493bb2013-02-07 17:24:58 +08001621 if message != self.last_log_disk_space_message:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001622 if above_threshold:
1623 logging.warning(message)
1624 else:
1625 logging.info(message)
Jon Salz3c493bb2013-02-07 17:24:58 +08001626 self.last_log_disk_space_message = message
Jon Salzb92c5112012-09-21 15:40:11 +08001627 except: # pylint: disable=W0702
1628 logging.exception('Unable to get disk space used')
1629
Justin Chuang83813982013-05-13 01:26:32 +08001630 def check_battery(self):
1631 '''Checks the current battery status.
1632
1633 Logs current battery charging level and status to log. If the battery level
1634 is lower below warning_low_battery_pct, send warning event to shopfloor.
1635 If the battery level is lower below critical_low_battery_pct, flush disks.
1636 '''
1637 if not self.test_list.options.check_battery_period_secs:
1638 return
1639
1640 now = time.time()
1641 if (self.last_check_battery_time and
1642 now - self.last_check_battery_time <
1643 self.test_list.options.check_battery_period_secs):
1644 return
1645 self.last_check_battery_time = now
1646
1647 message = ''
1648 log_level = logging.INFO
1649 try:
1650 power = system.GetBoard().power
1651 if not power.CheckBatteryPresent():
1652 message = 'Battery is not present'
1653 else:
1654 ac_present = power.CheckACPresent()
1655 charge_pct = power.GetChargePct(get_float=True)
1656 message = ('Current battery level %.1f%%, AC charger is %s' %
1657 (charge_pct, 'connected' if ac_present else 'disconnected'))
1658
1659 if charge_pct > self.test_list.options.critical_low_battery_pct:
1660 critical_low_battery = False
1661 else:
1662 critical_low_battery = True
1663 # Only sync disks when battery level is still above minimum
1664 # value. This can be used for offline analysis when shopfloor cannot
1665 # be connected.
1666 if charge_pct > MIN_BATTERY_LEVEL_FOR_DISK_SYNC:
1667 logging.warning('disk syncing for critical low battery situation')
1668 os.system('sync; sync; sync')
1669 else:
1670 logging.warning('disk syncing is cancelled '
1671 'because battery level is lower than %.1f',
1672 MIN_BATTERY_LEVEL_FOR_DISK_SYNC)
1673
1674 # Notify shopfloor server
1675 if (critical_low_battery or
1676 (not ac_present and
1677 charge_pct <= self.test_list.options.warning_low_battery_pct)):
1678 log_level = logging.WARNING
1679
1680 self.event_log.Log('low_battery',
1681 battery_level=charge_pct,
1682 charger_connected=ac_present,
1683 critical=critical_low_battery)
1684 self.log_watcher.KickWatchThread()
Cheng-Yi Chiangd3516a32013-07-17 15:30:47 +08001685 if self.system_log_manager:
1686 self.system_log_manager.KickSyncThread()
Justin Chuang83813982013-05-13 01:26:32 +08001687 except: # pylint: disable=W0702
1688 logging.exception('Unable to check battery or notify shopfloor')
1689 finally:
1690 if message != self.last_check_battery_message:
1691 logging.log(log_level, message)
1692 self.last_check_battery_message = message
1693
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001694 def check_core_dump(self):
1695 '''Checks if there is any core dumped file.
1696
1697 Removes unwanted core dump files immediately.
1698 Syncs those files matching watch list to server with a delay between
1699 each sync. After the files have been synced to server, deletes the files.
1700 '''
1701 core_dump_files = self.core_dump_manager.ScanFiles()
1702 if core_dump_files:
1703 now = time.time()
1704 if (self.last_kick_sync_time and now - self.last_kick_sync_time <
1705 self.test_list.options.kick_sync_min_interval_secs):
1706 return
1707 self.last_kick_sync_time = now
1708
1709 # Sends event to server
1710 self.event_log.Log('core_dumped', files=core_dump_files)
1711 self.log_watcher.KickWatchThread()
1712
1713 # Syncs files to server
Cheng-Yi Chiangd3516a32013-07-17 15:30:47 +08001714 if self.system_log_manager:
1715 self.system_log_manager.KickSyncThread(
1716 core_dump_files, self.core_dump_manager.ClearFiles)
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001717
Jon Salz8fa8e832012-07-13 19:04:09 +08001718 def sync_time_in_background(self):
Jon Salzb22d1172012-08-06 10:38:57 +08001719 '''Writes out current time and tries to sync with shopfloor server.'''
1720 if not self.time_sanitizer:
1721 return
1722
1723 # Write out the current time.
1724 self.time_sanitizer.SaveTime()
1725
Jon Salz54882d02012-08-31 01:57:54 +08001726 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001727 return
1728
1729 now = time.time()
1730 if self.last_sync_time and (
1731 now - self.last_sync_time <
1732 self.test_list.options.sync_time_period_secs):
1733 # Not yet time for another check.
1734 return
1735 self.last_sync_time = now
1736
1737 def target():
1738 try:
Jon Salz54882d02012-08-31 01:57:54 +08001739 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001740 except: # pylint: disable=W0702
1741 # Oh well. Log an error (but no trace)
1742 logging.info(
1743 'Unable to get time from shopfloor server: %s',
1744 utils.FormatExceptionOnly())
1745
1746 thread = threading.Thread(target=target)
1747 thread.daemon = True
1748 thread.start()
1749
Jon Salz0697cbf2012-07-04 15:14:04 +08001750 def _run_queue_idle(self):
Vic Yang4953fc12012-07-26 16:19:53 +08001751 '''Invoked when the run queue has no events.
1752
1753 This method must not raise exception.
1754 '''
Jon Salzb22d1172012-08-06 10:38:57 +08001755 now = time.time()
1756 if (self.last_idle and
1757 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
1758 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
1759 # 1) seconds.
1760 return
1761
1762 self.last_idle = now
1763
Vic Yang311ddb82012-09-26 12:08:28 +08001764 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001765 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001766 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001767 self.log_disk_space_stats()
Justin Chuang83813982013-05-13 01:26:32 +08001768 self.check_battery()
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001769 self.check_core_dump()
Jon Salz57717ca2012-04-04 16:47:25 +08001770
Jon Salzd15bbcf2013-05-21 17:33:57 +08001771 def handle_event_logs(self, chunks):
Jon Salz0697cbf2012-07-04 15:14:04 +08001772 '''Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001773
Jon Salz0697cbf2012-07-04 15:14:04 +08001774 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001775
1776 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001777 chunks: A list of Chunk objects.
Jon Salz0697cbf2012-07-04 15:14:04 +08001778 '''
Cheng-Yi Chiang3e5ec7b2013-07-17 12:44:46 +08001779 if not self.test_list.options.sync_event_log:
1780 logging.info('Skipped syncing event logs %s',
1781 ', '.join([str(chunk) for chunk in chunks]))
1782 return
1783
Vic Yang93027612013-05-06 02:42:49 +08001784 first_exception = None
1785 exception_count = 0
1786
Jon Salzd15bbcf2013-05-21 17:33:57 +08001787 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001788 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001789 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001790 start_time = time.time()
1791 shopfloor_client = shopfloor.get_instance(
1792 detect=True,
1793 timeout=self.test_list.options.shopfloor_timeout_secs)
Jon Salzd15bbcf2013-05-21 17:33:57 +08001794 shopfloor_client.UploadEvent(chunk.log_name + "." +
1795 event_log.GetReimageId(),
1796 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001797 logging.info(
1798 'Successfully synced %s in %.03f s',
1799 description, time.time() - start_time)
1800 except: # pylint: disable=W0702
Jon Salzd15bbcf2013-05-21 17:33:57 +08001801 first_exception = (first_exception or (chunk.log_name + ': ' +
Vic Yang93027612013-05-06 02:42:49 +08001802 utils.FormatExceptionOnly()))
1803 exception_count += 1
1804
1805 if exception_count:
1806 if exception_count == 1:
1807 msg = 'Log upload failed: %s' % first_exception
1808 else:
1809 msg = '%d log upload failed; first is: %s' % (
1810 exception_count, first_exception)
1811 raise Exception(msg)
1812
Jon Salz57717ca2012-04-04 16:47:25 +08001813
Jon Salz0697cbf2012-07-04 15:14:04 +08001814 def run_tests_with_status(self, statuses_to_run, starting_at=None,
1815 root=None):
1816 '''Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001817
Jon Salz0697cbf2012-07-04 15:14:04 +08001818 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001819
Jon Salz0697cbf2012-07-04 15:14:04 +08001820 Args:
1821 starting_at: If provided, only auto-runs tests beginning with
1822 this test.
1823 '''
1824 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001825
Jon Salz0697cbf2012-07-04 15:14:04 +08001826 if starting_at:
1827 # Make sure they passed a test, not a string.
1828 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001829
Jon Salz0697cbf2012-07-04 15:14:04 +08001830 tests_to_reset = []
1831 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001832
Jon Salz0697cbf2012-07-04 15:14:04 +08001833 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001834
Jon Salz0697cbf2012-07-04 15:14:04 +08001835 for test in root.get_top_level_tests():
1836 if starting_at:
1837 if test == starting_at:
1838 # We've found starting_at; do auto-run on all
1839 # subsequent tests.
1840 found_starting_at = True
1841 if not found_starting_at:
1842 # Don't start this guy yet
1843 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001844
Jon Salz0697cbf2012-07-04 15:14:04 +08001845 status = test.get_state().status
1846 if status == TestState.ACTIVE or status in statuses_to_run:
1847 # Reset the test (later; we will need to abort
1848 # all active tests first).
1849 tests_to_reset.append(test)
1850 if status in statuses_to_run:
1851 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001852
Jon Salz6dc031d2013-06-19 13:06:23 +08001853 self.abort_active_tests('Operator requested run/re-run of certain tests')
Jon Salz258a40c2012-04-19 12:34:01 +08001854
Jon Salz0697cbf2012-07-04 15:14:04 +08001855 # Reset all statuses of the tests to run (in case any tests were active;
1856 # we want them to be run again).
1857 for test_to_reset in tests_to_reset:
1858 for test in test_to_reset.walk():
1859 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001860
Jon Salz0697cbf2012-07-04 15:14:04 +08001861 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001862
Jon Salz0697cbf2012-07-04 15:14:04 +08001863 def restart_tests(self, root=None):
1864 '''Restarts all tests.'''
1865 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001866
Jon Salz6dc031d2013-06-19 13:06:23 +08001867 self.abort_active_tests('Operator requested restart of certain tests')
Jon Salz0697cbf2012-07-04 15:14:04 +08001868 for test in root.walk():
1869 test.update_state(status=TestState.UNTESTED)
1870 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001871
Jon Salz0697cbf2012-07-04 15:14:04 +08001872 def auto_run(self, starting_at=None, root=None):
1873 '''"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001874
Jon Salz0697cbf2012-07-04 15:14:04 +08001875 Args:
1876 starting_at: If provide, only auto-runs tests beginning with
1877 this test.
1878 '''
1879 root = root or self.test_list
1880 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
1881 starting_at=starting_at,
1882 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001883
Jon Salz0697cbf2012-07-04 15:14:04 +08001884 def re_run_failed(self, root=None):
1885 '''Re-runs failed tests.'''
1886 root = root or self.test_list
1887 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001888
Jon Salz0697cbf2012-07-04 15:14:04 +08001889 def show_review_information(self):
1890 '''Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001891
Jon Salz0697cbf2012-07-04 15:14:04 +08001892 The information screene is rendered by main UI program (ui.py), so in
1893 goofy we only need to kill all active tests, set them as untested, and
1894 clear remaining tests.
1895 '''
1896 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001897 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001898
Jon Salz0697cbf2012-07-04 15:14:04 +08001899 def handle_switch_test(self, event):
1900 '''Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001901
Jon Salz0697cbf2012-07-04 15:14:04 +08001902 @param event: The SWITCH_TEST event.
1903 '''
1904 test = self.test_list.lookup_path(event.path)
1905 if not test:
1906 logging.error('Unknown test %r', event.key)
1907 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001908
Jon Salz0697cbf2012-07-04 15:14:04 +08001909 invoc = self.invocations.get(test)
1910 if invoc and test.backgroundable:
1911 # Already running: just bring to the front if it
1912 # has a UI.
1913 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001914 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001915 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001916
Jon Salz6dc031d2013-06-19 13:06:23 +08001917 self.abort_active_tests('Operator requested abort (switch_test)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001918 for t in test.walk():
1919 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001920
Jon Salz0697cbf2012-07-04 15:14:04 +08001921 if self.test_list.options.auto_run_on_keypress:
1922 self.auto_run(starting_at=test)
1923 else:
1924 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001925
Jon Salz0697cbf2012-07-04 15:14:04 +08001926 def wait(self):
1927 '''Waits for all pending invocations.
1928
1929 Useful for testing.
1930 '''
Jon Salz1acc8742012-07-17 17:45:55 +08001931 while self.invocations:
1932 for k, v in self.invocations.iteritems():
1933 logging.info('Waiting for %s to complete...', k)
1934 v.thread.join()
1935 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001936
1937 def check_exceptions(self):
1938 '''Raises an error if any exceptions have occurred in
1939 invocation threads.'''
1940 if self.exceptions:
1941 raise RuntimeError('Exception in invocation thread: %r' %
1942 self.exceptions)
1943
1944 def record_exception(self, msg):
1945 '''Records an exception in an invocation thread.
1946
1947 An exception with the given message will be rethrown when
1948 Goofy is destroyed.'''
1949 self.exceptions.append(msg)
Jon Salz73e0fd02012-04-04 11:46:38 +08001950
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001951
1952if __name__ == '__main__':
Jon Salz77c151e2012-08-28 07:20:37 +08001953 goofy = Goofy()
1954 try:
1955 goofy.main()
Jon Salz0f996602012-10-03 15:26:48 +08001956 except SystemExit:
1957 # Propagate SystemExit without logging.
1958 raise
Jon Salz31373eb2012-09-21 16:19:49 +08001959 except:
Jon Salz0f996602012-10-03 15:26:48 +08001960 # Log the error before trying to shut down (unless it's a graceful
1961 # exit).
Jon Salz31373eb2012-09-21 16:19:49 +08001962 logging.exception('Error in main loop')
1963 raise
Jon Salz77c151e2012-08-28 07:20:37 +08001964 finally:
1965 goofy.destroy()