blob: adcb7b07b2bb85cddaa66025ed0ba2ae37e44abc [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
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08008"""The main factory flow that runs the factory test and finalizes a device."""
Hung-Te Linf2f78f72012-02-08 19:27:11 +08009
Joel Kitchingb85ed7f2014-10-08 18:24:39 +080010from __future__ import print_function
11
Jon Salze12c2b32013-06-25 16:24:34 +080012import glob
Wei-Han Chenc17b4112016-11-22 14:56:51 +080013import itertools
Jon Salz0405ab52012-03-16 15:26:52 +080014import logging
Wei-Han Chenc17b4112016-11-22 14:56:51 +080015from optparse import OptionParser
Jon Salz0405ab52012-03-16 15:26:52 +080016import os
Jon Salze12c2b32013-06-25 16:24:34 +080017import shutil
Jon Salz77c151e2012-08-28 07:20:37 +080018import signal
Jon Salz0405ab52012-03-16 15:26:52 +080019import sys
Jon Salzeff94182013-06-19 15:06:28 +080020import syslog
Jon Salz0405ab52012-03-16 15:26:52 +080021import threading
22import time
23import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080024import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080025from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080026
Jon Salz0697cbf2012-07-04 15:14:04 +080027import factory_common # pylint: disable=W0611
Hung-Te Linb6287242016-05-18 14:39:05 +080028from cros.factory.device import device_utils
Vic Yangd80ea752014-09-24 16:07:14 +080029from cros.factory.goofy.goofy_base import GoofyBase
30from cros.factory.goofy.goofy_rpc import GoofyRPC
Vic Yangd80ea752014-09-24 16:07:14 +080031from cros.factory.goofy.invocation import TestInvocation
32from cros.factory.goofy.link_manager import PresenterLinkManager
Earl Oua3bca122016-10-21 16:00:30 +080033from cros.factory.goofy.plugins import plugin_controller
Vic Yange2c76a82014-10-30 12:48:19 -070034from cros.factory.goofy import prespawner
Wei-Ning Huang38b75f02015-02-25 18:25:14 +080035from cros.factory.goofy.terminal_manager import TerminalManager
Earl Oua3bca122016-10-21 16:00:30 +080036from cros.factory.goofy import test_environment
Wei-Han Chenc17b4112016-11-22 14:56:51 +080037from cros.factory.goofy.test_list_iterator import TestListIterator
Earl Oua3bca122016-10-21 16:00:30 +080038from cros.factory.goofy import updater
Vic Yangd80ea752014-09-24 16:07:14 +080039from cros.factory.goofy.web_socket_manager import WebSocketManager
Hung-Te Linb6287242016-05-18 14:39:05 +080040from cros.factory.test.e2e_test.common import AutomationMode
41from cros.factory.test.e2e_test.common import AutomationModePrompt
42from cros.factory.test.e2e_test.common import ParseAutomationMode
43from cros.factory.test.env import paths
Jon Salz83591782012-06-26 11:09:58 +080044from cros.factory.test.event import Event
45from cros.factory.test.event import EventClient
46from cros.factory.test.event import EventServer
Hung-Te Linb6287242016-05-18 14:39:05 +080047from cros.factory.test import event_log
48from cros.factory.test.event_log import EventLog
Hung-Te Linb6287242016-05-18 14:39:05 +080049from cros.factory.test.event_log import GetBootSequence
Hung-Te Lin91492a12014-11-25 18:56:30 +080050from cros.factory.test.event_log_watcher import EventLogWatcher
Earl Oua3bca122016-10-21 16:00:30 +080051from cros.factory.test import factory
jcliangcd688182012-08-20 21:01:26 +080052from cros.factory.test.factory import TestState
Hung-Te Lin3f096842016-01-13 17:37:06 +080053from cros.factory.test.rules import phase
Earl Oua3bca122016-10-21 16:00:30 +080054from cros.factory.test import shopfloor
55from cros.factory.test import state
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080056from cros.factory.test.test_lists import test_lists
Earl Oua3bca122016-10-21 16:00:30 +080057from cros.factory.test import testlog
58from cros.factory.test import testlog_goofy
Hung-Te Linb6287242016-05-18 14:39:05 +080059from cros.factory.tools.key_filter import KeyFilter
Hung-Te Linf707b242016-01-08 23:11:42 +080060from cros.factory.utils import debug_utils
Jon Salz2af235d2013-06-24 14:47:21 +080061from cros.factory.utils import file_utils
Joel Kitchingb85ed7f2014-10-08 18:24:39 +080062from cros.factory.utils import net_utils
Hung-Te Lin4e6357c2016-01-08 14:32:00 +080063from cros.factory.utils import process_utils
64from cros.factory.utils import sys_utils
65from cros.factory.utils import time_utils
Hung-Te Linf707b242016-01-08 23:11:42 +080066from cros.factory.utils import type_utils
Hung-Te Linf2f78f72012-02-08 19:27:11 +080067
68
Hung-Te Linf2f78f72012-02-08 19:27:11 +080069HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Joel Kitching625ff0f2016-05-16 14:59:40 -070070CACHES_DIR = os.path.join(paths.GetStateRoot(), 'caches')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080071
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +080072CLEANUP_LOGS_PAUSED = '/var/lib/cleanup_logs_paused'
73
Jon Salz5c344f62012-07-13 14:31:16 +080074# Value for tests_after_shutdown that forces auto-run (e.g., after
75# a factory update, when the available set of tests might change).
76FORCE_AUTO_RUN = 'force_auto_run'
77
Wei-Han Chenc17b4112016-11-22 14:56:51 +080078# Key to load the test list iterator after shutdown test
79TESTS_AFTER_SHUTDOWN = 'tests_after_shutdown'
80
Justin Chuang83813982013-05-13 01:26:32 +080081
Ricky Liang45c73e72015-01-15 15:00:30 +080082MAX_CRASH_FILE_SIZE = 64 * 1024
Jon Salze12c2b32013-06-25 16:24:34 +080083
Hung-Te Linf707b242016-01-08 23:11:42 +080084Status = type_utils.Enum(['UNINITIALIZED', 'INITIALIZING', 'RUNNING',
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080085 'TERMINATING', 'TERMINATED'])
Jon Salzd7550792013-07-12 05:49:27 +080086
Ricky Liang45c73e72015-01-15 15:00:30 +080087
Hung-Te Linf2f78f72012-02-08 19:27:11 +080088def get_hwid_cfg():
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +080089 """Returns the HWID config tag, or an empty string if none can be found."""
Jon Salz0697cbf2012-07-04 15:14:04 +080090 if 'CROS_HWID' in os.environ:
91 return os.environ['CROS_HWID']
92 if os.path.exists(HWID_CFG_PATH):
Ricky Liang45c73e72015-01-15 15:00:30 +080093 with open(HWID_CFG_PATH, 'r') as hwid_cfg_handle:
Jon Salz0697cbf2012-07-04 15:14:04 +080094 return hwid_cfg_handle.read().strip()
95 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080096
97
Jon Salz73e0fd02012-04-04 11:46:38 +080098_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080099
Ricky Liang45c73e72015-01-15 15:00:30 +0800100
Peter Ammon1e1ec572014-06-26 17:56:32 -0700101class Goofy(GoofyBase):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800102 """The main factory flow.
Jon Salz0697cbf2012-07-04 15:14:04 +0800103
104 Note that all methods in this class must be invoked from the main
105 (event) thread. Other threads, such as callbacks and TestInvocation
106 methods, should instead post events on the run queue.
107
108 TODO: Unit tests. (chrome-os-partner:7409)
109
110 Properties:
111 uuid: A unique UUID for this invocation of Goofy.
112 state_instance: An instance of FactoryState.
113 state_server: The FactoryState XML/RPC server.
114 state_server_thread: A thread running state_server.
115 event_server: The EventServer socket server.
116 event_server_thread: A thread running event_server.
117 event_client: A client to the event server.
Earl Oua3bca122016-10-21 16:00:30 +0800118 plugin_controller: The PluginController object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800119 ui_process: The factory ui process object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800120 invocations: A map from FactoryTest objects to the corresponding
121 TestInvocations objects representing active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800122 options: Command-line options.
123 args: Command-line args.
124 test_list: The test list.
Jon Salz128b0932013-07-03 16:55:26 +0800125 test_lists: All new-style test lists.
Ricky Liang4bff3e32014-02-20 18:46:11 +0800126 run_id: The identifier for latest test run.
127 scheduled_run_tests: The list of tests scheduled for latest test run.
Jon Salz0697cbf2012-07-04 15:14:04 +0800128 event_handlers: Map of Event.Type to the method used to handle that
129 event. If the method has an 'event' argument, the event is passed
130 to the handler.
Jon Salz416f9cc2013-05-10 18:32:50 +0800131 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salzd7550792013-07-12 05:49:27 +0800132 status: The current Goofy status (a member of the Status enum).
Peter Ammon948b7172014-07-15 12:43:06 -0700133 link_manager: Instance of PresenterLinkManager for communicating
134 with GoofyPresenter
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800135 """
Ricky Liang45c73e72015-01-15 15:00:30 +0800136
Jon Salz0697cbf2012-07-04 15:14:04 +0800137 def __init__(self):
Peter Ammon1e1ec572014-06-26 17:56:32 -0700138 super(Goofy, self).__init__()
Jon Salz0697cbf2012-07-04 15:14:04 +0800139 self.uuid = str(uuid.uuid4())
140 self.state_instance = None
141 self.state_server = None
142 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800143 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800144 self.event_server = None
145 self.event_server_thread = None
146 self.event_client = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800147 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800148 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800149 self.testlog = None
Vic Yange2c76a82014-10-30 12:48:19 -0700150 self.autotest_prespawner = None
Earl Oua3bca122016-10-21 16:00:30 +0800151 self.plugin_controller = None
Vic Yange2c76a82014-10-30 12:48:19 -0700152 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800153 self.ui_process = None
Vic Yanga3cecf82014-12-26 00:44:21 -0800154 self._ui_initialized = False
Jon Salzc79a9982012-08-30 04:42:01 +0800155 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800156 self.invocations = {}
Jon Salz0697cbf2012-07-04 15:14:04 +0800157 self.visible_test = None
158 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800159 self.hooks = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800160
161 self.options = None
162 self.args = None
163 self.test_list = None
Jon Salz128b0932013-07-03 16:55:26 +0800164 self.test_lists = None
Ricky Liang4bff3e32014-02-20 18:46:11 +0800165 self.run_id = None
166 self.scheduled_run_tests = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800167 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800168 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800169 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800170 self.last_update_check = None
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800171 self._suppress_periodic_update_messages = False
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +0800172 self._suppress_event_log_error_messages = False
Earl Ouab979142016-10-25 16:48:06 +0800173 self.exclusive_resources = set()
Dean Liao592e4d52013-01-10 20:06:39 +0800174 self.key_filter = None
Jon Salzd7550792013-07-12 05:49:27 +0800175 self.status = Status.UNINITIALIZED
Ricky Liang36512a32014-07-25 11:47:04 +0800176 self.ready_for_ui_connection = False
Peter Ammon1e1ec572014-06-26 17:56:32 -0700177 self.link_manager = None
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800178 self.is_restart_requested = False
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800179 self.test_list_iterator = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800180
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800181 # TODO(hungte) Support controlling remote DUT.
Hung-Te Linb6287242016-05-18 14:39:05 +0800182 self.dut = device_utils.CreateDUTInterface()
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800183
Jon Salz85a39882012-07-05 16:45:04 +0800184 def test_or_root(event, parent_or_group=True):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800185 """Returns the test affected by a particular event.
Jon Salz85a39882012-07-05 16:45:04 +0800186
187 Args:
188 event: The event containing an optional 'path' attribute.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800189 parent_or_group: If True, returns the top-level parent for a test (the
Jon Salz85a39882012-07-05 16:45:04 +0800190 root node of the tests that need to be run together if the given test
191 path is to be run).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800192 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800193 try:
194 path = event.path
195 except AttributeError:
196 path = None
197
198 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800199 test = self.test_list.lookup_path(path)
200 if parent_or_group:
201 test = test.get_top_level_parent_or_group()
202 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800203 else:
204 return self.test_list
205
206 self.event_handlers = {
Ricky Liang45c73e72015-01-15 15:00:30 +0800207 Event.Type.SWITCH_TEST: self.handle_switch_test,
Ricky Liang45c73e72015-01-15 15:00:30 +0800208 Event.Type.RESTART_TESTS:
209 lambda event: self.restart_tests(root=test_or_root(event)),
210 Event.Type.AUTO_RUN:
211 lambda event: self.auto_run(root=test_or_root(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800212 Event.Type.RUN_TESTS_WITH_STATUS:
213 lambda event: self.run_tests_with_status(
214 event.status,
215 root=test_or_root(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800216 Event.Type.UPDATE_SYSTEM_INFO:
217 lambda event: self.update_system_info(),
218 Event.Type.STOP:
219 lambda event: self.stop(root=test_or_root(event, False),
220 fail=getattr(event, 'fail', False),
221 reason=getattr(event, 'reason', None)),
222 Event.Type.SET_VISIBLE_TEST:
223 lambda event: self.set_visible_test(
224 self.test_list.lookup_path(event.path)),
225 Event.Type.CLEAR_STATE:
226 lambda event: self.clear_state(
227 self.test_list.lookup_path(event.path)),
Wei-Ning Huang38b75f02015-02-25 18:25:14 +0800228 Event.Type.KEY_FILTER_MODE: self.handle_key_filter_mode,
Jon Salz0697cbf2012-07-04 15:14:04 +0800229 }
230
Jon Salz0697cbf2012-07-04 15:14:04 +0800231 self.web_socket_manager = None
Wei-Ning Huang38b75f02015-02-25 18:25:14 +0800232 self.terminal_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800233
234 def destroy(self):
Ricky Liang74237a02014-09-18 15:11:23 +0800235 """Performs any shutdown tasks. Overrides base class method."""
Jon Salzd7550792013-07-12 05:49:27 +0800236 self.status = Status.TERMINATING
Jon Salz0697cbf2012-07-04 15:14:04 +0800237 if self.chrome:
238 self.chrome.kill()
239 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800240 if self.dummy_shopfloor:
241 self.dummy_shopfloor.kill()
242 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800243 if self.ui_process:
Hung-Te Lin4e6357c2016-01-08 14:32:00 +0800244 process_utils.KillProcessTree(self.ui_process, 'ui')
Jon Salz0697cbf2012-07-04 15:14:04 +0800245 self.ui_process = None
246 if self.web_socket_manager:
247 logging.info('Stopping web sockets')
248 self.web_socket_manager.close()
249 self.web_socket_manager = None
250 if self.state_server_thread:
251 logging.info('Stopping state server')
252 self.state_server.shutdown()
253 self.state_server_thread.join()
254 self.state_server.server_close()
255 self.state_server_thread = None
256 if self.state_instance:
257 self.state_instance.close()
258 if self.event_server_thread:
259 logging.info('Stopping event server')
260 self.event_server.shutdown() # pylint: disable=E1101
261 self.event_server_thread.join()
262 self.event_server.server_close()
263 self.event_server_thread = None
264 if self.log_watcher:
265 if self.log_watcher.IsThreadStarted():
266 self.log_watcher.StopWatchThread()
267 self.log_watcher = None
Vic Yange2c76a82014-10-30 12:48:19 -0700268 if self.autotest_prespawner:
269 logging.info('Stopping autotest prespawner')
270 self.autotest_prespawner.stop()
271 self.autotest_prespawner = None
272 if self.pytest_prespawner:
273 logging.info('Stopping pytest prespawner')
274 self.pytest_prespawner.stop()
275 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800276 if self.event_client:
277 logging.info('Closing event client')
278 self.event_client.close()
279 self.event_client = None
280 if self.event_log:
281 self.event_log.Close()
282 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800283 if self.testlog:
284 self.testlog.Close()
285 self.testlog = None
Dean Liao592e4d52013-01-10 20:06:39 +0800286 if self.key_filter:
287 self.key_filter.Stop()
Peter Ammon1e1ec572014-06-26 17:56:32 -0700288 if self.link_manager:
289 self.link_manager.Stop()
290 self.link_manager = None
Earl Oua3bca122016-10-21 16:00:30 +0800291 if self.plugin_controller:
292 self.plugin_controller.StopAndDestroyAllPlugins()
293 self.plugin_controller = None
Dean Liao592e4d52013-01-10 20:06:39 +0800294
Peter Ammon1e1ec572014-06-26 17:56:32 -0700295 super(Goofy, self).destroy()
Jon Salz0697cbf2012-07-04 15:14:04 +0800296 logging.info('Done destroying Goofy')
Jon Salzd7550792013-07-12 05:49:27 +0800297 self.status = Status.TERMINATED
Jon Salz0697cbf2012-07-04 15:14:04 +0800298
299 def start_state_server(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800300 # Before starting state server, remount stateful partitions with
301 # no commit flag. The default commit time (commit=600) makes corruption
302 # too likely.
Hung-Te Lin1968d9c2016-01-08 22:55:46 +0800303 sys_utils.ResetCommitTime()
Jon Salz2af235d2013-06-24 14:47:21 +0800304
Jon Salz0697cbf2012-07-04 15:14:04 +0800305 self.state_instance, self.state_server = (
Ricky Liang45c73e72015-01-15 15:00:30 +0800306 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800307 self.goofy_rpc = GoofyRPC(self)
308 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800309 logging.info('Starting state server')
310 self.state_server_thread = threading.Thread(
Ricky Liang45c73e72015-01-15 15:00:30 +0800311 target=self.state_server.serve_forever,
312 name='StateServer')
Jon Salz0697cbf2012-07-04 15:14:04 +0800313 self.state_server_thread.start()
314
315 def start_event_server(self):
316 self.event_server = EventServer()
317 logging.info('Starting factory event server')
318 self.event_server_thread = threading.Thread(
Ricky Liang45c73e72015-01-15 15:00:30 +0800319 target=self.event_server.serve_forever,
320 name='EventServer') # pylint: disable=E1101
Jon Salz0697cbf2012-07-04 15:14:04 +0800321 self.event_server_thread.start()
322
323 self.event_client = EventClient(
Ricky Liang45c73e72015-01-15 15:00:30 +0800324 callback=self.handle_event, event_loop=self.run_queue)
Jon Salz0697cbf2012-07-04 15:14:04 +0800325
326 self.web_socket_manager = WebSocketManager(self.uuid)
Ricky Liang45c73e72015-01-15 15:00:30 +0800327 self.state_server.add_handler('/event',
328 self.web_socket_manager.handle_web_socket)
Jon Salz0697cbf2012-07-04 15:14:04 +0800329
Wei-Ning Huang38b75f02015-02-25 18:25:14 +0800330 def start_terminal_server(self):
331 self.terminal_manager = TerminalManager()
332 self.state_server.add_handler('/pty',
333 self.terminal_manager.handle_web_socket)
334
Jon Salz0697cbf2012-07-04 15:14:04 +0800335 def start_ui(self):
336 ui_proc_args = [
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +0800337 os.path.join(paths.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
Ricky Liang45c73e72015-01-15 15:00:30 +0800338 self.options.test_list
339 ]
Jon Salz0697cbf2012-07-04 15:14:04 +0800340 if self.options.verbose:
341 ui_proc_args.append('-v')
342 logging.info('Starting ui %s', ui_proc_args)
Hung-Te Lin4e6357c2016-01-08 14:32:00 +0800343 self.ui_process = process_utils.Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800344 logging.info('Waiting for UI to come up...')
345 self.event_client.wait(
Ricky Liang45c73e72015-01-15 15:00:30 +0800346 lambda event: event.type == Event.Type.UI_READY)
Jon Salz0697cbf2012-07-04 15:14:04 +0800347 logging.info('UI has started')
348
349 def set_visible_test(self, test):
350 if self.visible_test == test:
351 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800352 if test and not test.has_ui:
353 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800354
355 if test:
356 test.update_state(visible=True)
357 if self.visible_test:
358 self.visible_test.update_state(visible=False)
359 self.visible_test = test
360
Ricky Liang48e47f92014-02-26 19:31:51 +0800361 def log_startup_messages(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800362 """Logs the tail of var/log/messages and mosys and EC console logs."""
Jon Salzd4306c82012-11-30 15:16:36 +0800363 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
364 # for factory-3004.B only. Consolidate and merge back to ToT.
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800365 if sys_utils.InChroot():
Jon Salzd4306c82012-11-30 15:16:36 +0800366 return
367
368 try:
Hung-Te Lin1a4e30c2016-01-08 23:25:10 +0800369 var_log_messages = sys_utils.GetVarLogMessagesBeforeReboot()
Jon Salzd4306c82012-11-30 15:16:36 +0800370 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +0800371 'Tail of /var/log/messages before last reboot:\n'
372 '%s', ('\n'.join(
373 ' ' + x for x in var_log_messages)))
Jon Salzd4306c82012-11-30 15:16:36 +0800374 except: # pylint: disable=W0702
375 logging.exception('Unable to grok /var/log/messages')
376
377 try:
Hung-Te Lin4e6357c2016-01-08 14:32:00 +0800378 mosys_log = process_utils.Spawn(
Jon Salzd4306c82012-11-30 15:16:36 +0800379 ['mosys', 'eventlog', 'list'],
380 read_stdout=True, log_stderr_on_error=True).stdout_data
381 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
382 except: # pylint: disable=W0702
383 logging.exception('Unable to read mosys eventlog')
384
Dean Liao88b93192014-10-23 19:37:41 +0800385 self.log_ec_console()
386 self.log_ec_panic_info()
387
388 @staticmethod
389 def log_ec_console():
390 """Logs EC console log into logging.info.
391
392 It logs an error message in logging.exception if an exception is raised
393 when getting EC console log.
394 For unsupported device, it logs unsupport message in logging.info
395
396 Returns:
397 EC console log string.
398 """
Jon Salzd4306c82012-11-30 15:16:36 +0800399 try:
Hung-Te Linb6287242016-05-18 14:39:05 +0800400 ec_console_log = device_utils.CreateDUTInterface().ec.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800401 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Dean Liao88b93192014-10-23 19:37:41 +0800402 return ec_console_log
403 except NotImplementedError:
404 logging.info('EC console log not supported')
Jon Salzd4306c82012-11-30 15:16:36 +0800405 except: # pylint: disable=W0702
406 logging.exception('Error retrieving EC console log')
407
Dean Liao88b93192014-10-23 19:37:41 +0800408 @staticmethod
409 def log_ec_panic_info():
410 """Logs EC panic info into logging.info.
411
412 It logs an error message in logging.exception if an exception is raised
413 when getting EC panic info.
414 For unsupported device, it logs unsupport message in logging.info
415
416 Returns:
417 EC panic info string.
418 """
Vic Yang079f9872013-07-01 11:32:00 +0800419 try:
Hung-Te Linb6287242016-05-18 14:39:05 +0800420 ec_panic_info = device_utils.CreateDUTInterface().ec.GetECPanicInfo()
Vic Yang079f9872013-07-01 11:32:00 +0800421 logging.info('EC panic info after reboot:\n%s\n', ec_panic_info)
Dean Liao88b93192014-10-23 19:37:41 +0800422 return ec_panic_info
423 except NotImplementedError:
424 logging.info('EC panic info is not supported')
Vic Yang079f9872013-07-01 11:32:00 +0800425 except: # pylint: disable=W0702
426 logging.exception('Error retrieving EC panic info')
427
Ricky Liang48e47f92014-02-26 19:31:51 +0800428 def shutdown(self, operation):
429 """Starts shutdown procedure.
430
431 Args:
Vic (Chun-Ju) Yang05b0d952014-04-28 17:39:09 +0800432 operation: The shutdown operation (reboot, full_reboot, or halt).
Ricky Liang48e47f92014-02-26 19:31:51 +0800433 """
434 active_tests = []
435 for test in self.test_list.walk():
436 if not test.is_leaf():
437 continue
438
439 test_state = test.get_state()
440 if test_state.status == TestState.ACTIVE:
441 active_tests.append(test)
442
Ricky Liang48e47f92014-02-26 19:31:51 +0800443 if not (len(active_tests) == 1 and
444 isinstance(active_tests[0], factory.ShutdownStep)):
445 logging.error(
446 'Calling Goofy shutdown outside of the shutdown factory test')
447 return
448
449 logging.info('Start Goofy shutdown (%s)', operation)
450 # Save pending test list in the state server
451 self.state_instance.set_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800452 TESTS_AFTER_SHUTDOWN, self.test_list_iterator)
Ricky Liang48e47f92014-02-26 19:31:51 +0800453 # Save shutdown time
454 self.state_instance.set_shared_data('shutdown_time', time.time())
455
456 with self.env.lock:
457 self.event_log.Log('shutdown', operation=operation)
458 shutdown_result = self.env.shutdown(operation)
459 if shutdown_result:
460 # That's all, folks!
Peter Ammon1e1ec572014-06-26 17:56:32 -0700461 self.run_enqueue(None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800462 else:
463 # Just pass (e.g., in the chroot).
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800464 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800465 # Send event with no fields to indicate that there is no
466 # longer a pending shutdown.
467 self.event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
468
469 def handle_shutdown_complete(self, test):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800470 """Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800471
Ricky Liang6fe218c2013-12-27 15:17:17 +0800472 Args:
473 test: The ShutdownStep.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800474 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800475 test_state = test.update_state(increment_shutdown_count=1)
476 logging.info('Detected shutdown (%d of %d)',
Ricky Liang48e47f92014-02-26 19:31:51 +0800477 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800478
Ricky Liang48e47f92014-02-26 19:31:51 +0800479 tests_after_shutdown = self.state_instance.get_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800480 TESTS_AFTER_SHUTDOWN, optional=True)
481
482 # Make this shutdown test the next test to run. This is to continue on
483 # post-shutdown verification in the shutdown step.
Ricky Liang48e47f92014-02-26 19:31:51 +0800484 if not tests_after_shutdown:
Ricky Liang48e47f92014-02-26 19:31:51 +0800485 self.state_instance.set_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800486 TESTS_AFTER_SHUTDOWN, TestListIterator(test))
487 else:
488 # unset inited, so we will start from the reboot test.
489 tests_after_shutdown.inited = False
490 self.state_instance.set_shared_data(
491 TESTS_AFTER_SHUTDOWN, tests_after_shutdown)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800492
Ricky Liang48e47f92014-02-26 19:31:51 +0800493 # Set 'post_shutdown' to inform shutdown test that a shutdown just occurred.
Ricky Liangb7eb8772014-09-15 18:05:22 +0800494 self.state_instance.set_shared_data(
495 state.POST_SHUTDOWN_TAG % test.path,
496 self.state_instance.get_test_state(test.path).invocation)
Jon Salz258a40c2012-04-19 12:34:01 +0800497
Jon Salz0697cbf2012-07-04 15:14:04 +0800498 def init_states(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800499 """Initializes all states on startup."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800500 for test in self.test_list.get_all_tests():
501 # Make sure the state server knows about all the tests,
502 # defaulting to an untested state.
503 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800504
Jon Salz0697cbf2012-07-04 15:14:04 +0800505 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800506 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800507 ec_console_log = None
Vic Yang079f9872013-07-01 11:32:00 +0800508 ec_panic_info = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800509
Jon Salz0697cbf2012-07-04 15:14:04 +0800510 # Any 'active' tests should be marked as failed now.
511 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800512 if not test.is_leaf():
513 # Don't bother with parents; they will be updated when their
514 # children are updated.
515 continue
516
Jon Salz0697cbf2012-07-04 15:14:04 +0800517 test_state = test.get_state()
518 if test_state.status != TestState.ACTIVE:
519 continue
520 if isinstance(test, factory.ShutdownStep):
521 # Shutdown while the test was active - that's good.
Ricky Liang48e47f92014-02-26 19:31:51 +0800522 self.handle_shutdown_complete(test)
Jon Salz0697cbf2012-07-04 15:14:04 +0800523 else:
524 # Unexpected shutdown. Grab /var/log/messages for context.
525 if var_log_messages is None:
526 try:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800527 var_log_messages = sys_utils.GetVarLogMessagesBeforeReboot()
Jon Salz0697cbf2012-07-04 15:14:04 +0800528 # Write it to the log, to make it easier to
529 # correlate with /var/log/messages.
530 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +0800531 'Unexpected shutdown. '
532 'Tail of /var/log/messages before last reboot:\n'
533 '%s', ('\n'.join(
534 ' ' + x for x in var_log_messages)))
Jon Salz0697cbf2012-07-04 15:14:04 +0800535 except: # pylint: disable=W0702
536 logging.exception('Unable to grok /var/log/messages')
537 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800538
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800539 if mosys_log is None and not sys_utils.InChroot():
Jon Salz008f4ea2012-08-28 05:39:45 +0800540 try:
Hung-Te Lin4e6357c2016-01-08 14:32:00 +0800541 mosys_log = process_utils.Spawn(
Jon Salz008f4ea2012-08-28 05:39:45 +0800542 ['mosys', 'eventlog', 'list'],
543 read_stdout=True, log_stderr_on_error=True).stdout_data
544 # Write it to the log also.
545 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
546 except: # pylint: disable=W0702
547 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800548
Vic Yange4c275d2012-08-28 01:50:20 +0800549 if ec_console_log is None:
Dean Liao88b93192014-10-23 19:37:41 +0800550 ec_console_log = self.log_ec_console()
Vic Yange4c275d2012-08-28 01:50:20 +0800551
Vic Yang079f9872013-07-01 11:32:00 +0800552 if ec_panic_info is None:
Dean Liao88b93192014-10-23 19:37:41 +0800553 ec_panic_info = self.log_ec_panic_info()
Vic Yang079f9872013-07-01 11:32:00 +0800554
Jon Salz0697cbf2012-07-04 15:14:04 +0800555 error_msg = 'Unexpected shutdown while test was running'
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800556 # TODO(itspeter): Add testlog to collect expired session infos.
Jon Salz0697cbf2012-07-04 15:14:04 +0800557 self.event_log.Log('end_test',
Ricky Liang45c73e72015-01-15 15:00:30 +0800558 path=test.path,
559 status=TestState.FAILED,
560 invocation=test.get_state().invocation,
561 error_msg=error_msg,
562 var_log_messages='\n'.join(var_log_messages),
563 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800564 test.update_state(
Ricky Liang45c73e72015-01-15 15:00:30 +0800565 status=TestState.FAILED,
566 error_msg=error_msg)
Chun-Ta Lin87c2dac2015-05-02 01:35:01 -0700567 # Trigger the OnTestFailure callback.
Claire Changd1961a22015-08-05 16:15:55 +0800568 self.run_queue.put(lambda: self.test_fail(test))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800569
Jon Salz50efe942012-07-26 11:54:10 +0800570 if not test.never_fails:
571 # For "never_fails" tests (such as "Start"), don't cancel
572 # pending tests, since reboot is expected.
573 factory.console.info('Unexpected shutdown while test %s '
574 'running; cancelling any pending tests',
575 test.path)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800576 # cancel pending tests by replace the iterator with an empty one
577 self.state_instance.set_shared_data(
578 TESTS_AFTER_SHUTDOWN,
579 TestListIterator(None))
Jon Salz008f4ea2012-08-28 05:39:45 +0800580
Jon Salz0697cbf2012-07-04 15:14:04 +0800581 def handle_event(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800582 """Handles an event from the event server."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800583 handler = self.event_handlers.get(event.type)
584 if handler:
585 handler(event)
586 else:
587 # We don't register handlers for all event types - just ignore
588 # this event.
589 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800590
Vic Yangaabf9fd2013-04-09 18:56:13 +0800591 def check_critical_factory_note(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800592 """Returns True if the last factory note is critical."""
Vic Yangaabf9fd2013-04-09 18:56:13 +0800593 notes = self.state_instance.get_shared_data('factory_note', True)
594 return notes and notes[-1]['level'] == 'CRITICAL'
595
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800596 def schedule_restart(self):
597 """Schedules a restart event when any invocation is completed."""
598 self.is_restart_requested = True
599
600 def invocation_completion(self):
601 """Callback when an invocation is completed."""
602 if self.is_restart_requested:
603 logging.info('Restart by scheduled event.')
604 self.is_restart_requested = False
605 self.restart_tests()
606 else:
607 self.run_next_test()
608
Jon Salz0697cbf2012-07-04 15:14:04 +0800609 def run_next_test(self):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800610 """Runs the next eligible test.
henryhsu4cc6b022014-04-22 17:12:42 +0800611
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800612 self.test_list_iterator (a TestListIterator object) will determine which
613 test should be run.
henryhsu4cc6b022014-04-22 17:12:42 +0800614 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800615 self.reap_completed_tests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800616
617 if self.invocations:
618 # there are tests still running, we cannot start new tests
Vic Yangaabf9fd2013-04-09 18:56:13 +0800619 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800620
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800621 if self.check_critical_factory_note():
622 logging.info('has critical factory note, stop running')
623 self.test_list_iterator.stop()
624 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800625
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800626 while True:
627 try:
628 path = self.test_list_iterator.next()
629 test = self.test_list.lookup_path(path)
630 except StopIteration:
631 logging.info('no next test, stop running')
Jon Salz0697cbf2012-07-04 15:14:04 +0800632 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800633
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800634 # check if we have run all required tests
Jon Salz304a75d2012-07-06 11:14:15 +0800635 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800636 for requirement in test.require_run:
637 for i in requirement.test.walk():
638 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800639 # We've hit this test itself; stop checking
640 break
Jon Salza1412922012-07-23 16:04:17 +0800641 if ((i.get_state().status == TestState.UNTESTED) or
642 (requirement.passed and i.get_state().status !=
643 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800644 # Found an untested test; move on to the next
645 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800646 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800647 break
648
649 if untested:
650 untested_paths = ', '.join(sorted([x.path for x in untested]))
651 if self.state_instance.get_shared_data('engineering_mode',
652 optional=True):
653 # In engineering mode, we'll let it go.
654 factory.console.warn('In engineering mode; running '
655 '%s even though required tests '
656 '[%s] have not completed',
657 test.path, untested_paths)
658 else:
659 # Not in engineering mode; mark it failed.
660 error_msg = ('Required tests [%s] have not been run yet'
661 % untested_paths)
662 factory.console.error('Not running %s: %s',
663 test.path, error_msg)
664 test.update_state(status=TestState.FAILED,
665 error_msg=error_msg)
666 continue
667
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800668 # okay, let's run the test
Ricky Liang48e47f92014-02-26 19:31:51 +0800669 if (isinstance(test, factory.ShutdownStep) and
Ricky Liangb7eb8772014-09-15 18:05:22 +0800670 self.state_instance.get_shared_data(
671 state.POST_SHUTDOWN_TAG % test.path, optional=True)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800672 # Invoking post shutdown method of shutdown test. We should retain the
673 # iterations_left and retries_left of the original test state.
674 test_state = self.state_instance.get_test_state(test.path)
675 self._run_test(test, test_state.iterations_left,
676 test_state.retries_left)
677 else:
678 # Starts a new test run; reset iterations and retries.
679 self._run_test(test, test.iterations, test.retries)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800680 return # to leave while
Jon Salz1acc8742012-07-17 17:45:55 +0800681
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800682 def _run_test(self, test, iterations_left=None, retries_left=None):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800683 """Invokes the test.
684
685 The argument `test` should be either a leaf test (no subtests) or a parallel
686 test (all subtests should be run in parallel).
687 """
Vic Yanga3cecf82014-12-26 00:44:21 -0800688 if not self._ui_initialized and not test.is_no_host():
689 self.init_ui()
Jon Salz1acc8742012-07-17 17:45:55 +0800690
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800691 if test.is_leaf():
692 invoc = TestInvocation(
693 self, test, on_completion=self.invocation_completion,
694 on_test_failure=lambda: self.test_fail(test))
695 new_state = test.update_state(
696 status=TestState.ACTIVE, increment_count=1, error_msg='',
697 invocation=invoc.uuid, iterations_left=iterations_left,
698 retries_left=retries_left,
699 visible=(self.visible_test == test))
700 invoc.count = new_state.count
701 self.invocations[test] = invoc
702 if self.visible_test is None and test.has_ui:
703 self.set_visible_test(test)
704 self.check_plugins()
705 invoc.start()
706 else:
707 assert test.is_parallel()
708 for subtest in test.subtests:
709 # TODO(stimim): what if the subtests *must* be run in parallel?
710 # for example, stressapptest and countdown test.
711
712 # Make sure we don't need to skip it:
713 if not self.test_list_iterator.check_skip(subtest):
714 self._run_test(subtest, subtest.iterations, subtest.retries)
Jon Salz5f2a0672012-05-22 17:14:06 +0800715
Earl Oua3bca122016-10-21 16:00:30 +0800716 def check_plugins(self):
717 """Check plugins to be paused or resumed."""
718 exclusive_resources = set()
719 for test in self.invocations:
720 exclusive_resources = exclusive_resources.union(
721 test.get_exclusive_resources())
722 self.plugin_controller.PauseAndResumePluginByResource(exclusive_resources)
723
cychiang21886742012-07-05 15:16:32 +0800724 def check_for_updates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800725 """Schedules an asynchronous check for updates if necessary."""
cychiang21886742012-07-05 15:16:32 +0800726 if not self.test_list.options.update_period_secs:
727 # Not enabled.
728 return
729
730 now = time.time()
731 if self.last_update_check and (
732 now - self.last_update_check <
733 self.test_list.options.update_period_secs):
734 # Not yet time for another check.
735 return
736
737 self.last_update_check = now
738
739 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
740 if reached_shopfloor:
741 new_update_md5sum = md5sum if needs_update else None
Hung-Te Line594e5d2015-12-16 02:36:05 +0800742 if self.dut.info.update_md5sum != new_update_md5sum:
cychiang21886742012-07-05 15:16:32 +0800743 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
Hung-Te Line594e5d2015-12-16 02:36:05 +0800744 self.dut.info.Overrides('update_md5sum', new_update_md5sum)
Peter Ammon1e1ec572014-06-26 17:56:32 -0700745 self.run_enqueue(self.update_system_info)
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800746 else:
747 if not self._suppress_periodic_update_messages:
748 logging.warning('Suppress error messages for periodic update checking'
749 ' after the first one.')
750 self._suppress_periodic_update_messages = True
cychiang21886742012-07-05 15:16:32 +0800751
752 updater.CheckForUpdateAsync(
Ricky Liang45c73e72015-01-15 15:00:30 +0800753 handle_check_for_update,
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800754 self.test_list.options.shopfloor_timeout_secs,
755 self._suppress_periodic_update_messages)
cychiang21886742012-07-05 15:16:32 +0800756
Jon Salza6711d72012-07-18 14:33:03 +0800757 def cancel_pending_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800758 """Cancels any tests in the run queue."""
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800759 self.run_tests(None)
Jon Salza6711d72012-07-18 14:33:03 +0800760
Ricky Liang4bff3e32014-02-20 18:46:11 +0800761 def restore_active_run_state(self):
762 """Restores active run id and the list of scheduled tests."""
763 self.run_id = self.state_instance.get_shared_data('run_id', optional=True)
764 self.scheduled_run_tests = self.state_instance.get_shared_data(
765 'scheduled_run_tests', optional=True)
766
767 def set_active_run_state(self):
768 """Sets active run id and the list of scheduled tests."""
769 self.run_id = str(uuid.uuid4())
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800770 # try our best to predict which tests will be run.
771 self.scheduled_run_tests = self.test_list_iterator.get_pending_tests()
Ricky Liang4bff3e32014-02-20 18:46:11 +0800772 self.state_instance.set_shared_data('run_id', self.run_id)
773 self.state_instance.set_shared_data('scheduled_run_tests',
774 self.scheduled_run_tests)
775
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800776 def run_tests(self, subtree, status_filter=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800777 """Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800778
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800779 Run tests under a given subtree.
Jon Salzb1b39092012-05-03 02:05:09 +0800780
Ricky Liang6fe218c2013-12-27 15:17:17 +0800781 Args:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800782 subtree: root of subtree to run or None to run nothing.
Chih-Yu Huang85dc63c2015-08-12 15:21:28 +0800783 status_filter: List of available test states. Only run the tests which
784 states are in the list. Set to None if all test states are available.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800785 """
Hung-Te Lin410f70a2015-12-15 14:53:42 +0800786 self.dut.hooks.OnTestStart()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800787 self.test_list_iterator = TestListIterator(
788 subtree, status_filter, self.test_list)
789 if subtree is not None:
Ricky Liang4bff3e32014-02-20 18:46:11 +0800790 self.set_active_run_state()
Jon Salz0697cbf2012-07-04 15:14:04 +0800791 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800792
Jon Salz0697cbf2012-07-04 15:14:04 +0800793 def reap_completed_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800794 """Removes completed tests from the set of active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800795
796 Also updates the visible test if it was reaped.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800797 """
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800798 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800799 for t, v in dict(self.invocations).iteritems():
800 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800801 test_completed = True
Jon Salz1acc8742012-07-17 17:45:55 +0800802 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800803 del self.invocations[t]
804
Johny Lin62ed2a32015-05-13 11:57:12 +0800805 # Stop on failure if flag is true and there is no retry chances.
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800806 if (self.test_list.options.stop_on_failure and
Johny Lin62ed2a32015-05-13 11:57:12 +0800807 new_state.retries_left < 0 and
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800808 new_state.status == TestState.FAILED):
809 # Clean all the tests to cause goofy to stop.
Ricky Liang45c73e72015-01-15 15:00:30 +0800810 factory.console.info('Stop on failure triggered. Empty the queue.')
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800811 self.cancel_pending_tests()
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800812
Jon Salz1acc8742012-07-17 17:45:55 +0800813 if new_state.iterations_left and new_state.status == TestState.PASSED:
814 # Play it again, Sam!
815 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800816 # new_state.retries_left is obtained after update.
817 # For retries_left == 0, test can still be run for the last time.
818 elif (new_state.retries_left >= 0 and
819 new_state.status == TestState.FAILED):
820 # Still have to retry, Sam!
821 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +0800822
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800823 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800824 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800825
Jon Salz0697cbf2012-07-04 15:14:04 +0800826 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800827 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800828 self.set_visible_test(None)
829 # Make the first running test, if any, the visible test
830 for t in self.test_list.walk():
831 if t in self.invocations:
832 self.set_visible_test(t)
833 break
834
Jon Salz6dc031d2013-06-19 13:06:23 +0800835 def kill_active_tests(self, abort, root=None, reason=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800836 """Kills and waits for all active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800837
Jon Salz85a39882012-07-05 16:45:04 +0800838 Args:
839 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800840 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800841 root: If set, only kills tests with root as an ancestor.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800842 reason: If set, the abort reason.
843 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800844 self.reap_completed_tests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800845 # since we remove objects while iterating, make a copy
846 for test, invoc in dict(self.invocations).iteritems():
Jon Salz85a39882012-07-05 16:45:04 +0800847 if root and not test.has_ancestor(root):
848 continue
849
Ricky Liang45c73e72015-01-15 15:00:30 +0800850 factory.console.info('Killing active test %s...', test.path)
Jon Salz6dc031d2013-06-19 13:06:23 +0800851 invoc.abort_and_join(reason)
Ricky Liang45c73e72015-01-15 15:00:30 +0800852 factory.console.info('Killed %s', test.path)
Jon Salz1acc8742012-07-17 17:45:55 +0800853 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800854 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800855
Jon Salz0697cbf2012-07-04 15:14:04 +0800856 if not abort:
857 test.update_state(status=TestState.UNTESTED)
858 self.reap_completed_tests()
859
Jon Salz6dc031d2013-06-19 13:06:23 +0800860 def stop(self, root=None, fail=False, reason=None):
861 self.kill_active_tests(fail, root, reason)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800862
863 if not root:
864 self.test_list_iterator.stop()
865 else:
866 # only skip tests under `root`
867 self.test_list_iterator = itertools.dropwhile(
868 lambda path: self.test_list.lookup_path(path).has_ancestor(root),
869 self.test_list_iterator)
Jon Salz85a39882012-07-05 16:45:04 +0800870 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800871
Jon Salz4712ac72013-02-07 17:12:05 +0800872 def clear_state(self, root=None):
Jon Salzd7550792013-07-12 05:49:27 +0800873 if root is None:
874 root = self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +0800875 self.stop(root, reason='Clearing test state')
Jon Salz4712ac72013-02-07 17:12:05 +0800876 for f in root.walk():
877 if f.is_leaf():
878 f.update_state(status=TestState.UNTESTED)
879
Jon Salz6dc031d2013-06-19 13:06:23 +0800880 def abort_active_tests(self, reason=None):
881 self.kill_active_tests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +0800882
883 def main(self):
Jon Salzeff94182013-06-19 15:06:28 +0800884 syslog.openlog('goofy')
885
Jon Salz0697cbf2012-07-04 15:14:04 +0800886 try:
Jon Salzd7550792013-07-12 05:49:27 +0800887 self.status = Status.INITIALIZING
Jon Salz0697cbf2012-07-04 15:14:04 +0800888 self.init()
889 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800890 success=True)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800891 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800892 testlog.StationInit({
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800893 'stationDeviceId': testlog_goofy.GetDeviceID(),
Joel Kitching21bc69b2016-07-13 08:29:52 -0700894 'stationInstallationId': testlog_goofy.GetInstallationID(),
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800895 'count': testlog_goofy.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800896 'success': True}))
Jon Salz0697cbf2012-07-04 15:14:04 +0800897 except:
Joel Kitching9eb203a2016-04-21 15:36:30 +0800898 try:
899 if self.event_log:
Jon Salz0697cbf2012-07-04 15:14:04 +0800900 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800901 success=False,
902 trace=traceback.format_exc())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800903 if self.testlog:
904 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800905 testlog.StationInit({
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800906 'stationDeviceId': testlog_goofy.GetDeviceID(),
Joel Kitching21bc69b2016-07-13 08:29:52 -0700907 'stationInstallationId': testlog_goofy.GetInstallationID(),
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800908 'count': testlog_goofy.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800909 'success': False,
910 'failureMessage': traceback.format_exc()}))
911 except: # pylint: disable=W0702
912 pass
Jon Salz0697cbf2012-07-04 15:14:04 +0800913 raise
914
Jon Salzd7550792013-07-12 05:49:27 +0800915 self.status = Status.RUNNING
Jon Salzeff94182013-06-19 15:06:28 +0800916 syslog.syslog('Goofy (factory test harness) starting')
Chun-Ta Lin5d12b592015-06-30 00:54:23 -0700917 syslog.syslog('Boot sequence = %d' % GetBootSequence())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800918 syslog.syslog('Goofy init count = %d' % testlog_goofy.GetInitCount())
Jon Salz0697cbf2012-07-04 15:14:04 +0800919 self.run()
920
921 def update_system_info(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800922 """Updates system info."""
Hung-Te Line594e5d2015-12-16 02:36:05 +0800923 info = self.dut.info.GetAll()
924 self.state_instance.set_shared_data('system_info', info)
Jon Salz0697cbf2012-07-04 15:14:04 +0800925 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
Hung-Te Line594e5d2015-12-16 02:36:05 +0800926 system_info=info))
927 logging.info('System info: %r', info)
Jon Salz0697cbf2012-07-04 15:14:04 +0800928
Jon Salzeb42f0d2012-07-27 19:14:04 +0800929 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800930 """Commences updating factory software.
Jon Salzeb42f0d2012-07-27 19:14:04 +0800931
932 Args:
933 auto_run_on_restart: Auto-run when the machine comes back up.
934 post_update_hook: Code to call after update but immediately before
935 restart.
936
937 Returns:
938 Never if the update was successful (we just reboot).
939 False if the update was unnecessary (no update available).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800940 """
Jon Salz6dc031d2013-06-19 13:06:23 +0800941 self.kill_active_tests(False, reason='Factory software update')
Jon Salza6711d72012-07-18 14:33:03 +0800942 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800943
Jon Salz5c344f62012-07-13 14:31:16 +0800944 def pre_update_hook():
945 if auto_run_on_restart:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800946 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN,
Jon Salz5c344f62012-07-13 14:31:16 +0800947 FORCE_AUTO_RUN)
948 self.state_instance.close()
949
Jon Salzeb42f0d2012-07-27 19:14:04 +0800950 if updater.TryUpdate(pre_update_hook=pre_update_hook):
951 if post_update_hook:
952 post_update_hook()
953 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800954
Ricky Liang8fecf412014-05-22 10:56:14 +0800955 def handle_sigint(self, dummy_signum, dummy_frame): # pylint: disable=W0613
Jon Salz77c151e2012-08-28 07:20:37 +0800956 logging.error('Received SIGINT')
Peter Ammon1e1ec572014-06-26 17:56:32 -0700957 self.run_enqueue(None)
Jon Salz77c151e2012-08-28 07:20:37 +0800958 raise KeyboardInterrupt()
959
Ricky Liang8fecf412014-05-22 10:56:14 +0800960 def handle_sigterm(self, dummy_signum, dummy_frame): # pylint: disable=W0613
961 logging.error('Received SIGTERM')
Hung-Te Lin94ca4742014-07-09 20:13:50 +0800962 self.env.terminate()
963 self.run_queue.put(None)
Ricky Liang8fecf412014-05-22 10:56:14 +0800964 raise RuntimeError('Received SIGTERM')
965
Jon Salze12c2b32013-06-25 16:24:34 +0800966 def find_kcrashes(self):
967 """Finds kcrash files, logs them, and marks them as seen."""
968 seen_crashes = set(
969 self.state_instance.get_shared_data('seen_crashes', optional=True)
970 or [])
971
972 for path in glob.glob('/var/spool/crash/*'):
973 if not os.path.isfile(path):
974 continue
975 if path in seen_crashes:
976 continue
977 try:
978 stat = os.stat(path)
Hung-Te Lin4e6357c2016-01-08 14:32:00 +0800979 mtime = time_utils.TimeString(stat.st_mtime)
Jon Salze12c2b32013-06-25 16:24:34 +0800980 logging.info(
981 'Found new crash file %s (%d bytes at %s)',
982 path, stat.st_size, mtime)
983 extra_log_args = {}
984
985 try:
986 _, ext = os.path.splitext(path)
987 if ext in ['.kcrash', '.meta']:
988 ext = ext.replace('.', '')
989 with open(path) as f:
990 data = f.read(MAX_CRASH_FILE_SIZE)
991 tell = f.tell()
992 logging.info(
993 'Contents of %s%s:%s',
994 path,
995 ('' if tell == stat.st_size
996 else '(truncated to %d bytes)' % MAX_CRASH_FILE_SIZE),
997 ('\n' + data).replace('\n', '\n ' + ext + '> '))
998 extra_log_args['data'] = data
999
1000 # Copy to /var/factory/kcrash for posterity
Joel Kitching625ff0f2016-05-16 14:59:40 -07001001 kcrash_dir = paths.GetFactoryRoot('kcrash')
Hung-Te Lin4e6357c2016-01-08 14:32:00 +08001002 file_utils.TryMakeDirs(kcrash_dir)
Jon Salze12c2b32013-06-25 16:24:34 +08001003 shutil.copy(path, kcrash_dir)
1004 logging.info('Copied to %s',
1005 os.path.join(kcrash_dir, os.path.basename(path)))
1006 finally:
1007 # Even if something goes wrong with the above, still try to
1008 # log to event log
1009 self.event_log.Log('crash_file',
1010 path=path, size=stat.st_size, mtime=mtime,
1011 **extra_log_args)
1012 except: # pylint: disable=W0702
1013 logging.exception('Unable to handle crash files %s', path)
1014 seen_crashes.add(path)
1015
1016 self.state_instance.set_shared_data('seen_crashes', list(seen_crashes))
1017
Jon Salz128b0932013-07-03 16:55:26 +08001018 def GetTestList(self, test_list_id):
1019 """Returns the test list with the given ID.
1020
1021 Raises:
1022 TestListError: The test list ID is not valid.
1023 """
1024 try:
1025 return self.test_lists[test_list_id]
1026 except KeyError:
1027 raise test_lists.TestListError(
1028 '%r is not a valid test list ID (available IDs are [%s])' % (
1029 test_list_id, ', '.join(sorted(self.test_lists.keys()))))
1030
1031 def InitTestLists(self):
Joel Kitching50a63ea2016-02-22 13:15:09 +08001032 """Reads in all test lists and sets the active test list.
1033
1034 Returns:
1035 True if the active test list could be set, False if failed.
1036 """
1037 startup_errors = []
1038 self.test_lists, failed_files = test_lists.BuildAllTestLists(
Ricky Liang27051552014-05-04 14:22:26 +08001039 force_generic=(self.options.automation_mode is not None))
Jon Salzd7550792013-07-12 05:49:27 +08001040 logging.info('Loaded test lists: [%s]',
1041 test_lists.DescribeTestLists(self.test_lists))
Jon Salz128b0932013-07-03 16:55:26 +08001042
Joel Kitching50a63ea2016-02-22 13:15:09 +08001043 # Check for any syntax errors in test list files.
1044 if failed_files:
1045 logging.info('Failed test list files: [%s]',
1046 ' '.join(failed_files.keys()))
1047 for f, exc_info in failed_files.iteritems():
1048 logging.error('Error in test list file: %s', f,
1049 exc_info=exc_info)
1050
1051 # Limit the stack trace to the very last entry.
1052 exc_type, exc_value, exc_traceback = exc_info
1053 while exc_traceback and exc_traceback.tb_next:
1054 exc_traceback = exc_traceback.tb_next
1055
1056 exc_string = ''.join(
1057 traceback.format_exception(
1058 exc_type, exc_value, exc_traceback)).rstrip()
1059 startup_errors.append('Error in test list file (%s):\n%s'
1060 % (f, exc_string))
1061
Jon Salz128b0932013-07-03 16:55:26 +08001062 if not self.options.test_list:
1063 self.options.test_list = test_lists.GetActiveTestListId()
1064
Joel Kitching50a63ea2016-02-22 13:15:09 +08001065 # Check for a non-existent test list ID.
1066 try:
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001067 self.test_list = self.GetTestList(self.options.test_list)
Joel Kitching50a63ea2016-02-22 13:15:09 +08001068 logging.info('Active test list: %s', self.test_list.test_list_id)
1069 except test_lists.TestListError as e:
1070 logging.exception('Invalid active test list: %s',
1071 self.options.test_list)
1072 startup_errors.append(e.message)
Jon Salz128b0932013-07-03 16:55:26 +08001073
Joel Kitching50a63ea2016-02-22 13:15:09 +08001074 # We may have failed loading the active test list.
1075 if self.test_list:
Joel Kitching50a63ea2016-02-22 13:15:09 +08001076 self.test_list.state_instance = self.state_instance
Jon Salz128b0932013-07-03 16:55:26 +08001077
Joel Kitching50a63ea2016-02-22 13:15:09 +08001078 # Prepare DUT link.
1079 if self.test_list.options.dut_options:
1080 logging.info('dut_options set by %s: %r', self.test_list.test_list_id,
1081 self.test_list.options.dut_options)
Hung-Te Linb6287242016-05-18 14:39:05 +08001082 device_utils.PrepareDUTLink(**self.test_list.options.dut_options)
Wei-Han Chene8a025f2016-01-14 16:42:02 +08001083
Joel Kitching50a63ea2016-02-22 13:15:09 +08001084 # Show all startup errors.
1085 if startup_errors:
1086 self.state_instance.set_shared_data(
1087 'startup_error', '\n\n'.join(startup_errors))
1088
1089 # Only return False if failed to load the active test list.
1090 return bool(self.test_list)
Jon Salz128b0932013-07-03 16:55:26 +08001091
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001092 def init_hooks(self):
1093 """Initializes hooks.
1094
1095 Must run after self.test_list ready.
1096 """
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001097 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1098 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
1099 assert isinstance(self.hooks, factory.Hooks), (
Ricky Liang45c73e72015-01-15 15:00:30 +08001100 'hooks should be of type Hooks but is %r' % type(self.hooks))
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001101 self.hooks.test_list = self.test_list
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001102 self.hooks.OnCreatedTestList()
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001103
Vic Yanga3cecf82014-12-26 00:44:21 -08001104 def init_ui(self):
1105 """Initialize UI."""
1106 self._ui_initialized = True
1107 if self.options.ui == 'chrome':
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001108 if self.options.monolithic:
Hung-Te Lin7bd55312014-12-30 16:43:36 +08001109 self.env.launch_chrome()
1110 else:
1111 # The presenter is responsible for launching Chrome. Let's just
1112 # wait here.
1113 self.env.controller_ready_for_ui()
Vic Yanga3cecf82014-12-26 00:44:21 -08001114 logging.info('Waiting for a web socket connection')
1115 self.web_socket_manager.wait()
1116
1117 # Wait for the test widget size to be set; this is done in
1118 # an asynchronous RPC so there is a small chance that the
1119 # web socket might be opened first.
1120 for _ in range(100): # 10 s
1121 try:
1122 if self.state_instance.get_shared_data('test_widget_size'):
1123 break
1124 except KeyError:
1125 pass # Retry
1126 time.sleep(0.1) # 100 ms
1127 else:
1128 logging.warn('Never received test_widget_size from UI')
1129
Jon Salz0697cbf2012-07-04 15:14:04 +08001130 def init(self, args=None, env=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001131 """Initializes Goofy.
Jon Salz0697cbf2012-07-04 15:14:04 +08001132
1133 Args:
1134 args: A list of command-line arguments. Uses sys.argv if
1135 args is None.
1136 env: An Environment instance to use (or None to choose
1137 FakeChrootEnvironment or DUTEnvironment as appropriate).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001138 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001139 parser = OptionParser()
1140 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001141 action='store_true',
1142 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001143 parser.add_option('--print_test_list', dest='print_test_list',
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001144 metavar='TEST_LIST_ID',
1145 help='Print the content of TEST_LIST_ID and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001146 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001147 action='store_true',
1148 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001149 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz7b5482e2014-08-04 17:48:41 +08001150 choices=['none', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001151 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001152 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001153 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001154 type='int', default=1,
1155 help=('Factor by which to scale UI '
1156 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001157 parser.add_option('--test_list', dest='test_list',
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001158 metavar='TEST_LIST_ID',
1159 help='Use test list whose id is TEST_LIST_ID')
Jon Salzc79a9982012-08-30 04:42:01 +08001160 parser.add_option('--dummy_shopfloor', action='store_true',
1161 help='Use a dummy shopfloor server')
Ricky Liang6fe218c2013-12-27 15:17:17 +08001162 parser.add_option('--automation-mode',
1163 choices=[m.lower() for m in AutomationMode],
Ricky Liang45c73e72015-01-15 15:00:30 +08001164 default='none', help='Factory test automation mode.')
Ricky Liang117484a2014-04-14 11:14:41 +08001165 parser.add_option('--no-auto-run-on-start', dest='auto_run_on_start',
1166 action='store_false', default=True,
1167 help=('do not automatically run the test list on goofy '
1168 'start; this is only valid when factory test '
1169 'automation is enabled'))
Chun-Ta Lina8dd3172014-11-26 16:15:13 +08001170 parser.add_option('--handshake_timeout', dest='handshake_timeout',
1171 type='float', default=0.3,
1172 help=('RPC timeout when doing handshake between device '
1173 'and presenter.'))
Vic Yang7d693c42014-09-14 09:52:39 +08001174 parser.add_option('--standalone', dest='standalone',
1175 action='store_true', default=False,
1176 help=('Assume the presenter is running on the same '
1177 'machines.'))
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001178 parser.add_option('--monolithic', dest='monolithic',
1179 action='store_true', default=False,
1180 help='Run in monolithic mode (without presenter)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001181 (self.options, self.args) = parser.parse_args(args)
1182
Hung-Te Lina846f602014-07-04 20:32:22 +08001183 signal.signal(signal.SIGINT, self.handle_sigint)
1184 # TODO(hungte) SIGTERM does not work properly without Telemetry and should
1185 # be fixed.
Hung-Te Lina846f602014-07-04 20:32:22 +08001186
Jon Salz46b89562012-07-05 11:49:22 +08001187 # Make sure factory directories exist.
Joel Kitching625ff0f2016-05-16 14:59:40 -07001188 paths.GetLogRoot()
1189 paths.GetStateRoot()
1190 paths.GetTestDataRoot()
Jon Salz46b89562012-07-05 11:49:22 +08001191
Jon Salz0697cbf2012-07-04 15:14:04 +08001192 global _inited_logging # pylint: disable=W0603
1193 if not _inited_logging:
1194 factory.init_logging('goofy', verbose=self.options.verbose)
1195 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001196
Jon Salz0f996602012-10-03 15:26:48 +08001197 if self.options.print_test_list:
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001198 test_list = test_lists.BuildTestList(self.options.print_test_list)
1199 print(test_list.__repr__(recursive=True))
Jon Salz0f996602012-10-03 15:26:48 +08001200 sys.exit(0)
1201
Jon Salzee85d522012-07-17 14:34:46 +08001202 event_log.IncrementBootSequence()
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001203 testlog_goofy.IncrementInitCount()
1204
Jon Salzd15bbcf2013-05-21 17:33:57 +08001205 # Don't defer logging the initial event, so we can make sure
1206 # that device_id, reimage_id, etc. are all set up.
1207 self.event_log = EventLog('goofy', defer=False)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001208 self.testlog = testlog.Testlog(
1209 log_root=paths.GetLogRoot(), uuid=self.uuid)
1210 # Direct the logging calls to testlog as well.
1211 testlog.CapturePythonLogging(
1212 callback=self.testlog.primary_json.Log,
1213 level=logging.getLogger().getEffectiveLevel())
Jon Salz0697cbf2012-07-04 15:14:04 +08001214
Jon Salz0697cbf2012-07-04 15:14:04 +08001215 if env:
1216 self.env = env
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +08001217 elif sys_utils.InChroot():
Jon Salz0697cbf2012-07-04 15:14:04 +08001218 self.env = test_environment.FakeChrootEnvironment()
1219 logging.warn(
Ricky Liang45c73e72015-01-15 15:00:30 +08001220 'Using chroot environment: will not actually run autotests')
Hung-Te Lina846f602014-07-04 20:32:22 +08001221 elif self.options.ui == 'chrome':
Ricky Liang09d66d82014-09-25 11:20:54 +08001222 self.env = test_environment.DUTEnvironment()
Jon Salz0697cbf2012-07-04 15:14:04 +08001223 self.env.goofy = self
Vic Yanga4931152014-08-11 16:36:24 -07001224 # web_socket_manager will be initialized later
1225 # pylint: disable=W0108
1226 self.env.has_sockets = lambda: self.web_socket_manager.has_sockets()
Jon Salz0697cbf2012-07-04 15:14:04 +08001227
1228 if self.options.restart:
1229 state.clear_state()
1230
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +08001231 if self.options.ui_scale_factor != 1 and sys_utils.InQEMU():
Jon Salz0697cbf2012-07-04 15:14:04 +08001232 logging.warn(
Ricky Liang45c73e72015-01-15 15:00:30 +08001233 'In QEMU; ignoring ui_scale_factor argument')
Jon Salz0697cbf2012-07-04 15:14:04 +08001234 self.options.ui_scale_factor = 1
1235
1236 logging.info('Started')
1237
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001238 if not self.options.monolithic:
Hung-Te Lin7bd55312014-12-30 16:43:36 +08001239 self.link_manager = PresenterLinkManager(
1240 check_interval=1,
1241 handshake_timeout=self.options.handshake_timeout,
1242 standalone=self.options.standalone)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001243
Jon Salz0697cbf2012-07-04 15:14:04 +08001244 self.start_state_server()
1245 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1246 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001247 self.options.ui_scale_factor)
Jon Salz0697cbf2012-07-04 15:14:04 +08001248 self.last_shutdown_time = (
Ricky Liang45c73e72015-01-15 15:00:30 +08001249 self.state_instance.get_shared_data('shutdown_time', optional=True))
Jon Salz0697cbf2012-07-04 15:14:04 +08001250 self.state_instance.del_shared_data('shutdown_time', optional=True)
Jon Salzb19ea072013-02-07 16:35:00 +08001251 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001252
Ricky Liang6fe218c2013-12-27 15:17:17 +08001253 self.options.automation_mode = ParseAutomationMode(
1254 self.options.automation_mode)
1255 self.state_instance.set_shared_data('automation_mode',
1256 self.options.automation_mode)
1257 self.state_instance.set_shared_data(
1258 'automation_mode_prompt',
1259 AutomationModePrompt[self.options.automation_mode])
1260
Joel Kitching50a63ea2016-02-22 13:15:09 +08001261 success = False
1262 exc_info = None
Jon Salz128b0932013-07-03 16:55:26 +08001263 try:
Joel Kitching50a63ea2016-02-22 13:15:09 +08001264 success = self.InitTestLists()
Jon Salz128b0932013-07-03 16:55:26 +08001265 except: # pylint: disable=W0702
Joel Kitching50a63ea2016-02-22 13:15:09 +08001266 exc_info = sys.exc_info()
1267
1268 if not success:
1269 if exc_info:
1270 logging.exception('Unable to initialize test lists')
1271 self.state_instance.set_shared_data(
1272 'startup_error',
1273 'Unable to initialize test lists\n%s' % (
1274 traceback.format_exc()))
Jon Salzb19ea072013-02-07 16:35:00 +08001275 if self.options.ui == 'chrome':
1276 # Create an empty test list with default options so that the rest of
1277 # startup can proceed.
1278 self.test_list = factory.FactoryTestList(
1279 [], self.state_instance, factory.Options())
1280 else:
1281 # Bail with an error; no point in starting up.
1282 sys.exit('No valid test list; exiting.')
1283
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001284 self.init_hooks()
1285
Jon Salz822838b2013-03-25 17:32:33 +08001286 if self.test_list.options.clear_state_on_start:
1287 self.state_instance.clear_test_state()
1288
Jon Salz670ce062014-05-16 15:53:50 +08001289 # If the phase is invalid, this will raise a ValueError.
1290 phase.SetPersistentPhase(self.test_list.options.phase)
1291
Dean Liao85ca86f2014-11-03 12:28:08 +08001292 # For netboot firmware, mainfw_type should be 'netboot'.
Hung-Te Line594e5d2015-12-16 02:36:05 +08001293 if (self.dut.info.mainfw_type != 'nonchrome' and
1294 self.dut.info.firmware_version is None):
Ricky Liang45c73e72015-01-15 15:00:30 +08001295 self.state_instance.set_shared_data(
1296 'startup_error',
Vic Yang9bd4f772013-06-04 17:34:00 +08001297 'Netboot firmware detected\n'
1298 'Connect Ethernet and reboot to re-image.\n'
1299 u'侦测到网路开机固件\n'
1300 u'请连接乙太网并重启')
1301
Jon Salz0697cbf2012-07-04 15:14:04 +08001302 if not self.state_instance.has_shared_data('ui_lang'):
1303 self.state_instance.set_shared_data('ui_lang',
Ricky Liang45c73e72015-01-15 15:00:30 +08001304 self.test_list.options.ui_lang)
Jon Salz0697cbf2012-07-04 15:14:04 +08001305 self.state_instance.set_shared_data(
Ricky Liang45c73e72015-01-15 15:00:30 +08001306 'test_list_options',
1307 self.test_list.options.__dict__)
Jon Salz0697cbf2012-07-04 15:14:04 +08001308 self.state_instance.test_list = self.test_list
1309
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001310 self.check_log_rotation()
Jon Salz83ef34b2012-11-01 19:46:35 +08001311
Jon Salz23926422012-09-01 03:38:13 +08001312 if self.options.dummy_shopfloor:
Ricky Liang45c73e72015-01-15 15:00:30 +08001313 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1314 'http://%s:%d/' %
Joel Kitchingb85ed7f2014-10-08 18:24:39 +08001315 (net_utils.LOCALHOST, shopfloor.DEFAULT_SERVER_PORT))
Hung-Te Lin4e6357c2016-01-08 14:32:00 +08001316 self.dummy_shopfloor = process_utils.Spawn(
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +08001317 [os.path.join(paths.FACTORY_PATH, 'bin', 'shopfloor_server'),
Jon Salz23926422012-09-01 03:38:13 +08001318 '--dummy'])
1319 elif self.test_list.options.shopfloor_server_url:
1320 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001321 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001322
Jon Salz0697cbf2012-07-04 15:14:04 +08001323 self.init_states()
1324 self.start_event_server()
Wei-Ning Huang38b75f02015-02-25 18:25:14 +08001325 self.start_terminal_server()
Hung-Te Lincc41d2a2014-10-29 13:35:20 +08001326
Earl Oua3bca122016-10-21 16:00:30 +08001327 # Load and run Goofy plugins.
1328 self.plugin_controller = plugin_controller.PluginController(
1329 self.test_list.options.plugin_config_name, self)
1330 self.plugin_controller.StartAllPlugins()
1331
Jon Salz0697cbf2012-07-04 15:14:04 +08001332 # Note that we create a log watcher even if
1333 # sync_event_log_period_secs isn't set (no background
1334 # syncing), since we may use it to flush event logs as well.
1335 self.log_watcher = EventLogWatcher(
Ricky Liang45c73e72015-01-15 15:00:30 +08001336 self.test_list.options.sync_event_log_period_secs,
1337 event_log_db_file=None,
1338 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001339 if self.test_list.options.sync_event_log_period_secs:
1340 self.log_watcher.StartWatchThread()
1341
1342 self.update_system_info()
1343
1344 os.environ['CROS_FACTORY'] = '1'
1345 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1346
Jon Salze12c2b32013-06-25 16:24:34 +08001347 self.find_kcrashes()
1348
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001349 # Should not move earlier.
1350 self.hooks.OnStartup()
1351
Ricky Liang36512a32014-07-25 11:47:04 +08001352 # Only after this point the Goofy backend is ready for UI connection.
1353 self.ready_for_ui_connection = True
1354
Ricky Liang650f6bf2012-09-28 13:22:54 +08001355 # Create download path for autotest beforehand or autotests run at
1356 # the same time might fail due to race condition.
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +08001357 if not sys_utils.InChroot():
Hung-Te Lin4e6357c2016-01-08 14:32:00 +08001358 file_utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1359 'download'))
Ricky Liang650f6bf2012-09-28 13:22:54 +08001360
Jon Salz0697cbf2012-07-04 15:14:04 +08001361 def state_change_callback(test, test_state):
1362 self.event_client.post_event(
Ricky Liang4bff3e32014-02-20 18:46:11 +08001363 Event(Event.Type.STATE_CHANGE, path=test.path, state=test_state))
Jon Salz0697cbf2012-07-04 15:14:04 +08001364 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001365
Vic Yange2c76a82014-10-30 12:48:19 -07001366 self.autotest_prespawner = prespawner.AutotestPrespawner()
1367 self.autotest_prespawner.start()
1368
1369 self.pytest_prespawner = prespawner.PytestPrespawner()
1370 self.pytest_prespawner.start()
Jon Salza6711d72012-07-18 14:33:03 +08001371
Ricky Liang48e47f92014-02-26 19:31:51 +08001372 tests_after_shutdown = self.state_instance.get_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001373 TESTS_AFTER_SHUTDOWN, optional=True)
Jon Salz5c344f62012-07-13 14:31:16 +08001374 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001375
Jon Salz5c344f62012-07-13 14:31:16 +08001376 if not force_auto_run and tests_after_shutdown is not None:
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001377 logging.info('Resuming tests after shutdown: %r', tests_after_shutdown)
1378 self.test_list_iterator = tests_after_shutdown
1379 self.test_list_iterator.set_test_list(self.test_list)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001380 self.run_enqueue(self.run_next_test)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001381 elif force_auto_run or self.test_list.options.auto_run_on_start:
1382 # If automation mode is enabled, allow suppress auto_run_on_start.
1383 if (self.options.automation_mode == 'NONE' or
1384 self.options.auto_run_on_start):
1385 status_filter = [TestState.UNTESTED]
1386 if self.test_list.options.retry_failed_on_start:
1387 status_filter.append(TestState.FAILED)
1388 self.run_enqueue(lambda: self.run_tests(self.test_list, status_filter))
1389 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, None)
Ricky Liang4bff3e32014-02-20 18:46:11 +08001390 self.restore_active_run_state()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001391
Hung-Te Lin410f70a2015-12-15 14:53:42 +08001392 self.dut.hooks.OnTestStart()
Vic Yang08505c72015-01-06 17:01:53 -08001393
Dean Liao592e4d52013-01-10 20:06:39 +08001394 self.may_disable_cros_shortcut_keys()
1395
1396 def may_disable_cros_shortcut_keys(self):
1397 test_options = self.test_list.options
1398 if test_options.disable_cros_shortcut_keys:
1399 logging.info('Filter ChromeOS shortcut keys.')
1400 self.key_filter = KeyFilter(
1401 unmap_caps_lock=test_options.disable_caps_lock,
1402 caps_lock_keycode=test_options.caps_lock_keycode)
1403 self.key_filter.Start()
1404
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001405 def check_log_rotation(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001406 """Checks log rotation file presence/absence according to test_list option.
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001407
1408 Touch /var/lib/cleanup_logs_paused if test_list.options.disable_log_rotation
1409 is True, delete it otherwise. This must be done in idle loop because
1410 autotest client will touch /var/lib/cleanup_logs_paused each time it runs
1411 an autotest.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001412 """
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +08001413 if sys_utils.InChroot():
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001414 return
1415 try:
1416 if self.test_list.options.disable_log_rotation:
1417 open(CLEANUP_LOGS_PAUSED, 'w').close()
1418 else:
1419 file_utils.TryUnlink(CLEANUP_LOGS_PAUSED)
1420 except: # pylint: disable=W0702
1421 # Oh well. Logs an error (but no trace)
1422 logging.info(
1423 'Unable to %s %s: %s',
1424 'touch' if self.test_list.options.disable_log_rotation else 'delete',
Hung-Te Linf707b242016-01-08 23:11:42 +08001425 CLEANUP_LOGS_PAUSED, debug_utils.FormatExceptionOnly())
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001426
Peter Ammon1e1ec572014-06-26 17:56:32 -07001427 def perform_periodic_tasks(self):
1428 """Override of base method to perform periodic work.
Vic Yang4953fc12012-07-26 16:19:53 +08001429
Peter Ammon1e1ec572014-06-26 17:56:32 -07001430 This method must not raise exceptions.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001431 """
Peter Ammon1e1ec572014-06-26 17:56:32 -07001432 super(Goofy, self).perform_periodic_tasks()
Jon Salzb22d1172012-08-06 10:38:57 +08001433
Earl Oua3bca122016-10-21 16:00:30 +08001434 self.check_plugins()
cychiang21886742012-07-05 15:16:32 +08001435 self.check_for_updates()
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001436 self.check_log_rotation()
Jon Salz57717ca2012-04-04 16:47:25 +08001437
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001438 def handle_event_logs(self, chunks, periodic=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001439 """Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001440
Jon Salz0697cbf2012-07-04 15:14:04 +08001441 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001442
1443 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001444 chunks: A list of Chunk objects.
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001445 periodic: This event log handling is periodic. Error messages
1446 will only be shown for the first time.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001447 """
Vic Yang93027612013-05-06 02:42:49 +08001448 first_exception = None
1449 exception_count = 0
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001450 # Suppress error messages for periodic event syncing except for the
1451 # first time. If event syncing is not periodic, always show the error
1452 # messages.
1453 quiet = self._suppress_event_log_error_messages if periodic else False
Vic Yang93027612013-05-06 02:42:49 +08001454
Jon Salzd15bbcf2013-05-21 17:33:57 +08001455 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001456 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001457 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001458 start_time = time.time()
1459 shopfloor_client = shopfloor.get_instance(
Ricky Liang45c73e72015-01-15 15:00:30 +08001460 detect=True,
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001461 timeout=self.test_list.options.shopfloor_timeout_secs,
1462 quiet=quiet)
Ricky Liang45c73e72015-01-15 15:00:30 +08001463 shopfloor_client.UploadEvent(chunk.log_name + '.' +
Jon Salzd15bbcf2013-05-21 17:33:57 +08001464 event_log.GetReimageId(),
1465 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001466 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +08001467 'Successfully synced %s in %.03f s',
1468 description, time.time() - start_time)
1469 except: # pylint: disable=W0702
Hung-Te Linf707b242016-01-08 23:11:42 +08001470 first_exception = (first_exception or
1471 (chunk.log_name + ': ' +
1472 debug_utils.FormatExceptionOnly()))
Vic Yang93027612013-05-06 02:42:49 +08001473 exception_count += 1
1474
1475 if exception_count:
1476 if exception_count == 1:
1477 msg = 'Log upload failed: %s' % first_exception
1478 else:
1479 msg = '%d log upload failed; first is: %s' % (
1480 exception_count, first_exception)
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001481 # For periodic event log syncing, only show the first error messages.
1482 if periodic:
1483 if not self._suppress_event_log_error_messages:
1484 self._suppress_event_log_error_messages = True
1485 logging.warning('Suppress periodic shopfloor error messages for '
1486 'event log syncing after the first one.')
1487 raise Exception(msg)
1488 # For event log syncing by request, show the error messages.
1489 else:
1490 raise Exception(msg)
Vic Yang93027612013-05-06 02:42:49 +08001491
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001492 def run_tests_with_status(self, statuses_to_run, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001493 """Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001494
Jon Salz0697cbf2012-07-04 15:14:04 +08001495 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001496
Jon Salz0697cbf2012-07-04 15:14:04 +08001497 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001498 statuses_to_run: The particular status that caller wants to run.
Jon Salz0697cbf2012-07-04 15:14:04 +08001499 starting_at: If provided, only auto-runs tests beginning with
1500 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001501 root: The root of tests to run. If not provided, it will be
1502 the root of all tests.
1503 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001504 root = root or self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +08001505 self.abort_active_tests('Operator requested run/re-run of certain tests')
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001506 self.run_tests(root, status_filter=statuses_to_run)
Jon Salz0405ab52012-03-16 15:26:52 +08001507
Jon Salz0697cbf2012-07-04 15:14:04 +08001508 def restart_tests(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001509 """Restarts all tests."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001510 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001511
Jon Salz6dc031d2013-06-19 13:06:23 +08001512 self.abort_active_tests('Operator requested restart of certain tests')
Jon Salz0697cbf2012-07-04 15:14:04 +08001513 for test in root.walk():
Ricky Liangfea4ac92014-08-21 11:55:59 +08001514 test.update_state(status=TestState.UNTESTED)
Jon Salz0697cbf2012-07-04 15:14:04 +08001515 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001516
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001517 def auto_run(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001518 """"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001519
Jon Salz0697cbf2012-07-04 15:14:04 +08001520 Args:
1521 starting_at: If provide, only auto-runs tests beginning with
1522 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001523 root: If provided, the root of tests to run. If not provided, the root
1524 will be test_list (root of all tests).
1525 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001526 root = root or self.test_list
1527 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Ricky Liang45c73e72015-01-15 15:00:30 +08001528 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001529
Jon Salz0697cbf2012-07-04 15:14:04 +08001530 def handle_switch_test(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001531 """Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001532
Ricky Liang6fe218c2013-12-27 15:17:17 +08001533 Args:
1534 event: The SWITCH_TEST event.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001535 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001536 test = self.test_list.lookup_path(event.path)
1537 if not test:
1538 logging.error('Unknown test %r', event.key)
1539 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001540
Jon Salz0697cbf2012-07-04 15:14:04 +08001541 invoc = self.invocations.get(test)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001542 if invoc:
Jon Salz0697cbf2012-07-04 15:14:04 +08001543 # Already running: just bring to the front if it
1544 # has a UI.
1545 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001546 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001547 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001548
Jon Salz6dc031d2013-06-19 13:06:23 +08001549 self.abort_active_tests('Operator requested abort (switch_test)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001550 for t in test.walk():
1551 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001552
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001553 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001554
Wei-Ning Huang38b75f02015-02-25 18:25:14 +08001555 def handle_key_filter_mode(self, event):
1556 if self.key_filter:
1557 if getattr(event, 'enabled'):
1558 self.key_filter.Start()
1559 else:
1560 self.key_filter.Stop()
1561
Jon Salz0697cbf2012-07-04 15:14:04 +08001562 def wait(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001563 """Waits for all pending invocations.
Jon Salz0697cbf2012-07-04 15:14:04 +08001564
1565 Useful for testing.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001566 """
Jon Salz1acc8742012-07-17 17:45:55 +08001567 while self.invocations:
1568 for k, v in self.invocations.iteritems():
1569 logging.info('Waiting for %s to complete...', k)
1570 v.thread.join()
1571 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001572
Claire Changd1961a22015-08-05 16:15:55 +08001573 def test_fail(self, test):
Hung-Te Lin410f70a2015-12-15 14:53:42 +08001574 self.dut.hooks.OnTestFailure(test)
Claire Changd1961a22015-08-05 16:15:55 +08001575 if self.link_manager:
1576 self.link_manager.UpdateStatus(False)
1577
Wei-Han Chenced08ef2016-11-08 09:40:02 +08001578
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001579if __name__ == '__main__':
Peter Ammona3d298c2014-09-23 10:11:02 -07001580 Goofy.run_main_and_exit()