blob: 87b4e871b3ae0e5f2b693cbbf1d732847108dd37 [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 Salz0405ab52012-03-16 15:26:52 +080012import logging
Wei-Han Chenc17b4112016-11-22 14:56:51 +080013from optparse import OptionParser
Jon Salz0405ab52012-03-16 15:26:52 +080014import os
Jon Salz77c151e2012-08-28 07:20:37 +080015import signal
Jon Salz0405ab52012-03-16 15:26:52 +080016import sys
Jon Salz0405ab52012-03-16 15:26:52 +080017import threading
18import time
19import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080020import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080021from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080022
Jon Salz0697cbf2012-07-04 15:14:04 +080023import factory_common # pylint: disable=W0611
Hung-Te Linb6287242016-05-18 14:39:05 +080024from cros.factory.device import device_utils
Vic Yangd80ea752014-09-24 16:07:14 +080025from cros.factory.goofy.goofy_base import GoofyBase
26from cros.factory.goofy.goofy_rpc import GoofyRPC
Earl Ouacbe99c2017-02-21 16:04:19 +080027from cros.factory.goofy import goofy_server
Vic Yangd80ea752014-09-24 16:07:14 +080028from cros.factory.goofy.invocation import TestInvocation
29from cros.factory.goofy.link_manager import PresenterLinkManager
Earl Oua3bca122016-10-21 16:00:30 +080030from cros.factory.goofy.plugins import plugin_controller
Vic Yange2c76a82014-10-30 12:48:19 -070031from cros.factory.goofy import prespawner
Earl Oua3bca122016-10-21 16:00:30 +080032from cros.factory.goofy import test_environment
Wei-Han Chenc17b4112016-11-22 14:56:51 +080033from cros.factory.goofy.test_list_iterator import TestListIterator
Earl Oua3bca122016-10-21 16:00:30 +080034from cros.factory.goofy import updater
Vic Yangd80ea752014-09-24 16:07:14 +080035from cros.factory.goofy.web_socket_manager import WebSocketManager
Hung-Te Linb6287242016-05-18 14:39:05 +080036from cros.factory.test.e2e_test.common import AutomationMode
37from cros.factory.test.e2e_test.common import AutomationModePrompt
38from cros.factory.test.e2e_test.common import ParseAutomationMode
Earl Ouacbe99c2017-02-21 16:04:19 +080039from cros.factory.test.env import goofy_proxy
Hung-Te Linb6287242016-05-18 14:39:05 +080040from cros.factory.test.env import paths
Jon Salz83591782012-06-26 11:09:58 +080041from cros.factory.test.event import Event
42from cros.factory.test.event import EventClient
43from cros.factory.test.event import EventServer
Hung-Te Linb6287242016-05-18 14:39:05 +080044from cros.factory.test import event_log
45from cros.factory.test.event_log import EventLog
Hung-Te Linb6287242016-05-18 14:39:05 +080046from cros.factory.test.event_log import GetBootSequence
Hung-Te Lin91492a12014-11-25 18:56:30 +080047from cros.factory.test.event_log_watcher import EventLogWatcher
Earl Oua3bca122016-10-21 16:00:30 +080048from cros.factory.test import factory
jcliangcd688182012-08-20 21:01:26 +080049from cros.factory.test.factory import TestState
Peter Shihce03c2e2017-03-21 17:36:10 +080050from cros.factory.test.i18n import html_translator
Peter Shihf65db932017-03-22 17:06:34 +080051from cros.factory.test.i18n import test_ui as i18n_test_ui
Peter Shih80e78b42017-03-10 17:00:56 +080052from cros.factory.test.i18n import translation
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_goofy
chuntseneb33f9d2017-05-12 13:38:17 +080058from cros.factory.testlog import testlog
Hung-Te Linb6287242016-05-18 14:39:05 +080059from cros.factory.tools.key_filter import KeyFilter
Wei-Han Chen78f35f62017-03-06 20:11:20 +080060from cros.factory.utils import config_utils
Hung-Te Linf707b242016-01-08 23:11:42 +080061from cros.factory.utils import debug_utils
Jon Salz2af235d2013-06-24 14:47:21 +080062from cros.factory.utils import file_utils
Joel Kitchingb85ed7f2014-10-08 18:24:39 +080063from cros.factory.utils import net_utils
Hung-Te Lin4e6357c2016-01-08 14:32:00 +080064from cros.factory.utils import process_utils
65from cros.factory.utils import sys_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
Earl Ou6de96c02017-05-19 18:51:28 +080068from cros.factory.external import syslog
69
Hung-Te Linf2f78f72012-02-08 19:27:11 +080070
Hung-Te Linf2f78f72012-02-08 19:27:11 +080071HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Joel Kitching625ff0f2016-05-16 14:59:40 -070072CACHES_DIR = os.path.join(paths.GetStateRoot(), 'caches')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080073
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
Hung-Te Linf707b242016-01-08 23:11:42 +080081Status = type_utils.Enum(['UNINITIALIZED', 'INITIALIZING', 'RUNNING',
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080082 'TERMINATING', 'TERMINATED'])
Jon Salzd7550792013-07-12 05:49:27 +080083
Hung-Te Linf2f78f72012-02-08 19:27:11 +080084def get_hwid_cfg():
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +080085 """Returns the HWID config tag, or an empty string if none can be found."""
Jon Salz0697cbf2012-07-04 15:14:04 +080086 if 'CROS_HWID' in os.environ:
87 return os.environ['CROS_HWID']
88 if os.path.exists(HWID_CFG_PATH):
Ricky Liang45c73e72015-01-15 15:00:30 +080089 with open(HWID_CFG_PATH, 'r') as hwid_cfg_handle:
Jon Salz0697cbf2012-07-04 15:14:04 +080090 return hwid_cfg_handle.read().strip()
91 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080092
Jon Salz73e0fd02012-04-04 11:46:38 +080093_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080094
Ricky Liang45c73e72015-01-15 15:00:30 +080095
Peter Ammon1e1ec572014-06-26 17:56:32 -070096class Goofy(GoofyBase):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +080097 """The main factory flow.
Jon Salz0697cbf2012-07-04 15:14:04 +080098
99 Note that all methods in this class must be invoked from the main
100 (event) thread. Other threads, such as callbacks and TestInvocation
101 methods, should instead post events on the run queue.
102
103 TODO: Unit tests. (chrome-os-partner:7409)
104
105 Properties:
106 uuid: A unique UUID for this invocation of Goofy.
107 state_instance: An instance of FactoryState.
108 state_server: The FactoryState XML/RPC server.
109 state_server_thread: A thread running state_server.
110 event_server: The EventServer socket server.
111 event_server_thread: A thread running event_server.
112 event_client: A client to the event server.
Earl Oua3bca122016-10-21 16:00:30 +0800113 plugin_controller: The PluginController object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800114 invocations: A map from FactoryTest objects to the corresponding
115 TestInvocations objects representing active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800116 options: Command-line options.
117 args: Command-line args.
118 test_list: The test list.
Jon Salz128b0932013-07-03 16:55:26 +0800119 test_lists: All new-style test lists.
Ricky Liang4bff3e32014-02-20 18:46:11 +0800120 run_id: The identifier for latest test run.
121 scheduled_run_tests: The list of tests scheduled for latest test run.
Jon Salz0697cbf2012-07-04 15:14:04 +0800122 event_handlers: Map of Event.Type to the method used to handle that
123 event. If the method has an 'event' argument, the event is passed
124 to the handler.
Jon Salz416f9cc2013-05-10 18:32:50 +0800125 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salzd7550792013-07-12 05:49:27 +0800126 status: The current Goofy status (a member of the Status enum).
Peter Ammon948b7172014-07-15 12:43:06 -0700127 link_manager: Instance of PresenterLinkManager for communicating
128 with GoofyPresenter
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800129 """
Ricky Liang45c73e72015-01-15 15:00:30 +0800130
Jon Salz0697cbf2012-07-04 15:14:04 +0800131 def __init__(self):
Peter Ammon1e1ec572014-06-26 17:56:32 -0700132 super(Goofy, self).__init__()
Jon Salz0697cbf2012-07-04 15:14:04 +0800133 self.uuid = str(uuid.uuid4())
134 self.state_instance = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800135 self.goofy_server = None
136 self.goofy_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800137 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800138 self.event_server = None
139 self.event_server_thread = None
140 self.event_client = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800141 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800142 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800143 self.testlog = None
Earl Oua3bca122016-10-21 16:00:30 +0800144 self.plugin_controller = None
Vic Yange2c76a82014-10-30 12:48:19 -0700145 self.pytest_prespawner = None
Vic Yanga3cecf82014-12-26 00:44:21 -0800146 self._ui_initialized = False
Jon Salzc79a9982012-08-30 04:42:01 +0800147 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800148 self.invocations = {}
Jon Salz0697cbf2012-07-04 15:14:04 +0800149 self.visible_test = None
150 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800151 self.hooks = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800152
153 self.options = None
154 self.args = None
155 self.test_list = None
Jon Salz128b0932013-07-03 16:55:26 +0800156 self.test_lists = None
Ricky Liang4bff3e32014-02-20 18:46:11 +0800157 self.run_id = None
158 self.scheduled_run_tests = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800159 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800160 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800161 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800162 self.last_update_check = None
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800163 self._suppress_periodic_update_messages = False
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +0800164 self._suppress_event_log_error_messages = False
Earl Ouab979142016-10-25 16:48:06 +0800165 self.exclusive_resources = set()
Dean Liao592e4d52013-01-10 20:06:39 +0800166 self.key_filter = None
Jon Salzd7550792013-07-12 05:49:27 +0800167 self.status = Status.UNINITIALIZED
Ricky Liang36512a32014-07-25 11:47:04 +0800168 self.ready_for_ui_connection = False
Peter Ammon1e1ec572014-06-26 17:56:32 -0700169 self.link_manager = None
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800170 self.is_restart_requested = False
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800171 self.test_list_iterator = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800172
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800173 # TODO(hungte) Support controlling remote DUT.
Hung-Te Linb6287242016-05-18 14:39:05 +0800174 self.dut = device_utils.CreateDUTInterface()
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800175
Jon Salz85a39882012-07-05 16:45:04 +0800176 def test_or_root(event, parent_or_group=True):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800177 """Returns the test affected by a particular event.
Jon Salz85a39882012-07-05 16:45:04 +0800178
179 Args:
180 event: The event containing an optional 'path' attribute.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800181 parent_or_group: If True, returns the top-level parent for a test (the
Jon Salz85a39882012-07-05 16:45:04 +0800182 root node of the tests that need to be run together if the given test
183 path is to be run).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800184 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800185 try:
186 path = event.path
187 except AttributeError:
188 path = None
189
190 if path:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800191 test = self.test_list.LookupPath(path)
Jon Salz85a39882012-07-05 16:45:04 +0800192 if parent_or_group:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800193 test = test.GetTopLevelParentOrGroup()
Jon Salz85a39882012-07-05 16:45:04 +0800194 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800195 else:
196 return self.test_list
197
198 self.event_handlers = {
Ricky Liang45c73e72015-01-15 15:00:30 +0800199 Event.Type.SWITCH_TEST: self.handle_switch_test,
Ricky Liang45c73e72015-01-15 15:00:30 +0800200 Event.Type.RESTART_TESTS:
201 lambda event: self.restart_tests(root=test_or_root(event)),
202 Event.Type.AUTO_RUN:
203 lambda event: self.auto_run(root=test_or_root(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800204 Event.Type.RUN_TESTS_WITH_STATUS:
205 lambda event: self.run_tests_with_status(
206 event.status,
207 root=test_or_root(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800208 Event.Type.UPDATE_SYSTEM_INFO:
209 lambda event: self.update_system_info(),
210 Event.Type.STOP:
211 lambda event: self.stop(root=test_or_root(event, False),
212 fail=getattr(event, 'fail', False),
213 reason=getattr(event, 'reason', None)),
214 Event.Type.SET_VISIBLE_TEST:
215 lambda event: self.set_visible_test(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800216 self.test_list.LookupPath(event.path)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800217 Event.Type.CLEAR_STATE:
218 lambda event: self.clear_state(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800219 self.test_list.LookupPath(event.path)),
Wei-Ning Huang38b75f02015-02-25 18:25:14 +0800220 Event.Type.KEY_FILTER_MODE: self.handle_key_filter_mode,
Jon Salz0697cbf2012-07-04 15:14:04 +0800221 }
222
Jon Salz0697cbf2012-07-04 15:14:04 +0800223 self.web_socket_manager = None
224
225 def destroy(self):
Ricky Liang74237a02014-09-18 15:11:23 +0800226 """Performs any shutdown tasks. Overrides base class method."""
Jon Salzd7550792013-07-12 05:49:27 +0800227 self.status = Status.TERMINATING
Jon Salz0697cbf2012-07-04 15:14:04 +0800228 if self.chrome:
229 self.chrome.kill()
230 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800231 if self.dummy_shopfloor:
232 self.dummy_shopfloor.kill()
233 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800234 if self.web_socket_manager:
235 logging.info('Stopping web sockets')
236 self.web_socket_manager.close()
237 self.web_socket_manager = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800238 if self.goofy_server_thread:
239 logging.info('Stopping goofy server')
240 self.goofy_server.shutdown()
241 self.goofy_server_thread.join()
242 self.goofy_server.server_close()
243 self.goofy_server_thread = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800244 if self.state_instance:
245 self.state_instance.close()
246 if self.event_server_thread:
247 logging.info('Stopping event server')
Peter Shihce9490e2017-05-11 14:32:12 +0800248 net_utils.ShutdownTCPServer(self.event_server)
Jon Salz0697cbf2012-07-04 15:14:04 +0800249 self.event_server_thread.join()
250 self.event_server.server_close()
251 self.event_server_thread = None
252 if self.log_watcher:
253 if self.log_watcher.IsThreadStarted():
254 self.log_watcher.StopWatchThread()
255 self.log_watcher = None
Vic Yange2c76a82014-10-30 12:48:19 -0700256 if self.pytest_prespawner:
257 logging.info('Stopping pytest prespawner')
258 self.pytest_prespawner.stop()
259 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800260 if self.event_client:
261 logging.info('Closing event client')
262 self.event_client.close()
263 self.event_client = None
264 if self.event_log:
265 self.event_log.Close()
266 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800267 if self.testlog:
268 self.testlog.Close()
269 self.testlog = None
Dean Liao592e4d52013-01-10 20:06:39 +0800270 if self.key_filter:
271 self.key_filter.Stop()
Peter Ammon1e1ec572014-06-26 17:56:32 -0700272 if self.link_manager:
273 self.link_manager.Stop()
274 self.link_manager = None
Earl Oua3bca122016-10-21 16:00:30 +0800275 if self.plugin_controller:
276 self.plugin_controller.StopAndDestroyAllPlugins()
277 self.plugin_controller = None
Dean Liao592e4d52013-01-10 20:06:39 +0800278
Peter Ammon1e1ec572014-06-26 17:56:32 -0700279 super(Goofy, self).destroy()
Jon Salz0697cbf2012-07-04 15:14:04 +0800280 logging.info('Done destroying Goofy')
Jon Salzd7550792013-07-12 05:49:27 +0800281 self.status = Status.TERMINATED
Jon Salz0697cbf2012-07-04 15:14:04 +0800282
Earl Ouacbe99c2017-02-21 16:04:19 +0800283 def start_goofy_server(self):
284 self.goofy_server = goofy_server.GoofyServer(
285 (goofy_proxy.DEFAULT_GOOFY_ADDRESS, goofy_proxy.DEFAULT_GOOFY_PORT))
286 logging.info('Starting goofy server')
287 self.goofy_server_thread = threading.Thread(
288 target=self.goofy_server.serve_forever,
289 name='GoofyServer')
290 self.goofy_server_thread.start()
291
292 # Setup static file path
293 self.goofy_server.RegisterPath(
294 '/', os.path.join(paths.FACTORY_PACKAGE_PATH, 'goofy/static'))
Peter Shihce03c2e2017-03-21 17:36:10 +0800295 # index.html needs to be preprocessed.
296 index_path = os.path.join(paths.FACTORY_PACKAGE_PATH,
297 'goofy/static/index.html')
298 index_html = html_translator.TranslateHTML(file_utils.ReadFile(index_path))
299 self.goofy_server.RegisterData('/index.html', 'text/html', index_html)
Earl Ouacbe99c2017-02-21 16:04:19 +0800300
301 def init_state_instance(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800302 # Before starting state server, remount stateful partitions with
303 # no commit flag. The default commit time (commit=600) makes corruption
304 # too likely.
Hung-Te Lin1968d9c2016-01-08 22:55:46 +0800305 sys_utils.ResetCommitTime()
Earl Ouacbe99c2017-02-21 16:04:19 +0800306 self.state_instance = state.FactoryState()
307 self.goofy_server.AddRPCInstance(goofy_proxy.STATE_URL, self.state_instance)
Jon Salz2af235d2013-06-24 14:47:21 +0800308
Earl Ouacbe99c2017-02-21 16:04:19 +0800309 # Setup Goofy RPC.
310 # TODO(shunhsingou): separate goofy_rpc and state server instead of
311 # injecting goofy_rpc functions into state.
Jon Salz16d10542012-07-23 12:18:45 +0800312 self.goofy_rpc = GoofyRPC(self)
313 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800314
Peter Shih80e78b42017-03-10 17:00:56 +0800315 def init_i18n(self):
316 js_data = 'var goofy_i18n_data = %s;' % translation.GetAllI18nDataJS()
Peter Shihce03c2e2017-03-21 17:36:10 +0800317 self.goofy_server.RegisterData('/js/goofy-translations.js',
318 'application/javascript', js_data)
Peter Shihf65db932017-03-22 17:06:34 +0800319 self.goofy_server.RegisterData('/css/i18n.css',
320 'text/css', i18n_test_ui.GetStyleSheet())
Peter Shih80e78b42017-03-10 17:00:56 +0800321
Jon Salz0697cbf2012-07-04 15:14:04 +0800322 def start_event_server(self):
323 self.event_server = EventServer()
324 logging.info('Starting factory event server')
325 self.event_server_thread = threading.Thread(
Ricky Liang45c73e72015-01-15 15:00:30 +0800326 target=self.event_server.serve_forever,
327 name='EventServer') # pylint: disable=E1101
Jon Salz0697cbf2012-07-04 15:14:04 +0800328 self.event_server_thread.start()
329
330 self.event_client = EventClient(
Ricky Liang45c73e72015-01-15 15:00:30 +0800331 callback=self.handle_event, event_loop=self.run_queue)
Jon Salz0697cbf2012-07-04 15:14:04 +0800332
333 self.web_socket_manager = WebSocketManager(self.uuid)
Earl Ouacbe99c2017-02-21 16:04:19 +0800334 self.goofy_server.AddHTTPGetHandler(
335 '/event', self.web_socket_manager.handle_web_socket)
Jon Salz0697cbf2012-07-04 15:14:04 +0800336
Jon Salz0697cbf2012-07-04 15:14:04 +0800337 def set_visible_test(self, test):
338 if self.visible_test == test:
339 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800340 if test and not test.has_ui:
341 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800342
343 if test:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800344 test.UpdateState(visible=True)
Jon Salz0697cbf2012-07-04 15:14:04 +0800345 if self.visible_test:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800346 self.visible_test.UpdateState(visible=False)
Jon Salz0697cbf2012-07-04 15:14:04 +0800347 self.visible_test = test
348
Ricky Liang48e47f92014-02-26 19:31:51 +0800349 def shutdown(self, operation):
350 """Starts shutdown procedure.
351
352 Args:
Vic (Chun-Ju) Yang05b0d952014-04-28 17:39:09 +0800353 operation: The shutdown operation (reboot, full_reboot, or halt).
Ricky Liang48e47f92014-02-26 19:31:51 +0800354 """
355 active_tests = []
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800356 for test in self.test_list.Walk():
357 if not test.IsLeaf():
Ricky Liang48e47f92014-02-26 19:31:51 +0800358 continue
359
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800360 test_state = test.GetState()
Ricky Liang48e47f92014-02-26 19:31:51 +0800361 if test_state.status == TestState.ACTIVE:
362 active_tests.append(test)
363
Ricky Liang48e47f92014-02-26 19:31:51 +0800364 if not (len(active_tests) == 1 and
365 isinstance(active_tests[0], factory.ShutdownStep)):
366 logging.error(
367 'Calling Goofy shutdown outside of the shutdown factory test')
368 return
369
370 logging.info('Start Goofy shutdown (%s)', operation)
371 # Save pending test list in the state server
372 self.state_instance.set_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800373 TESTS_AFTER_SHUTDOWN, self.test_list_iterator)
Ricky Liang48e47f92014-02-26 19:31:51 +0800374 # Save shutdown time
375 self.state_instance.set_shared_data('shutdown_time', time.time())
376
377 with self.env.lock:
378 self.event_log.Log('shutdown', operation=operation)
379 shutdown_result = self.env.shutdown(operation)
380 if shutdown_result:
381 # That's all, folks!
Peter Ammon1e1ec572014-06-26 17:56:32 -0700382 self.run_enqueue(None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800383 else:
384 # Just pass (e.g., in the chroot).
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800385 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800386 # Send event with no fields to indicate that there is no
387 # longer a pending shutdown.
388 self.event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
389
390 def handle_shutdown_complete(self, test):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800391 """Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800392
Ricky Liang6fe218c2013-12-27 15:17:17 +0800393 Args:
394 test: The ShutdownStep.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800395 """
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800396 test_state = test.UpdateState(increment_shutdown_count=1)
Jon Salz0697cbf2012-07-04 15:14:04 +0800397 logging.info('Detected shutdown (%d of %d)',
Ricky Liang48e47f92014-02-26 19:31:51 +0800398 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800399
Ricky Liang48e47f92014-02-26 19:31:51 +0800400 tests_after_shutdown = self.state_instance.get_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800401 TESTS_AFTER_SHUTDOWN, optional=True)
402
403 # Make this shutdown test the next test to run. This is to continue on
404 # post-shutdown verification in the shutdown step.
Ricky Liang48e47f92014-02-26 19:31:51 +0800405 if not tests_after_shutdown:
Ricky Liang48e47f92014-02-26 19:31:51 +0800406 self.state_instance.set_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800407 TESTS_AFTER_SHUTDOWN, TestListIterator(test))
408 else:
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800409 tests_after_shutdown.RestartLastTest()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800410 self.state_instance.set_shared_data(
411 TESTS_AFTER_SHUTDOWN, tests_after_shutdown)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800412
Ricky Liang48e47f92014-02-26 19:31:51 +0800413 # Set 'post_shutdown' to inform shutdown test that a shutdown just occurred.
Ricky Liangb7eb8772014-09-15 18:05:22 +0800414 self.state_instance.set_shared_data(
415 state.POST_SHUTDOWN_TAG % test.path,
416 self.state_instance.get_test_state(test.path).invocation)
Jon Salz258a40c2012-04-19 12:34:01 +0800417
Jon Salz0697cbf2012-07-04 15:14:04 +0800418 def init_states(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800419 """Initializes all states on startup."""
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800420 for test in self.test_list.GetAllTests():
Jon Salz0697cbf2012-07-04 15:14:04 +0800421 # Make sure the state server knows about all the tests,
422 # defaulting to an untested state.
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800423 test.UpdateState(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800424
Earl Ouf76e55c2017-03-07 11:48:34 +0800425 is_unexpected_shutdown = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800426
Jon Salz0697cbf2012-07-04 15:14:04 +0800427 # Any 'active' tests should be marked as failed now.
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800428 for test in self.test_list.Walk():
429 if not test.IsLeaf():
Jon Salza6711d72012-07-18 14:33:03 +0800430 # Don't bother with parents; they will be updated when their
431 # children are updated.
432 continue
433
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800434 test_state = test.GetState()
Jon Salz0697cbf2012-07-04 15:14:04 +0800435 if test_state.status != TestState.ACTIVE:
436 continue
437 if isinstance(test, factory.ShutdownStep):
438 # Shutdown while the test was active - that's good.
Ricky Liang48e47f92014-02-26 19:31:51 +0800439 self.handle_shutdown_complete(test)
Jon Salz0697cbf2012-07-04 15:14:04 +0800440 else:
Earl Ouf76e55c2017-03-07 11:48:34 +0800441 is_unexpected_shutdown = True
Jon Salz0697cbf2012-07-04 15:14:04 +0800442 error_msg = 'Unexpected shutdown while test was running'
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800443 # TODO(itspeter): Add testlog to collect expired session infos.
Jon Salz0697cbf2012-07-04 15:14:04 +0800444 self.event_log.Log('end_test',
Ricky Liang45c73e72015-01-15 15:00:30 +0800445 path=test.path,
446 status=TestState.FAILED,
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800447 invocation=test.GetState().invocation,
Earl Ouf76e55c2017-03-07 11:48:34 +0800448 error_msg=error_msg)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800449 test.UpdateState(
Ricky Liang45c73e72015-01-15 15:00:30 +0800450 status=TestState.FAILED,
451 error_msg=error_msg)
Chun-Ta Lin87c2dac2015-05-02 01:35:01 -0700452 # Trigger the OnTestFailure callback.
Claire Changd1961a22015-08-05 16:15:55 +0800453 self.run_queue.put(lambda: self.test_fail(test))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800454
Jon Salz50efe942012-07-26 11:54:10 +0800455 if not test.never_fails:
456 # For "never_fails" tests (such as "Start"), don't cancel
457 # pending tests, since reboot is expected.
458 factory.console.info('Unexpected shutdown while test %s '
459 'running; cancelling any pending tests',
460 test.path)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800461 # cancel pending tests by replace the iterator with an empty one
462 self.state_instance.set_shared_data(
463 TESTS_AFTER_SHUTDOWN,
464 TestListIterator(None))
Jon Salz008f4ea2012-08-28 05:39:45 +0800465
Earl Ouf76e55c2017-03-07 11:48:34 +0800466 if is_unexpected_shutdown:
467 logging.warning("Unexpected shutdown.")
468 self.dut.hooks.OnUnexpectedReboot()
469
Jon Salz0697cbf2012-07-04 15:14:04 +0800470 def handle_event(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800471 """Handles an event from the event server."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800472 handler = self.event_handlers.get(event.type)
473 if handler:
474 handler(event)
475 else:
476 # We don't register handlers for all event types - just ignore
477 # this event.
478 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800479
Vic Yangaabf9fd2013-04-09 18:56:13 +0800480 def check_critical_factory_note(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800481 """Returns True if the last factory note is critical."""
Vic Yangaabf9fd2013-04-09 18:56:13 +0800482 notes = self.state_instance.get_shared_data('factory_note', True)
483 return notes and notes[-1]['level'] == 'CRITICAL'
484
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800485 def schedule_restart(self):
486 """Schedules a restart event when any invocation is completed."""
487 self.is_restart_requested = True
488
489 def invocation_completion(self):
490 """Callback when an invocation is completed."""
491 if self.is_restart_requested:
492 logging.info('Restart by scheduled event.')
493 self.is_restart_requested = False
494 self.restart_tests()
495 else:
496 self.run_next_test()
497
Jon Salz0697cbf2012-07-04 15:14:04 +0800498 def run_next_test(self):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800499 """Runs the next eligible test.
henryhsu4cc6b022014-04-22 17:12:42 +0800500
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800501 self.test_list_iterator (a TestListIterator object) will determine which
502 test should be run.
henryhsu4cc6b022014-04-22 17:12:42 +0800503 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800504 self.reap_completed_tests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800505
506 if self.invocations:
507 # there are tests still running, we cannot start new tests
Vic Yangaabf9fd2013-04-09 18:56:13 +0800508 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800509
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800510 if self.check_critical_factory_note():
511 logging.info('has critical factory note, stop running')
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800512 self.test_list_iterator.Stop()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800513 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800514
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800515 while True:
516 try:
517 path = self.test_list_iterator.next()
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800518 test = self.test_list.LookupPath(path)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800519 except StopIteration:
520 logging.info('no next test, stop running')
Jon Salz0697cbf2012-07-04 15:14:04 +0800521 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800522
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800523 # check if we have run all required tests
Jon Salz304a75d2012-07-06 11:14:15 +0800524 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800525 for requirement in test.require_run:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800526 for i in requirement.test.Walk():
Jon Salza1412922012-07-23 16:04:17 +0800527 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800528 # We've hit this test itself; stop checking
529 break
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800530 if ((i.GetState().status == TestState.UNTESTED) or
531 (requirement.passed and i.GetState().status !=
Jon Salza1412922012-07-23 16:04:17 +0800532 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800533 # Found an untested test; move on to the next
534 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800535 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800536 break
537
538 if untested:
539 untested_paths = ', '.join(sorted([x.path for x in untested]))
540 if self.state_instance.get_shared_data('engineering_mode',
541 optional=True):
542 # In engineering mode, we'll let it go.
543 factory.console.warn('In engineering mode; running '
544 '%s even though required tests '
545 '[%s] have not completed',
546 test.path, untested_paths)
547 else:
548 # Not in engineering mode; mark it failed.
549 error_msg = ('Required tests [%s] have not been run yet'
550 % untested_paths)
551 factory.console.error('Not running %s: %s',
552 test.path, error_msg)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800553 test.UpdateState(status=TestState.FAILED,
554 error_msg=error_msg)
Jon Salz304a75d2012-07-06 11:14:15 +0800555 continue
556
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800557 # okay, let's run the test
Ricky Liang48e47f92014-02-26 19:31:51 +0800558 if (isinstance(test, factory.ShutdownStep) and
Ricky Liangb7eb8772014-09-15 18:05:22 +0800559 self.state_instance.get_shared_data(
560 state.POST_SHUTDOWN_TAG % test.path, optional=True)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800561 # Invoking post shutdown method of shutdown test. We should retain the
562 # iterations_left and retries_left of the original test state.
563 test_state = self.state_instance.get_test_state(test.path)
564 self._run_test(test, test_state.iterations_left,
565 test_state.retries_left)
566 else:
567 # Starts a new test run; reset iterations and retries.
568 self._run_test(test, test.iterations, test.retries)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800569 return # to leave while
Jon Salz1acc8742012-07-17 17:45:55 +0800570
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800571 def _run_test(self, test, iterations_left=None, retries_left=None):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800572 """Invokes the test.
573
574 The argument `test` should be either a leaf test (no subtests) or a parallel
575 test (all subtests should be run in parallel).
576 """
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800577 if not self._ui_initialized and not test.IsNoHost():
Vic Yanga3cecf82014-12-26 00:44:21 -0800578 self.init_ui()
Jon Salz1acc8742012-07-17 17:45:55 +0800579
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800580 if test.IsLeaf():
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800581 invoc = TestInvocation(
582 self, test, on_completion=self.invocation_completion,
583 on_test_failure=lambda: self.test_fail(test))
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800584 new_state = test.UpdateState(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800585 status=TestState.ACTIVE, increment_count=1, error_msg='',
586 invocation=invoc.uuid, iterations_left=iterations_left,
587 retries_left=retries_left,
588 visible=(self.visible_test == test))
589 invoc.count = new_state.count
590 self.invocations[test] = invoc
591 if self.visible_test is None and test.has_ui:
592 self.set_visible_test(test)
593 self.check_plugins()
594 invoc.start()
595 else:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800596 assert test.IsParallel()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800597 for subtest in test.subtests:
598 # TODO(stimim): what if the subtests *must* be run in parallel?
599 # for example, stressapptest and countdown test.
600
601 # Make sure we don't need to skip it:
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800602 if not self.test_list_iterator.CheckSkip(subtest):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800603 self._run_test(subtest, subtest.iterations, subtest.retries)
Jon Salz5f2a0672012-05-22 17:14:06 +0800604
Earl Oua3bca122016-10-21 16:00:30 +0800605 def check_plugins(self):
606 """Check plugins to be paused or resumed."""
607 exclusive_resources = set()
608 for test in self.invocations:
609 exclusive_resources = exclusive_resources.union(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800610 test.GetExclusiveResources())
Earl Oua3bca122016-10-21 16:00:30 +0800611 self.plugin_controller.PauseAndResumePluginByResource(exclusive_resources)
612
cychiang21886742012-07-05 15:16:32 +0800613 def check_for_updates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800614 """Schedules an asynchronous check for updates if necessary."""
cychiang21886742012-07-05 15:16:32 +0800615 if not self.test_list.options.update_period_secs:
616 # Not enabled.
617 return
618
619 now = time.time()
620 if self.last_update_check and (
621 now - self.last_update_check <
622 self.test_list.options.update_period_secs):
623 # Not yet time for another check.
624 return
625
626 self.last_update_check = now
627
628 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
629 if reached_shopfloor:
630 new_update_md5sum = md5sum if needs_update else None
Hung-Te Line594e5d2015-12-16 02:36:05 +0800631 if self.dut.info.update_md5sum != new_update_md5sum:
cychiang21886742012-07-05 15:16:32 +0800632 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
Hung-Te Line594e5d2015-12-16 02:36:05 +0800633 self.dut.info.Overrides('update_md5sum', new_update_md5sum)
Peter Ammon1e1ec572014-06-26 17:56:32 -0700634 self.run_enqueue(self.update_system_info)
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800635 else:
636 if not self._suppress_periodic_update_messages:
637 logging.warning('Suppress error messages for periodic update checking'
638 ' after the first one.')
639 self._suppress_periodic_update_messages = True
cychiang21886742012-07-05 15:16:32 +0800640
641 updater.CheckForUpdateAsync(
Ricky Liang45c73e72015-01-15 15:00:30 +0800642 handle_check_for_update,
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800643 self.test_list.options.shopfloor_timeout_secs,
644 self._suppress_periodic_update_messages)
cychiang21886742012-07-05 15:16:32 +0800645
Jon Salza6711d72012-07-18 14:33:03 +0800646 def cancel_pending_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800647 """Cancels any tests in the run queue."""
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800648 self.run_tests(None)
Jon Salza6711d72012-07-18 14:33:03 +0800649
Ricky Liang4bff3e32014-02-20 18:46:11 +0800650 def restore_active_run_state(self):
651 """Restores active run id and the list of scheduled tests."""
652 self.run_id = self.state_instance.get_shared_data('run_id', optional=True)
653 self.scheduled_run_tests = self.state_instance.get_shared_data(
654 'scheduled_run_tests', optional=True)
655
656 def set_active_run_state(self):
657 """Sets active run id and the list of scheduled tests."""
658 self.run_id = str(uuid.uuid4())
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800659 # try our best to predict which tests will be run.
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800660 self.scheduled_run_tests = self.test_list_iterator.GetPendingTests()
Ricky Liang4bff3e32014-02-20 18:46:11 +0800661 self.state_instance.set_shared_data('run_id', self.run_id)
662 self.state_instance.set_shared_data('scheduled_run_tests',
663 self.scheduled_run_tests)
664
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800665 def run_tests(self, subtree, status_filter=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800666 """Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800667
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800668 Run tests under a given subtree.
Jon Salzb1b39092012-05-03 02:05:09 +0800669
Ricky Liang6fe218c2013-12-27 15:17:17 +0800670 Args:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800671 subtree: root of subtree to run or None to run nothing.
Chih-Yu Huang85dc63c2015-08-12 15:21:28 +0800672 status_filter: List of available test states. Only run the tests which
673 states are in the list. Set to None if all test states are available.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800674 """
Hung-Te Lin410f70a2015-12-15 14:53:42 +0800675 self.dut.hooks.OnTestStart()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800676 self.test_list_iterator = TestListIterator(
677 subtree, status_filter, self.test_list)
678 if subtree is not None:
Ricky Liang4bff3e32014-02-20 18:46:11 +0800679 self.set_active_run_state()
Jon Salz0697cbf2012-07-04 15:14:04 +0800680 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800681
Jon Salz0697cbf2012-07-04 15:14:04 +0800682 def reap_completed_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800683 """Removes completed tests from the set of active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800684
685 Also updates the visible test if it was reaped.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800686 """
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800687 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800688 for t, v in dict(self.invocations).iteritems():
689 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800690 test_completed = True
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800691 new_state = t.UpdateState(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800692 del self.invocations[t]
693
Johny Lin62ed2a32015-05-13 11:57:12 +0800694 # Stop on failure if flag is true and there is no retry chances.
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800695 if (self.test_list.options.stop_on_failure and
Johny Lin62ed2a32015-05-13 11:57:12 +0800696 new_state.retries_left < 0 and
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800697 new_state.status == TestState.FAILED):
698 # Clean all the tests to cause goofy to stop.
Ricky Liang45c73e72015-01-15 15:00:30 +0800699 factory.console.info('Stop on failure triggered. Empty the queue.')
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800700 self.cancel_pending_tests()
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800701
Jon Salz1acc8742012-07-17 17:45:55 +0800702 if new_state.iterations_left and new_state.status == TestState.PASSED:
703 # Play it again, Sam!
704 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800705 # new_state.retries_left is obtained after update.
706 # For retries_left == 0, test can still be run for the last time.
707 elif (new_state.retries_left >= 0 and
708 new_state.status == TestState.FAILED):
709 # Still have to retry, Sam!
710 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +0800711
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800712 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800713 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800714
Jon Salz0697cbf2012-07-04 15:14:04 +0800715 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800716 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800717 self.set_visible_test(None)
718 # Make the first running test, if any, the visible test
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800719 for t in self.test_list.Walk():
Jon Salz0697cbf2012-07-04 15:14:04 +0800720 if t in self.invocations:
721 self.set_visible_test(t)
722 break
723
Jon Salz6dc031d2013-06-19 13:06:23 +0800724 def kill_active_tests(self, abort, root=None, reason=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800725 """Kills and waits for all active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800726
Jon Salz85a39882012-07-05 16:45:04 +0800727 Args:
728 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800729 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800730 root: If set, only kills tests with root as an ancestor.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800731 reason: If set, the abort reason.
732 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800733 self.reap_completed_tests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800734 # since we remove objects while iterating, make a copy
735 for test, invoc in dict(self.invocations).iteritems():
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800736 if root and not test.HasAncestor(root):
Jon Salz85a39882012-07-05 16:45:04 +0800737 continue
738
Ricky Liang45c73e72015-01-15 15:00:30 +0800739 factory.console.info('Killing active test %s...', test.path)
Jon Salz6dc031d2013-06-19 13:06:23 +0800740 invoc.abort_and_join(reason)
Ricky Liang45c73e72015-01-15 15:00:30 +0800741 factory.console.info('Killed %s', test.path)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800742 test.UpdateState(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800743 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800744
Jon Salz0697cbf2012-07-04 15:14:04 +0800745 if not abort:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800746 test.UpdateState(status=TestState.UNTESTED)
Jon Salz0697cbf2012-07-04 15:14:04 +0800747 self.reap_completed_tests()
748
Jon Salz6dc031d2013-06-19 13:06:23 +0800749 def stop(self, root=None, fail=False, reason=None):
750 self.kill_active_tests(fail, root, reason)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800751
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800752 self.test_list_iterator.Stop(root)
Jon Salz85a39882012-07-05 16:45:04 +0800753 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800754
Jon Salz4712ac72013-02-07 17:12:05 +0800755 def clear_state(self, root=None):
Jon Salzd7550792013-07-12 05:49:27 +0800756 if root is None:
757 root = self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +0800758 self.stop(root, reason='Clearing test state')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800759 for f in root.Walk():
760 if f.IsLeaf():
761 f.UpdateState(status=TestState.UNTESTED)
Jon Salz4712ac72013-02-07 17:12:05 +0800762
Jon Salz6dc031d2013-06-19 13:06:23 +0800763 def abort_active_tests(self, reason=None):
764 self.kill_active_tests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +0800765
766 def main(self):
Jon Salzeff94182013-06-19 15:06:28 +0800767 syslog.openlog('goofy')
768
Jon Salz0697cbf2012-07-04 15:14:04 +0800769 try:
Jon Salzd7550792013-07-12 05:49:27 +0800770 self.status = Status.INITIALIZING
Jon Salz0697cbf2012-07-04 15:14:04 +0800771 self.init()
772 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800773 success=True)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800774 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800775 testlog.StationInit({
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800776 'stationDeviceId': testlog_goofy.GetDeviceID(),
Joel Kitching21bc69b2016-07-13 08:29:52 -0700777 'stationInstallationId': testlog_goofy.GetInstallationID(),
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800778 'count': testlog_goofy.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800779 'success': True}))
Jon Salz0697cbf2012-07-04 15:14:04 +0800780 except:
Joel Kitching9eb203a2016-04-21 15:36:30 +0800781 try:
782 if self.event_log:
Jon Salz0697cbf2012-07-04 15:14:04 +0800783 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800784 success=False,
785 trace=traceback.format_exc())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800786 if self.testlog:
787 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800788 testlog.StationInit({
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800789 'stationDeviceId': testlog_goofy.GetDeviceID(),
Joel Kitching21bc69b2016-07-13 08:29:52 -0700790 'stationInstallationId': testlog_goofy.GetInstallationID(),
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800791 'count': testlog_goofy.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800792 'success': False,
793 'failureMessage': traceback.format_exc()}))
794 except: # pylint: disable=W0702
795 pass
Jon Salz0697cbf2012-07-04 15:14:04 +0800796 raise
797
Jon Salzd7550792013-07-12 05:49:27 +0800798 self.status = Status.RUNNING
Jon Salzeff94182013-06-19 15:06:28 +0800799 syslog.syslog('Goofy (factory test harness) starting')
Chun-Ta Lin5d12b592015-06-30 00:54:23 -0700800 syslog.syslog('Boot sequence = %d' % GetBootSequence())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800801 syslog.syslog('Goofy init count = %d' % testlog_goofy.GetInitCount())
Jon Salz0697cbf2012-07-04 15:14:04 +0800802 self.run()
803
804 def update_system_info(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800805 """Updates system info."""
Hung-Te Line594e5d2015-12-16 02:36:05 +0800806 info = self.dut.info.GetAll()
807 self.state_instance.set_shared_data('system_info', info)
Jon Salz0697cbf2012-07-04 15:14:04 +0800808 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
Hung-Te Line594e5d2015-12-16 02:36:05 +0800809 system_info=info))
810 logging.info('System info: %r', info)
Jon Salz0697cbf2012-07-04 15:14:04 +0800811
Jon Salzeb42f0d2012-07-27 19:14:04 +0800812 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800813 """Commences updating factory software.
Jon Salzeb42f0d2012-07-27 19:14:04 +0800814
815 Args:
816 auto_run_on_restart: Auto-run when the machine comes back up.
817 post_update_hook: Code to call after update but immediately before
818 restart.
819
820 Returns:
821 Never if the update was successful (we just reboot).
822 False if the update was unnecessary (no update available).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800823 """
Jon Salz6dc031d2013-06-19 13:06:23 +0800824 self.kill_active_tests(False, reason='Factory software update')
Jon Salza6711d72012-07-18 14:33:03 +0800825 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800826
Jon Salz5c344f62012-07-13 14:31:16 +0800827 def pre_update_hook():
828 if auto_run_on_restart:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800829 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN,
Jon Salz5c344f62012-07-13 14:31:16 +0800830 FORCE_AUTO_RUN)
831 self.state_instance.close()
832
Jon Salzeb42f0d2012-07-27 19:14:04 +0800833 if updater.TryUpdate(pre_update_hook=pre_update_hook):
834 if post_update_hook:
835 post_update_hook()
836 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800837
Joel Kitching261e0422017-03-30 16:52:01 -0700838 def handle_signal(self, signum, frame):
839 del signum, frame
840 logging.error('Received SIGINT or SIGKILL')
Peter Ammon1e1ec572014-06-26 17:56:32 -0700841 self.run_enqueue(None)
Jon Salz77c151e2012-08-28 07:20:37 +0800842 raise KeyboardInterrupt()
843
Jon Salz128b0932013-07-03 16:55:26 +0800844 def GetTestList(self, test_list_id):
845 """Returns the test list with the given ID.
846
847 Raises:
848 TestListError: The test list ID is not valid.
849 """
850 try:
851 return self.test_lists[test_list_id]
852 except KeyError:
853 raise test_lists.TestListError(
854 '%r is not a valid test list ID (available IDs are [%s])' % (
855 test_list_id, ', '.join(sorted(self.test_lists.keys()))))
856
Chih-Yu Huang1725d622017-03-24 16:08:35 +0800857 def _RecordStartError(self, error_message):
858 """Appends the startup error message into the shared data."""
859 KEY = 'startup_error'
860 data = self.state_instance.get_shared_data(KEY, optional=True)
861 new_data = '%s\n\n%s' % (data, error_message) if data else error_message
862 self.state_instance.set_shared_data(KEY, new_data)
863
Jon Salz128b0932013-07-03 16:55:26 +0800864 def InitTestLists(self):
Joel Kitching50a63ea2016-02-22 13:15:09 +0800865 """Reads in all test lists and sets the active test list.
866
867 Returns:
868 True if the active test list could be set, False if failed.
869 """
870 startup_errors = []
871 self.test_lists, failed_files = test_lists.BuildAllTestLists(
Ricky Liang27051552014-05-04 14:22:26 +0800872 force_generic=(self.options.automation_mode is not None))
Jon Salzd7550792013-07-12 05:49:27 +0800873 logging.info('Loaded test lists: [%s]',
874 test_lists.DescribeTestLists(self.test_lists))
Jon Salz128b0932013-07-03 16:55:26 +0800875
Joel Kitching50a63ea2016-02-22 13:15:09 +0800876 # Check for any syntax errors in test list files.
877 if failed_files:
878 logging.info('Failed test list files: [%s]',
879 ' '.join(failed_files.keys()))
880 for f, exc_info in failed_files.iteritems():
881 logging.error('Error in test list file: %s', f,
882 exc_info=exc_info)
883
884 # Limit the stack trace to the very last entry.
885 exc_type, exc_value, exc_traceback = exc_info
886 while exc_traceback and exc_traceback.tb_next:
887 exc_traceback = exc_traceback.tb_next
888
889 exc_string = ''.join(
890 traceback.format_exception(
891 exc_type, exc_value, exc_traceback)).rstrip()
892 startup_errors.append('Error in test list file (%s):\n%s'
893 % (f, exc_string))
894
Jon Salz128b0932013-07-03 16:55:26 +0800895 if not self.options.test_list:
896 self.options.test_list = test_lists.GetActiveTestListId()
897
Joel Kitching50a63ea2016-02-22 13:15:09 +0800898 # Check for a non-existent test list ID.
899 try:
Wei-Han Chen84fee7c2016-08-26 21:56:25 +0800900 self.test_list = self.GetTestList(self.options.test_list)
Joel Kitching50a63ea2016-02-22 13:15:09 +0800901 logging.info('Active test list: %s', self.test_list.test_list_id)
902 except test_lists.TestListError as e:
903 logging.exception('Invalid active test list: %s',
904 self.options.test_list)
905 startup_errors.append(e.message)
Jon Salz128b0932013-07-03 16:55:26 +0800906
Joel Kitching50a63ea2016-02-22 13:15:09 +0800907 # We may have failed loading the active test list.
908 if self.test_list:
Joel Kitching50a63ea2016-02-22 13:15:09 +0800909 self.test_list.state_instance = self.state_instance
Jon Salz128b0932013-07-03 16:55:26 +0800910
Joel Kitching50a63ea2016-02-22 13:15:09 +0800911 # Show all startup errors.
912 if startup_errors:
Chih-Yu Huang1725d622017-03-24 16:08:35 +0800913 self._RecordStartError('\n\n'.join(startup_errors))
Joel Kitching50a63ea2016-02-22 13:15:09 +0800914
915 # Only return False if failed to load the active test list.
916 return bool(self.test_list)
Jon Salz128b0932013-07-03 16:55:26 +0800917
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +0800918 def init_hooks(self):
919 """Initializes hooks.
920
921 Must run after self.test_list ready.
922 """
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +0800923 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
924 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
925 assert isinstance(self.hooks, factory.Hooks), (
Ricky Liang45c73e72015-01-15 15:00:30 +0800926 'hooks should be of type Hooks but is %r' % type(self.hooks))
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +0800927 self.hooks.test_list = self.test_list
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +0800928 self.hooks.OnCreatedTestList()
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +0800929
Vic Yanga3cecf82014-12-26 00:44:21 -0800930 def init_ui(self):
931 """Initialize UI."""
932 self._ui_initialized = True
933 if self.options.ui == 'chrome':
Hung-Te Lin8f6a3782015-01-06 22:58:32 +0800934 if self.options.monolithic:
Hung-Te Lin7bd55312014-12-30 16:43:36 +0800935 self.env.launch_chrome()
936 else:
937 # The presenter is responsible for launching Chrome. Let's just
938 # wait here.
939 self.env.controller_ready_for_ui()
Vic Yanga3cecf82014-12-26 00:44:21 -0800940 logging.info('Waiting for a web socket connection')
941 self.web_socket_manager.wait()
942
Jon Salz0697cbf2012-07-04 15:14:04 +0800943 def init(self, args=None, env=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800944 """Initializes Goofy.
Jon Salz0697cbf2012-07-04 15:14:04 +0800945
946 Args:
947 args: A list of command-line arguments. Uses sys.argv if
948 args is None.
949 env: An Environment instance to use (or None to choose
950 FakeChrootEnvironment or DUTEnvironment as appropriate).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800951 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800952 parser = OptionParser()
953 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800954 action='store_true',
955 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800956 parser.add_option('--print_test_list', dest='print_test_list',
Wei-Han Chen84fee7c2016-08-26 21:56:25 +0800957 metavar='TEST_LIST_ID',
958 help='Print the content of TEST_LIST_ID and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800959 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800960 action='store_true',
961 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +0800962 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz7b5482e2014-08-04 17:48:41 +0800963 choices=['none', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +0800964 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +0800965 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +0800966 parser.add_option('--test_list', dest='test_list',
Wei-Han Chen84fee7c2016-08-26 21:56:25 +0800967 metavar='TEST_LIST_ID',
968 help='Use test list whose id is TEST_LIST_ID')
Jon Salzc79a9982012-08-30 04:42:01 +0800969 parser.add_option('--dummy_shopfloor', action='store_true',
970 help='Use a dummy shopfloor server')
Ricky Liang6fe218c2013-12-27 15:17:17 +0800971 parser.add_option('--automation-mode',
972 choices=[m.lower() for m in AutomationMode],
Ricky Liang45c73e72015-01-15 15:00:30 +0800973 default='none', help='Factory test automation mode.')
Ricky Liang117484a2014-04-14 11:14:41 +0800974 parser.add_option('--no-auto-run-on-start', dest='auto_run_on_start',
975 action='store_false', default=True,
976 help=('do not automatically run the test list on goofy '
977 'start; this is only valid when factory test '
978 'automation is enabled'))
Chun-Ta Lina8dd3172014-11-26 16:15:13 +0800979 parser.add_option('--handshake_timeout', dest='handshake_timeout',
980 type='float', default=0.3,
981 help=('RPC timeout when doing handshake between device '
982 'and presenter.'))
Vic Yang7d693c42014-09-14 09:52:39 +0800983 parser.add_option('--standalone', dest='standalone',
984 action='store_true', default=False,
985 help=('Assume the presenter is running on the same '
986 'machines.'))
Hung-Te Lin8f6a3782015-01-06 22:58:32 +0800987 parser.add_option('--monolithic', dest='monolithic',
988 action='store_true', default=False,
989 help='Run in monolithic mode (without presenter)')
Jon Salz0697cbf2012-07-04 15:14:04 +0800990 (self.options, self.args) = parser.parse_args(args)
991
Joel Kitching261e0422017-03-30 16:52:01 -0700992 signal.signal(signal.SIGINT, self.handle_signal)
993 signal.signal(signal.SIGTERM, self.handle_signal)
Hung-Te Lina846f602014-07-04 20:32:22 +0800994 # TODO(hungte) SIGTERM does not work properly without Telemetry and should
995 # be fixed.
Hung-Te Lina846f602014-07-04 20:32:22 +0800996
Jon Salz46b89562012-07-05 11:49:22 +0800997 # Make sure factory directories exist.
Joel Kitching625ff0f2016-05-16 14:59:40 -0700998 paths.GetLogRoot()
999 paths.GetStateRoot()
1000 paths.GetTestDataRoot()
Jon Salz46b89562012-07-05 11:49:22 +08001001
Jon Salz0697cbf2012-07-04 15:14:04 +08001002 global _inited_logging # pylint: disable=W0603
1003 if not _inited_logging:
1004 factory.init_logging('goofy', verbose=self.options.verbose)
1005 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001006
Wei-Han Chen78f35f62017-03-06 20:11:20 +08001007 try:
1008 goofy_default_options = config_utils.LoadConfig(validate_schema=False)
1009 for key, value in goofy_default_options.iteritems():
1010 if getattr(self.options, key, None) is None:
1011 logging.info('self.options.%s = %r', key, value)
1012 setattr(self.options, key, value)
1013 except Exception:
1014 logging.exception('failed to load goofy overriding options')
1015
Jon Salz0f996602012-10-03 15:26:48 +08001016 if self.options.print_test_list:
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001017 test_list = test_lists.BuildTestList(self.options.print_test_list)
1018 print(test_list.__repr__(recursive=True))
Jon Salz0f996602012-10-03 15:26:48 +08001019 sys.exit(0)
1020
Jon Salzee85d522012-07-17 14:34:46 +08001021 event_log.IncrementBootSequence()
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001022 testlog_goofy.IncrementInitCount()
1023
Jon Salzd15bbcf2013-05-21 17:33:57 +08001024 # Don't defer logging the initial event, so we can make sure
1025 # that device_id, reimage_id, etc. are all set up.
1026 self.event_log = EventLog('goofy', defer=False)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001027 self.testlog = testlog.Testlog(
Joel Kitchingfa847512017-03-31 02:26:14 +08001028 log_root=paths.GetLogRoot(), uuid=self.uuid,
1029 stationDeviceId=testlog_goofy.GetDeviceID(),
1030 stationInstallationId=testlog_goofy.GetInstallationID())
Jon Salz0697cbf2012-07-04 15:14:04 +08001031
Jon Salz0697cbf2012-07-04 15:14:04 +08001032 if env:
1033 self.env = env
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +08001034 elif sys_utils.InChroot():
Jon Salz0697cbf2012-07-04 15:14:04 +08001035 self.env = test_environment.FakeChrootEnvironment()
Hung-Te Lina846f602014-07-04 20:32:22 +08001036 elif self.options.ui == 'chrome':
Ricky Liang09d66d82014-09-25 11:20:54 +08001037 self.env = test_environment.DUTEnvironment()
Jon Salz0697cbf2012-07-04 15:14:04 +08001038 self.env.goofy = self
Vic Yanga4931152014-08-11 16:36:24 -07001039 # web_socket_manager will be initialized later
1040 # pylint: disable=W0108
1041 self.env.has_sockets = lambda: self.web_socket_manager.has_sockets()
Jon Salz0697cbf2012-07-04 15:14:04 +08001042
1043 if self.options.restart:
1044 state.clear_state()
1045
Jon Salz0697cbf2012-07-04 15:14:04 +08001046 logging.info('Started')
1047
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001048 if not self.options.monolithic:
Hung-Te Lin7bd55312014-12-30 16:43:36 +08001049 self.link_manager = PresenterLinkManager(
1050 check_interval=1,
1051 handshake_timeout=self.options.handshake_timeout,
1052 standalone=self.options.standalone)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001053
Earl Ouacbe99c2017-02-21 16:04:19 +08001054 self.start_goofy_server()
1055 self.init_state_instance()
Peter Shih80e78b42017-03-10 17:00:56 +08001056 self.init_i18n()
Jon Salz0697cbf2012-07-04 15:14:04 +08001057 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
Jon Salz0697cbf2012-07-04 15:14:04 +08001058 self.last_shutdown_time = (
Ricky Liang45c73e72015-01-15 15:00:30 +08001059 self.state_instance.get_shared_data('shutdown_time', optional=True))
Jon Salz0697cbf2012-07-04 15:14:04 +08001060 self.state_instance.del_shared_data('shutdown_time', optional=True)
Jon Salzb19ea072013-02-07 16:35:00 +08001061 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001062
Ricky Liang6fe218c2013-12-27 15:17:17 +08001063 self.options.automation_mode = ParseAutomationMode(
1064 self.options.automation_mode)
1065 self.state_instance.set_shared_data('automation_mode',
1066 self.options.automation_mode)
1067 self.state_instance.set_shared_data(
1068 'automation_mode_prompt',
1069 AutomationModePrompt[self.options.automation_mode])
1070
Joel Kitching50a63ea2016-02-22 13:15:09 +08001071 success = False
1072 exc_info = None
Jon Salz128b0932013-07-03 16:55:26 +08001073 try:
Joel Kitching50a63ea2016-02-22 13:15:09 +08001074 success = self.InitTestLists()
Jon Salz128b0932013-07-03 16:55:26 +08001075 except: # pylint: disable=W0702
Joel Kitching50a63ea2016-02-22 13:15:09 +08001076 exc_info = sys.exc_info()
1077
1078 if not success:
1079 if exc_info:
1080 logging.exception('Unable to initialize test lists')
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001081 self._RecordStartError(
1082 'Unable to initialize test lists\n%s' % traceback.format_exc())
Jon Salzb19ea072013-02-07 16:35:00 +08001083 if self.options.ui == 'chrome':
1084 # Create an empty test list with default options so that the rest of
1085 # startup can proceed.
1086 self.test_list = factory.FactoryTestList(
1087 [], self.state_instance, factory.Options())
1088 else:
1089 # Bail with an error; no point in starting up.
1090 sys.exit('No valid test list; exiting.')
1091
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001092 self.init_hooks()
1093
Jon Salz822838b2013-03-25 17:32:33 +08001094 if self.test_list.options.clear_state_on_start:
1095 self.state_instance.clear_test_state()
1096
Jon Salz670ce062014-05-16 15:53:50 +08001097 # If the phase is invalid, this will raise a ValueError.
1098 phase.SetPersistentPhase(self.test_list.options.phase)
1099
Peter Shih3b0bb9f2017-03-21 16:23:32 +08001100 if not self.state_instance.has_shared_data('ui_locale'):
1101 if self.test_list.options.ui_lang is not None:
1102 # For backward compatibility
1103 ui_locale = ('en-US'
1104 if self.test_list.options.ui_lang == 'en' else 'zh-CN')
1105 else:
1106 ui_locale = self.test_list.options.ui_locale
1107 self.state_instance.set_shared_data('ui_locale', ui_locale)
Jon Salz0697cbf2012-07-04 15:14:04 +08001108 self.state_instance.set_shared_data(
Ricky Liang45c73e72015-01-15 15:00:30 +08001109 'test_list_options',
1110 self.test_list.options.__dict__)
Jon Salz0697cbf2012-07-04 15:14:04 +08001111 self.state_instance.test_list = self.test_list
1112
Jon Salz23926422012-09-01 03:38:13 +08001113 if self.options.dummy_shopfloor:
Ricky Liang45c73e72015-01-15 15:00:30 +08001114 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1115 'http://%s:%d/' %
Joel Kitchingb85ed7f2014-10-08 18:24:39 +08001116 (net_utils.LOCALHOST, shopfloor.DEFAULT_SERVER_PORT))
Hung-Te Lin4e6357c2016-01-08 14:32:00 +08001117 self.dummy_shopfloor = process_utils.Spawn(
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +08001118 [os.path.join(paths.FACTORY_PATH, 'bin', 'shopfloor_server'),
Jon Salz23926422012-09-01 03:38:13 +08001119 '--dummy'])
1120 elif self.test_list.options.shopfloor_server_url:
1121 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001122 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001123
Jon Salz0697cbf2012-07-04 15:14:04 +08001124 self.init_states()
1125 self.start_event_server()
Hung-Te Lincc41d2a2014-10-29 13:35:20 +08001126
Earl Oua3bca122016-10-21 16:00:30 +08001127 # Load and run Goofy plugins.
1128 self.plugin_controller = plugin_controller.PluginController(
1129 self.test_list.options.plugin_config_name, self)
1130 self.plugin_controller.StartAllPlugins()
1131
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001132 # TODO(akahuang): Move this part into a pytest.
1133 # Prepare DUT link after the plugins start running, because the link might
1134 # need the network connection.
1135 if success:
1136 try:
1137 if self.test_list.options.dut_options:
1138 logging.info('dut_options set by %s: %r', self.test_list.test_list_id,
1139 self.test_list.options.dut_options)
1140 device_utils.PrepareDUTLink(**self.test_list.options.dut_options)
1141 except Exception:
1142 logging.exception('Unable to prepare DUT link.')
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001143 self._RecordStartError(
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001144 'Unable to prepare DUT link.\n%s' % traceback.format_exc())
1145
Jon Salz0697cbf2012-07-04 15:14:04 +08001146 # Note that we create a log watcher even if
1147 # sync_event_log_period_secs isn't set (no background
1148 # syncing), since we may use it to flush event logs as well.
1149 self.log_watcher = EventLogWatcher(
Ricky Liang45c73e72015-01-15 15:00:30 +08001150 self.test_list.options.sync_event_log_period_secs,
1151 event_log_db_file=None,
1152 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001153 if self.test_list.options.sync_event_log_period_secs:
1154 self.log_watcher.StartWatchThread()
1155
1156 self.update_system_info()
1157
1158 os.environ['CROS_FACTORY'] = '1'
1159 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1160
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001161 # Should not move earlier.
1162 self.hooks.OnStartup()
1163
Ricky Liang36512a32014-07-25 11:47:04 +08001164 # Only after this point the Goofy backend is ready for UI connection.
1165 self.ready_for_ui_connection = True
1166
Jon Salz0697cbf2012-07-04 15:14:04 +08001167 def state_change_callback(test, test_state):
1168 self.event_client.post_event(
Ricky Liang4bff3e32014-02-20 18:46:11 +08001169 Event(Event.Type.STATE_CHANGE, path=test.path, state=test_state))
Jon Salz0697cbf2012-07-04 15:14:04 +08001170 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001171
Vic Yange2c76a82014-10-30 12:48:19 -07001172 self.pytest_prespawner = prespawner.PytestPrespawner()
1173 self.pytest_prespawner.start()
Jon Salza6711d72012-07-18 14:33:03 +08001174
Ricky Liang48e47f92014-02-26 19:31:51 +08001175 tests_after_shutdown = self.state_instance.get_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001176 TESTS_AFTER_SHUTDOWN, optional=True)
Jon Salz5c344f62012-07-13 14:31:16 +08001177 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001178
Jon Salz5c344f62012-07-13 14:31:16 +08001179 if not force_auto_run and tests_after_shutdown is not None:
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001180 logging.info('Resuming tests after shutdown: %r', tests_after_shutdown)
1181 self.test_list_iterator = tests_after_shutdown
Wei-Han Chenbcac7252017-04-21 19:46:51 +08001182 self.test_list_iterator.SetTestList(self.test_list)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001183 self.run_enqueue(self.run_next_test)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001184 elif force_auto_run or self.test_list.options.auto_run_on_start:
1185 # If automation mode is enabled, allow suppress auto_run_on_start.
1186 if (self.options.automation_mode == 'NONE' or
1187 self.options.auto_run_on_start):
1188 status_filter = [TestState.UNTESTED]
1189 if self.test_list.options.retry_failed_on_start:
1190 status_filter.append(TestState.FAILED)
1191 self.run_enqueue(lambda: self.run_tests(self.test_list, status_filter))
1192 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, None)
Ricky Liang4bff3e32014-02-20 18:46:11 +08001193 self.restore_active_run_state()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001194
Hung-Te Lin410f70a2015-12-15 14:53:42 +08001195 self.dut.hooks.OnTestStart()
Vic Yang08505c72015-01-06 17:01:53 -08001196
Dean Liao592e4d52013-01-10 20:06:39 +08001197 self.may_disable_cros_shortcut_keys()
1198
1199 def may_disable_cros_shortcut_keys(self):
1200 test_options = self.test_list.options
1201 if test_options.disable_cros_shortcut_keys:
1202 logging.info('Filter ChromeOS shortcut keys.')
1203 self.key_filter = KeyFilter(
1204 unmap_caps_lock=test_options.disable_caps_lock,
1205 caps_lock_keycode=test_options.caps_lock_keycode)
1206 self.key_filter.Start()
1207
Peter Ammon1e1ec572014-06-26 17:56:32 -07001208 def perform_periodic_tasks(self):
1209 """Override of base method to perform periodic work.
Vic Yang4953fc12012-07-26 16:19:53 +08001210
Peter Ammon1e1ec572014-06-26 17:56:32 -07001211 This method must not raise exceptions.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001212 """
Peter Ammon1e1ec572014-06-26 17:56:32 -07001213 super(Goofy, self).perform_periodic_tasks()
Jon Salzb22d1172012-08-06 10:38:57 +08001214
Earl Oua3bca122016-10-21 16:00:30 +08001215 self.check_plugins()
cychiang21886742012-07-05 15:16:32 +08001216 self.check_for_updates()
Jon Salz57717ca2012-04-04 16:47:25 +08001217
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001218 def handle_event_logs(self, chunks, periodic=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001219 """Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001220
Jon Salz0697cbf2012-07-04 15:14:04 +08001221 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001222
1223 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001224 chunks: A list of Chunk objects.
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001225 periodic: This event log handling is periodic. Error messages
1226 will only be shown for the first time.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001227 """
Vic Yang93027612013-05-06 02:42:49 +08001228 first_exception = None
1229 exception_count = 0
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001230 # Suppress error messages for periodic event syncing except for the
1231 # first time. If event syncing is not periodic, always show the error
1232 # messages.
1233 quiet = self._suppress_event_log_error_messages if periodic else False
Vic Yang93027612013-05-06 02:42:49 +08001234
Jon Salzd15bbcf2013-05-21 17:33:57 +08001235 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001236 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001237 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001238 start_time = time.time()
1239 shopfloor_client = shopfloor.get_instance(
Ricky Liang45c73e72015-01-15 15:00:30 +08001240 detect=True,
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001241 timeout=self.test_list.options.shopfloor_timeout_secs,
1242 quiet=quiet)
Ricky Liang45c73e72015-01-15 15:00:30 +08001243 shopfloor_client.UploadEvent(chunk.log_name + '.' +
Jon Salzd15bbcf2013-05-21 17:33:57 +08001244 event_log.GetReimageId(),
1245 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001246 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +08001247 'Successfully synced %s in %.03f s',
1248 description, time.time() - start_time)
1249 except: # pylint: disable=W0702
Hung-Te Linf707b242016-01-08 23:11:42 +08001250 first_exception = (first_exception or
1251 (chunk.log_name + ': ' +
1252 debug_utils.FormatExceptionOnly()))
Vic Yang93027612013-05-06 02:42:49 +08001253 exception_count += 1
1254
1255 if exception_count:
1256 if exception_count == 1:
1257 msg = 'Log upload failed: %s' % first_exception
1258 else:
1259 msg = '%d log upload failed; first is: %s' % (
1260 exception_count, first_exception)
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001261 # For periodic event log syncing, only show the first error messages.
1262 if periodic:
1263 if not self._suppress_event_log_error_messages:
1264 self._suppress_event_log_error_messages = True
1265 logging.warning('Suppress periodic shopfloor error messages for '
1266 'event log syncing after the first one.')
1267 raise Exception(msg)
1268 # For event log syncing by request, show the error messages.
1269 else:
1270 raise Exception(msg)
Vic Yang93027612013-05-06 02:42:49 +08001271
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001272 def run_tests_with_status(self, statuses_to_run, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001273 """Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001274
Jon Salz0697cbf2012-07-04 15:14:04 +08001275 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001276
Jon Salz0697cbf2012-07-04 15:14:04 +08001277 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001278 statuses_to_run: The particular status that caller wants to run.
Jon Salz0697cbf2012-07-04 15:14:04 +08001279 starting_at: If provided, only auto-runs tests beginning with
1280 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001281 root: The root of tests to run. If not provided, it will be
1282 the root of all tests.
1283 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001284 root = root or self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +08001285 self.abort_active_tests('Operator requested run/re-run of certain tests')
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001286 self.run_tests(root, status_filter=statuses_to_run)
Jon Salz0405ab52012-03-16 15:26:52 +08001287
Jon Salz0697cbf2012-07-04 15:14:04 +08001288 def restart_tests(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001289 """Restarts all tests."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001290 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001291
Jon Salz6dc031d2013-06-19 13:06:23 +08001292 self.abort_active_tests('Operator requested restart of certain tests')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +08001293 for test in root.Walk():
1294 test.UpdateState(status=TestState.UNTESTED)
Jon Salz0697cbf2012-07-04 15:14:04 +08001295 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001296
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001297 def auto_run(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001298 """"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001299
Jon Salz0697cbf2012-07-04 15:14:04 +08001300 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001301 root: If provided, the root of tests to run. If not provided, the root
1302 will be test_list (root of all tests).
1303 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001304 root = root or self.test_list
1305 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Ricky Liang45c73e72015-01-15 15:00:30 +08001306 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001307
Jon Salz0697cbf2012-07-04 15:14:04 +08001308 def handle_switch_test(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001309 """Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001310
Ricky Liang6fe218c2013-12-27 15:17:17 +08001311 Args:
1312 event: The SWITCH_TEST event.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001313 """
Wei-Han Chen3ae204c2017-04-28 19:36:55 +08001314 test = self.test_list.LookupPath(event.path)
Jon Salz0697cbf2012-07-04 15:14:04 +08001315 if not test:
1316 logging.error('Unknown test %r', event.key)
1317 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001318
Jon Salz0697cbf2012-07-04 15:14:04 +08001319 invoc = self.invocations.get(test)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001320 if invoc:
Jon Salz0697cbf2012-07-04 15:14:04 +08001321 # Already running: just bring to the front if it
1322 # has a UI.
1323 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001324 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001325 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001326
Jon Salz6dc031d2013-06-19 13:06:23 +08001327 self.abort_active_tests('Operator requested abort (switch_test)')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +08001328 for t in test.Walk():
1329 t.UpdateState(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001330
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001331 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001332
Wei-Ning Huang38b75f02015-02-25 18:25:14 +08001333 def handle_key_filter_mode(self, event):
1334 if self.key_filter:
1335 if getattr(event, 'enabled'):
1336 self.key_filter.Start()
1337 else:
1338 self.key_filter.Stop()
1339
Jon Salz0697cbf2012-07-04 15:14:04 +08001340 def wait(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001341 """Waits for all pending invocations.
Jon Salz0697cbf2012-07-04 15:14:04 +08001342
1343 Useful for testing.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001344 """
Jon Salz1acc8742012-07-17 17:45:55 +08001345 while self.invocations:
1346 for k, v in self.invocations.iteritems():
1347 logging.info('Waiting for %s to complete...', k)
1348 v.thread.join()
1349 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001350
Claire Changd1961a22015-08-05 16:15:55 +08001351 def test_fail(self, test):
Hung-Te Lin410f70a2015-12-15 14:53:42 +08001352 self.dut.hooks.OnTestFailure(test)
Claire Changd1961a22015-08-05 16:15:55 +08001353 if self.link_manager:
1354 self.link_manager.UpdateStatus(False)
1355
Wei-Han Chenced08ef2016-11-08 09:40:02 +08001356
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001357if __name__ == '__main__':
Peter Ammona3d298c2014-09-23 10:11:02 -07001358 Goofy.run_main_and_exit()