blob: 24e3f185c9d94cf6bacc1c55a5e8a457b36dc789 [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
Hung-Te Lin1990b742017-08-09 17:34:57 +08002# Copyright 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08006"""The main factory flow that runs the factory test and finalizes a device."""
Hung-Te Linf2f78f72012-02-08 19:27:11 +08007
Joel Kitchingb85ed7f2014-10-08 18:24:39 +08008from __future__ import print_function
9
Jon Salz0405ab52012-03-16 15:26:52 +080010import logging
Wei-Han Chenc17b4112016-11-22 14:56:51 +080011from optparse import OptionParser
Jon Salz0405ab52012-03-16 15:26:52 +080012import os
Jon Salz77c151e2012-08-28 07:20:37 +080013import signal
Jon Salz0405ab52012-03-16 15:26:52 +080014import sys
Jon Salz0405ab52012-03-16 15:26:52 +080015import threading
16import time
17import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080018import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080019from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080020
Peter Shihfdf17682017-05-26 11:38:39 +080021import factory_common # pylint: disable=unused-import
Hung-Te Linb6287242016-05-18 14:39:05 +080022from cros.factory.device import device_utils
Vic Yangd80ea752014-09-24 16:07:14 +080023from cros.factory.goofy.goofy_base import GoofyBase
24from cros.factory.goofy.goofy_rpc import GoofyRPC
Earl Ouacbe99c2017-02-21 16:04:19 +080025from cros.factory.goofy import goofy_server
Vic Yangd80ea752014-09-24 16:07:14 +080026from cros.factory.goofy.invocation import TestInvocation
27from cros.factory.goofy.link_manager import PresenterLinkManager
Earl Oua3bca122016-10-21 16:00:30 +080028from cros.factory.goofy.plugins import plugin_controller
Vic Yange2c76a82014-10-30 12:48:19 -070029from cros.factory.goofy import prespawner
Earl Oua3bca122016-10-21 16:00:30 +080030from cros.factory.goofy import test_environment
Wei-Han Chenc17b4112016-11-22 14:56:51 +080031from cros.factory.goofy.test_list_iterator import TestListIterator
Earl Oua3bca122016-10-21 16:00:30 +080032from cros.factory.goofy import updater
Vic Yangd80ea752014-09-24 16:07:14 +080033from cros.factory.goofy.web_socket_manager import WebSocketManager
Wei-Han Chen109d76f2017-08-08 18:50:35 +080034from cros.factory.test import device_data
Hung-Te Linb6287242016-05-18 14:39:05 +080035from cros.factory.test.e2e_test.common import AutomationMode
36from cros.factory.test.e2e_test.common import AutomationModePrompt
37from cros.factory.test.e2e_test.common import ParseAutomationMode
Earl Ouacbe99c2017-02-21 16:04:19 +080038from cros.factory.test.env import goofy_proxy
Hung-Te Linb6287242016-05-18 14:39:05 +080039from cros.factory.test.env import paths
Jon Salz83591782012-06-26 11:09:58 +080040from cros.factory.test.event import Event
41from cros.factory.test.event import EventClient
42from cros.factory.test.event import EventServer
Hung-Te Linb6287242016-05-18 14:39:05 +080043from cros.factory.test import event_log
44from cros.factory.test.event_log import EventLog
Hung-Te Linb6287242016-05-18 14:39:05 +080045from cros.factory.test.event_log import GetBootSequence
Hung-Te Lin91492a12014-11-25 18:56:30 +080046from cros.factory.test.event_log_watcher import EventLogWatcher
Earl Oua3bca122016-10-21 16:00:30 +080047from cros.factory.test import factory
jcliangcd688182012-08-20 21:01:26 +080048from cros.factory.test.factory import TestState
Peter Shihce03c2e2017-03-21 17:36:10 +080049from cros.factory.test.i18n import html_translator
Peter Shihf65db932017-03-22 17:06:34 +080050from cros.factory.test.i18n import test_ui as i18n_test_ui
Peter Shih80e78b42017-03-10 17:00:56 +080051from cros.factory.test.i18n import translation
Hung-Te Lin3f096842016-01-13 17:37:06 +080052from cros.factory.test.rules import phase
Earl Oua3bca122016-10-21 16:00:30 +080053from cros.factory.test import shopfloor
54from cros.factory.test import state
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +080055from cros.factory.test.test_lists import manager
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'
Peter Shihb4e49352017-05-25 17:35:11 +080072CACHES_DIR = os.path.join(paths.DATA_STATE_DIR, '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
Jon Salz73e0fd02012-04-04 11:46:38 +080084_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080085
Ricky Liang45c73e72015-01-15 15:00:30 +080086
Peter Ammon1e1ec572014-06-26 17:56:32 -070087class Goofy(GoofyBase):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +080088 """The main factory flow.
Jon Salz0697cbf2012-07-04 15:14:04 +080089
90 Note that all methods in this class must be invoked from the main
91 (event) thread. Other threads, such as callbacks and TestInvocation
92 methods, should instead post events on the run queue.
93
94 TODO: Unit tests. (chrome-os-partner:7409)
95
96 Properties:
97 uuid: A unique UUID for this invocation of Goofy.
98 state_instance: An instance of FactoryState.
99 state_server: The FactoryState XML/RPC server.
100 state_server_thread: A thread running state_server.
101 event_server: The EventServer socket server.
102 event_server_thread: A thread running event_server.
103 event_client: A client to the event server.
Earl Oua3bca122016-10-21 16:00:30 +0800104 plugin_controller: The PluginController object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800105 invocations: A map from FactoryTest objects to the corresponding
106 TestInvocations objects representing active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800107 options: Command-line options.
108 args: Command-line args.
109 test_list: The test list.
Jon Salz128b0932013-07-03 16:55:26 +0800110 test_lists: All new-style test lists.
Ricky Liang4bff3e32014-02-20 18:46:11 +0800111 run_id: The identifier for latest test run.
112 scheduled_run_tests: The list of tests scheduled for latest test run.
Jon Salz0697cbf2012-07-04 15:14:04 +0800113 event_handlers: Map of Event.Type to the method used to handle that
114 event. If the method has an 'event' argument, the event is passed
115 to the handler.
Jon Salz416f9cc2013-05-10 18:32:50 +0800116 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salzd7550792013-07-12 05:49:27 +0800117 status: The current Goofy status (a member of the Status enum).
Peter Ammon948b7172014-07-15 12:43:06 -0700118 link_manager: Instance of PresenterLinkManager for communicating
119 with GoofyPresenter
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800120 """
Ricky Liang45c73e72015-01-15 15:00:30 +0800121
Jon Salz0697cbf2012-07-04 15:14:04 +0800122 def __init__(self):
Peter Ammon1e1ec572014-06-26 17:56:32 -0700123 super(Goofy, self).__init__()
Jon Salz0697cbf2012-07-04 15:14:04 +0800124 self.uuid = str(uuid.uuid4())
125 self.state_instance = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800126 self.goofy_server = None
127 self.goofy_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800128 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800129 self.event_server = None
130 self.event_server_thread = None
131 self.event_client = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800132 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800133 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800134 self.testlog = None
Earl Oua3bca122016-10-21 16:00:30 +0800135 self.plugin_controller = None
Vic Yange2c76a82014-10-30 12:48:19 -0700136 self.pytest_prespawner = None
Vic Yanga3cecf82014-12-26 00:44:21 -0800137 self._ui_initialized = False
Jon Salzc79a9982012-08-30 04:42:01 +0800138 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800139 self.invocations = {}
Jon Salz0697cbf2012-07-04 15:14:04 +0800140 self.visible_test = None
141 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800142 self.hooks = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800143
144 self.options = None
145 self.args = None
146 self.test_list = None
Jon Salz128b0932013-07-03 16:55:26 +0800147 self.test_lists = None
Ricky Liang4bff3e32014-02-20 18:46:11 +0800148 self.run_id = None
149 self.scheduled_run_tests = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800150 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800151 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800152 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800153 self.last_update_check = None
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800154 self._suppress_periodic_update_messages = False
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +0800155 self._suppress_event_log_error_messages = False
Earl Ouab979142016-10-25 16:48:06 +0800156 self.exclusive_resources = set()
Dean Liao592e4d52013-01-10 20:06:39 +0800157 self.key_filter = None
Jon Salzd7550792013-07-12 05:49:27 +0800158 self.status = Status.UNINITIALIZED
Ricky Liang36512a32014-07-25 11:47:04 +0800159 self.ready_for_ui_connection = False
Peter Ammon1e1ec572014-06-26 17:56:32 -0700160 self.link_manager = None
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800161 self.is_restart_requested = False
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800162 self.test_list_iterator = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800163
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +0800164 self.test_list_manager = manager.Manager()
165
Peter Shih7cc81b12017-08-24 13:04:46 +0800166 self._default_test_ui_html = None
167
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800168 # TODO(hungte) Support controlling remote DUT.
Hung-Te Linb6287242016-05-18 14:39:05 +0800169 self.dut = device_utils.CreateDUTInterface()
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800170
Jon Salz85a39882012-07-05 16:45:04 +0800171 def test_or_root(event, parent_or_group=True):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800172 """Returns the test affected by a particular event.
Jon Salz85a39882012-07-05 16:45:04 +0800173
174 Args:
175 event: The event containing an optional 'path' attribute.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800176 parent_or_group: If True, returns the top-level parent for a test (the
Jon Salz85a39882012-07-05 16:45:04 +0800177 root node of the tests that need to be run together if the given test
178 path is to be run).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800179 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800180 try:
181 path = event.path
182 except AttributeError:
183 path = None
184
185 if path:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800186 test = self.test_list.LookupPath(path)
Jon Salz85a39882012-07-05 16:45:04 +0800187 if parent_or_group:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800188 test = test.GetTopLevelParentOrGroup()
Jon Salz85a39882012-07-05 16:45:04 +0800189 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800190 else:
Peter Shih999faf72017-07-07 11:32:42 +0800191 return self.test_list.ToFactoryTestList()
Jon Salz0697cbf2012-07-04 15:14:04 +0800192
193 self.event_handlers = {
Ricky Liang45c73e72015-01-15 15:00:30 +0800194 Event.Type.RESTART_TESTS:
195 lambda event: self.restart_tests(root=test_or_root(event)),
196 Event.Type.AUTO_RUN:
197 lambda event: self.auto_run(root=test_or_root(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800198 Event.Type.RUN_TESTS_WITH_STATUS:
199 lambda event: self.run_tests_with_status(
200 event.status,
201 root=test_or_root(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800202 Event.Type.UPDATE_SYSTEM_INFO:
203 lambda event: self.update_system_info(),
204 Event.Type.STOP:
205 lambda event: self.stop(root=test_or_root(event, False),
206 fail=getattr(event, 'fail', False),
207 reason=getattr(event, 'reason', None)),
208 Event.Type.SET_VISIBLE_TEST:
209 lambda event: self.set_visible_test(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800210 self.test_list.LookupPath(event.path)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800211 Event.Type.CLEAR_STATE:
212 lambda event: self.clear_state(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800213 self.test_list.LookupPath(event.path)),
Wei-Ning Huang38b75f02015-02-25 18:25:14 +0800214 Event.Type.KEY_FILTER_MODE: self.handle_key_filter_mode,
Jon Salz0697cbf2012-07-04 15:14:04 +0800215 }
216
Jon Salz0697cbf2012-07-04 15:14:04 +0800217 self.web_socket_manager = None
218
219 def destroy(self):
Ricky Liang74237a02014-09-18 15:11:23 +0800220 """Performs any shutdown tasks. Overrides base class method."""
chuntsen9d675c62017-06-20 14:35:30 +0800221 # To avoid race condition when running shutdown test.
222 for test, invoc in self.invocations.iteritems():
223 logging.info('Waiting for %s to complete...', test)
224 invoc.thread.join(3) # Timeout in 3 seconds.
225
Jon Salzd7550792013-07-12 05:49:27 +0800226 self.status = Status.TERMINATING
Jon Salz0697cbf2012-07-04 15:14:04 +0800227 if self.chrome:
228 self.chrome.kill()
229 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800230 if self.dummy_shopfloor:
231 self.dummy_shopfloor.kill()
232 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800233 if self.web_socket_manager:
234 logging.info('Stopping web sockets')
235 self.web_socket_manager.close()
236 self.web_socket_manager = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800237 if self.goofy_server_thread:
238 logging.info('Stopping goofy server')
239 self.goofy_server.shutdown()
240 self.goofy_server_thread.join()
241 self.goofy_server.server_close()
242 self.goofy_server_thread = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800243 if self.state_instance:
244 self.state_instance.close()
245 if self.event_server_thread:
246 logging.info('Stopping event server')
Peter Shihce9490e2017-05-11 14:32:12 +0800247 net_utils.ShutdownTCPServer(self.event_server)
Jon Salz0697cbf2012-07-04 15:14:04 +0800248 self.event_server_thread.join()
249 self.event_server.server_close()
250 self.event_server_thread = None
251 if self.log_watcher:
252 if self.log_watcher.IsThreadStarted():
253 self.log_watcher.StopWatchThread()
254 self.log_watcher = None
Vic Yange2c76a82014-10-30 12:48:19 -0700255 if self.pytest_prespawner:
256 logging.info('Stopping pytest prespawner')
257 self.pytest_prespawner.stop()
258 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800259 if self.event_client:
260 logging.info('Closing event client')
261 self.event_client.close()
262 self.event_client = None
263 if self.event_log:
264 self.event_log.Close()
265 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800266 if self.testlog:
267 self.testlog.Close()
268 self.testlog = None
Dean Liao592e4d52013-01-10 20:06:39 +0800269 if self.key_filter:
270 self.key_filter.Stop()
Peter Ammon1e1ec572014-06-26 17:56:32 -0700271 if self.link_manager:
272 self.link_manager.Stop()
273 self.link_manager = None
Earl Oua3bca122016-10-21 16:00:30 +0800274 if self.plugin_controller:
275 self.plugin_controller.StopAndDestroyAllPlugins()
276 self.plugin_controller = None
Dean Liao592e4d52013-01-10 20:06:39 +0800277
Peter Ammon1e1ec572014-06-26 17:56:32 -0700278 super(Goofy, self).destroy()
Jon Salz0697cbf2012-07-04 15:14:04 +0800279 logging.info('Done destroying Goofy')
Jon Salzd7550792013-07-12 05:49:27 +0800280 self.status = Status.TERMINATED
Jon Salz0697cbf2012-07-04 15:14:04 +0800281
Earl Ouacbe99c2017-02-21 16:04:19 +0800282 def start_goofy_server(self):
283 self.goofy_server = goofy_server.GoofyServer(
Shen-En Shihd5b96bf2017-08-09 17:47:21 +0800284 (goofy_proxy.DEFAULT_GOOFY_BIND, goofy_proxy.DEFAULT_GOOFY_PORT))
Earl Ouacbe99c2017-02-21 16:04:19 +0800285 logging.info('Starting goofy server')
286 self.goofy_server_thread = threading.Thread(
287 target=self.goofy_server.serve_forever,
288 name='GoofyServer')
289 self.goofy_server_thread.start()
290
Peter Shih7cc81b12017-08-24 13:04:46 +0800291 static_path = os.path.join(paths.FACTORY_PYTHON_PACKAGE_DIR, 'goofy/static')
Earl Ouacbe99c2017-02-21 16:04:19 +0800292 # Setup static file path
Peter Shih7cc81b12017-08-24 13:04:46 +0800293 self.goofy_server.RegisterPath('/', static_path)
Peter Shihce03c2e2017-03-21 17:36:10 +0800294 # index.html needs to be preprocessed.
Peter Shih7cc81b12017-08-24 13:04:46 +0800295 index_path = os.path.join(static_path, 'index.html')
Peter Shihce03c2e2017-03-21 17:36:10 +0800296 index_html = html_translator.TranslateHTML(file_utils.ReadFile(index_path))
297 self.goofy_server.RegisterData('/index.html', 'text/html', index_html)
Earl Ouacbe99c2017-02-21 16:04:19 +0800298
Peter Shih7cc81b12017-08-24 13:04:46 +0800299 default_test_ui_path = os.path.join(static_path,
300 'ui_templates/default_test_ui.html')
301 self._default_test_ui_html = html_translator.TranslateHTML(
302 file_utils.ReadFile(default_test_ui_path))
303
Earl Ouacbe99c2017-02-21 16:04:19 +0800304 def init_state_instance(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800305 # Before starting state server, remount stateful partitions with
306 # no commit flag. The default commit time (commit=600) makes corruption
307 # too likely.
Hung-Te Lin1968d9c2016-01-08 22:55:46 +0800308 sys_utils.ResetCommitTime()
Earl Ouacbe99c2017-02-21 16:04:19 +0800309 self.state_instance = state.FactoryState()
310 self.goofy_server.AddRPCInstance(goofy_proxy.STATE_URL, self.state_instance)
Jon Salz2af235d2013-06-24 14:47:21 +0800311
Earl Ouacbe99c2017-02-21 16:04:19 +0800312 # Setup Goofy RPC.
313 # TODO(shunhsingou): separate goofy_rpc and state server instead of
314 # injecting goofy_rpc functions into state.
Jon Salz16d10542012-07-23 12:18:45 +0800315 self.goofy_rpc = GoofyRPC(self)
316 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800317
Peter Shih80e78b42017-03-10 17:00:56 +0800318 def init_i18n(self):
319 js_data = 'var goofy_i18n_data = %s;' % translation.GetAllI18nDataJS()
Peter Shihce03c2e2017-03-21 17:36:10 +0800320 self.goofy_server.RegisterData('/js/goofy-translations.js',
321 'application/javascript', js_data)
Peter Shihf65db932017-03-22 17:06:34 +0800322 self.goofy_server.RegisterData('/css/i18n.css',
323 'text/css', i18n_test_ui.GetStyleSheet())
Peter Shih80e78b42017-03-10 17:00:56 +0800324
Jon Salz0697cbf2012-07-04 15:14:04 +0800325 def start_event_server(self):
326 self.event_server = EventServer()
327 logging.info('Starting factory event server')
328 self.event_server_thread = threading.Thread(
Ricky Liang45c73e72015-01-15 15:00:30 +0800329 target=self.event_server.serve_forever,
Peter Shihfdf17682017-05-26 11:38:39 +0800330 name='EventServer')
Jon Salz0697cbf2012-07-04 15:14:04 +0800331 self.event_server_thread.start()
332
333 self.event_client = EventClient(
Ricky Liang45c73e72015-01-15 15:00:30 +0800334 callback=self.handle_event, event_loop=self.run_queue)
Jon Salz0697cbf2012-07-04 15:14:04 +0800335
336 self.web_socket_manager = WebSocketManager(self.uuid)
Earl Ouacbe99c2017-02-21 16:04:19 +0800337 self.goofy_server.AddHTTPGetHandler(
338 '/event', self.web_socket_manager.handle_web_socket)
Jon Salz0697cbf2012-07-04 15:14:04 +0800339
Jon Salz0697cbf2012-07-04 15:14:04 +0800340 def set_visible_test(self, test):
341 if self.visible_test == test:
342 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800343 if self.visible_test:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800344 self.visible_test.UpdateState(visible=False)
Peter Shihd87297f2017-08-21 15:43:13 +0800345 if test:
346 test.UpdateState(visible=True)
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:
Wei-Han Chen29663c12017-06-27 10:28:54 +0800406 goofy_error = 'TESTS_AFTER_SHTUDOWN is not set'
Ricky Liang48e47f92014-02-26 19:31:51 +0800407 self.state_instance.set_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800408 TESTS_AFTER_SHUTDOWN, TestListIterator(test))
409 else:
Wei-Han Chen29663c12017-06-27 10:28:54 +0800410 goofy_error = tests_after_shutdown.RestartLastTest()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800411 self.state_instance.set_shared_data(
412 TESTS_AFTER_SHUTDOWN, tests_after_shutdown)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800413
Ricky Liang48e47f92014-02-26 19:31:51 +0800414 # Set 'post_shutdown' to inform shutdown test that a shutdown just occurred.
Ricky Liangb7eb8772014-09-15 18:05:22 +0800415 self.state_instance.set_shared_data(
Wei-Han Chen29663c12017-06-27 10:28:54 +0800416 state.KEY_POST_SHUTDOWN % test.path,
417 {'invocation': self.state_instance.get_test_state(test.path).invocation,
418 'goofy_error': goofy_error})
Jon Salz258a40c2012-04-19 12:34:01 +0800419
Jon Salz0697cbf2012-07-04 15:14:04 +0800420 def init_states(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800421 """Initializes all states on startup."""
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800422 for test in self.test_list.GetAllTests():
Jon Salz0697cbf2012-07-04 15:14:04 +0800423 # Make sure the state server knows about all the tests,
424 # defaulting to an untested state.
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800425 test.UpdateState(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800426
Earl Ouf76e55c2017-03-07 11:48:34 +0800427 is_unexpected_shutdown = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800428
Jon Salz0697cbf2012-07-04 15:14:04 +0800429 # Any 'active' tests should be marked as failed now.
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800430 for test in self.test_list.Walk():
431 if not test.IsLeaf():
Jon Salza6711d72012-07-18 14:33:03 +0800432 # Don't bother with parents; they will be updated when their
433 # children are updated.
434 continue
435
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800436 test_state = test.GetState()
Jon Salz0697cbf2012-07-04 15:14:04 +0800437 if test_state.status != TestState.ACTIVE:
438 continue
439 if isinstance(test, factory.ShutdownStep):
440 # Shutdown while the test was active - that's good.
Ricky Liang48e47f92014-02-26 19:31:51 +0800441 self.handle_shutdown_complete(test)
Jon Salz0697cbf2012-07-04 15:14:04 +0800442 else:
Earl Ouf76e55c2017-03-07 11:48:34 +0800443 is_unexpected_shutdown = True
Jon Salz0697cbf2012-07-04 15:14:04 +0800444 error_msg = 'Unexpected shutdown while test was running'
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800445 # TODO(itspeter): Add testlog to collect expired session infos.
Jon Salz0697cbf2012-07-04 15:14:04 +0800446 self.event_log.Log('end_test',
Ricky Liang45c73e72015-01-15 15:00:30 +0800447 path=test.path,
448 status=TestState.FAILED,
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800449 invocation=test.GetState().invocation,
Earl Ouf76e55c2017-03-07 11:48:34 +0800450 error_msg=error_msg)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800451 test.UpdateState(
Ricky Liang45c73e72015-01-15 15:00:30 +0800452 status=TestState.FAILED,
453 error_msg=error_msg)
Chun-Ta Lin87c2dac2015-05-02 01:35:01 -0700454 # Trigger the OnTestFailure callback.
Claire Changd1961a22015-08-05 16:15:55 +0800455 self.run_queue.put(lambda: self.test_fail(test))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800456
Jon Salz50efe942012-07-26 11:54:10 +0800457 if not test.never_fails:
458 # For "never_fails" tests (such as "Start"), don't cancel
459 # pending tests, since reboot is expected.
460 factory.console.info('Unexpected shutdown while test %s '
461 'running; cancelling any pending tests',
462 test.path)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800463 # cancel pending tests by replace the iterator with an empty one
464 self.state_instance.set_shared_data(
465 TESTS_AFTER_SHUTDOWN,
466 TestListIterator(None))
Jon Salz008f4ea2012-08-28 05:39:45 +0800467
Earl Ouf76e55c2017-03-07 11:48:34 +0800468 if is_unexpected_shutdown:
469 logging.warning("Unexpected shutdown.")
470 self.dut.hooks.OnUnexpectedReboot()
471
Wei-Han Chen109d76f2017-08-08 18:50:35 +0800472 if self.test_list.options.read_device_data_from_vpd_on_init:
473 vpd_data = {}
474 for section in [device_data.NAME_RO, device_data.NAME_RW]:
475 try:
476 vpd_data[section] = self.dut.vpd.boot.GetPartition(section).GetAll()
477 except Exception:
478 logging.exception('Failed to read %s_VPD, ignored...',
479 section.upper())
480 # using None for key_map will use default key_map
481 device_data.UpdateDeviceDataFromVPD(None, vpd_data)
482
Wei-Han Chen212d2af2017-08-03 18:12:23 +0800483 # state_instance is initialized, we can mark skipped and waived tests now.
484 self.test_list.SetSkippedAndWaivedTests()
485
Jon Salz0697cbf2012-07-04 15:14:04 +0800486 def handle_event(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800487 """Handles an event from the event server."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800488 handler = self.event_handlers.get(event.type)
489 if handler:
490 handler(event)
491 else:
492 # We don't register handlers for all event types - just ignore
493 # this event.
494 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800495
Vic Yangaabf9fd2013-04-09 18:56:13 +0800496 def check_critical_factory_note(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800497 """Returns True if the last factory note is critical."""
Vic Yangaabf9fd2013-04-09 18:56:13 +0800498 notes = self.state_instance.get_shared_data('factory_note', True)
499 return notes and notes[-1]['level'] == 'CRITICAL'
500
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800501 def schedule_restart(self):
502 """Schedules a restart event when any invocation is completed."""
503 self.is_restart_requested = True
504
505 def invocation_completion(self):
506 """Callback when an invocation is completed."""
507 if self.is_restart_requested:
508 logging.info('Restart by scheduled event.')
509 self.is_restart_requested = False
510 self.restart_tests()
511 else:
512 self.run_next_test()
513
Jon Salz0697cbf2012-07-04 15:14:04 +0800514 def run_next_test(self):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800515 """Runs the next eligible test.
henryhsu4cc6b022014-04-22 17:12:42 +0800516
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800517 self.test_list_iterator (a TestListIterator object) will determine which
518 test should be run.
henryhsu4cc6b022014-04-22 17:12:42 +0800519 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800520 self.reap_completed_tests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800521
522 if self.invocations:
523 # there are tests still running, we cannot start new tests
Vic Yangaabf9fd2013-04-09 18:56:13 +0800524 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800525
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800526 if self.check_critical_factory_note():
527 logging.info('has critical factory note, stop running')
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800528 self.test_list_iterator.Stop()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800529 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800530
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800531 while True:
532 try:
533 path = self.test_list_iterator.next()
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800534 test = self.test_list.LookupPath(path)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800535 except StopIteration:
536 logging.info('no next test, stop running')
Jon Salz0697cbf2012-07-04 15:14:04 +0800537 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800538
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800539 # check if we have run all required tests
Jon Salz304a75d2012-07-06 11:14:15 +0800540 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800541 for requirement in test.require_run:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800542 for i in requirement.test.Walk():
Jon Salza1412922012-07-23 16:04:17 +0800543 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800544 # We've hit this test itself; stop checking
545 break
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800546 if ((i.GetState().status == TestState.UNTESTED) or
Wei-Han Chen3a160172017-07-11 17:31:28 +0800547 (requirement.passed and
548 i.GetState().status not in [TestState.SKIPPED,
549 TestState.PASSED])):
Jon Salz304a75d2012-07-06 11:14:15 +0800550 # Found an untested test; move on to the next
551 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800552 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800553 break
554
555 if untested:
556 untested_paths = ', '.join(sorted([x.path for x in untested]))
557 if self.state_instance.get_shared_data('engineering_mode',
558 optional=True):
559 # In engineering mode, we'll let it go.
560 factory.console.warn('In engineering mode; running '
561 '%s even though required tests '
562 '[%s] have not completed',
563 test.path, untested_paths)
564 else:
565 # Not in engineering mode; mark it failed.
566 error_msg = ('Required tests [%s] have not been run yet'
567 % untested_paths)
568 factory.console.error('Not running %s: %s',
569 test.path, error_msg)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800570 test.UpdateState(status=TestState.FAILED,
571 error_msg=error_msg)
Jon Salz304a75d2012-07-06 11:14:15 +0800572 continue
573
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800574 # okay, let's run the test
Ricky Liang48e47f92014-02-26 19:31:51 +0800575 if (isinstance(test, factory.ShutdownStep) and
Ricky Liangb7eb8772014-09-15 18:05:22 +0800576 self.state_instance.get_shared_data(
Wei-Han Chen29663c12017-06-27 10:28:54 +0800577 state.KEY_POST_SHUTDOWN % test.path, optional=True)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800578 # Invoking post shutdown method of shutdown test. We should retain the
579 # iterations_left and retries_left of the original test state.
580 test_state = self.state_instance.get_test_state(test.path)
581 self._run_test(test, test_state.iterations_left,
582 test_state.retries_left)
583 else:
584 # Starts a new test run; reset iterations and retries.
585 self._run_test(test, test.iterations, test.retries)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800586 return # to leave while
Jon Salz1acc8742012-07-17 17:45:55 +0800587
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800588 def _run_test(self, test, iterations_left=None, retries_left=None):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800589 """Invokes the test.
590
591 The argument `test` should be either a leaf test (no subtests) or a parallel
592 test (all subtests should be run in parallel).
593 """
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800594 if not self._ui_initialized and not test.IsNoHost():
Vic Yanga3cecf82014-12-26 00:44:21 -0800595 self.init_ui()
Jon Salz1acc8742012-07-17 17:45:55 +0800596
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800597 if test.IsLeaf():
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800598 invoc = TestInvocation(
599 self, test, on_completion=self.invocation_completion,
600 on_test_failure=lambda: self.test_fail(test))
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800601 new_state = test.UpdateState(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800602 status=TestState.ACTIVE, increment_count=1, error_msg='',
603 invocation=invoc.uuid, iterations_left=iterations_left,
604 retries_left=retries_left,
605 visible=(self.visible_test == test))
606 invoc.count = new_state.count
607 self.invocations[test] = invoc
Peter Shihb2ecd552017-08-24 17:48:58 +0800608 if self.visible_test is None:
609 self.set_visible_test(test)
610 # Send a INIT_TEST_UI event here, so the test UI are initialized in
611 # order, and the tab order would be same as test list order when there
612 # are parallel tests with UI.
613 self.event_client.post_event(
614 Event(
615 Event.Type.INIT_TEST_UI,
616 html=self._default_test_ui_html,
617 test=test.path,
618 invocation=invoc.uuid))
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800619 self.check_plugins()
620 invoc.start()
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800621 elif test.parallel:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800622 for subtest in test.subtests:
623 # TODO(stimim): what if the subtests *must* be run in parallel?
624 # for example, stressapptest and countdown test.
625
626 # Make sure we don't need to skip it:
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800627 if not self.test_list_iterator.CheckSkip(subtest):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800628 self._run_test(subtest, subtest.iterations, subtest.retries)
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800629 else:
630 # This should never happen, there must be something wrong.
631 # However, we can't raise an exception, otherwise goofy will be closed
632 logging.critical(
633 'Goofy should not get a non-leaf test that is not parallel: %r',
634 test)
635 factory.console.critical(
636 'Goofy should not get a non-leaf test that is not parallel: %r',
637 test)
Jon Salz5f2a0672012-05-22 17:14:06 +0800638
Earl Oua3bca122016-10-21 16:00:30 +0800639 def check_plugins(self):
640 """Check plugins to be paused or resumed."""
641 exclusive_resources = set()
642 for test in self.invocations:
643 exclusive_resources = exclusive_resources.union(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800644 test.GetExclusiveResources())
Earl Oua3bca122016-10-21 16:00:30 +0800645 self.plugin_controller.PauseAndResumePluginByResource(exclusive_resources)
646
cychiang21886742012-07-05 15:16:32 +0800647 def check_for_updates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800648 """Schedules an asynchronous check for updates if necessary."""
cychiang21886742012-07-05 15:16:32 +0800649 if not self.test_list.options.update_period_secs:
650 # Not enabled.
651 return
652
653 now = time.time()
654 if self.last_update_check and (
655 now - self.last_update_check <
656 self.test_list.options.update_period_secs):
657 # Not yet time for another check.
658 return
659
660 self.last_update_check = now
661
You-Cheng Syud4a24bf2017-08-21 17:56:48 +0800662 def handle_check_for_update(
663 reached_shopfloor, toolkit_version, needs_update):
cychiang21886742012-07-05 15:16:32 +0800664 if reached_shopfloor:
You-Cheng Syud4a24bf2017-08-21 17:56:48 +0800665 new_update_toolkit_version = toolkit_version if needs_update else None
666 if self.dut.info.update_toolkit_version != new_update_toolkit_version:
667 logging.info('Received new update TOOLKIT_VERSION: %s',
668 new_update_toolkit_version)
669 self.dut.info.Overrides('update_toolkit_version',
670 new_update_toolkit_version)
Peter Ammon1e1ec572014-06-26 17:56:32 -0700671 self.run_enqueue(self.update_system_info)
You-Cheng Syud4a24bf2017-08-21 17:56:48 +0800672 elif not self._suppress_periodic_update_messages:
673 logging.warning('Suppress error messages for periodic update checking '
674 'after the first one.')
675 self._suppress_periodic_update_messages = True
cychiang21886742012-07-05 15:16:32 +0800676
677 updater.CheckForUpdateAsync(
Ricky Liang45c73e72015-01-15 15:00:30 +0800678 handle_check_for_update,
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800679 self.test_list.options.shopfloor_timeout_secs,
680 self._suppress_periodic_update_messages)
cychiang21886742012-07-05 15:16:32 +0800681
Jon Salza6711d72012-07-18 14:33:03 +0800682 def cancel_pending_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800683 """Cancels any tests in the run queue."""
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800684 self.run_tests(None)
Jon Salza6711d72012-07-18 14:33:03 +0800685
Ricky Liang4bff3e32014-02-20 18:46:11 +0800686 def restore_active_run_state(self):
687 """Restores active run id and the list of scheduled tests."""
688 self.run_id = self.state_instance.get_shared_data('run_id', optional=True)
689 self.scheduled_run_tests = self.state_instance.get_shared_data(
690 'scheduled_run_tests', optional=True)
691
692 def set_active_run_state(self):
693 """Sets active run id and the list of scheduled tests."""
694 self.run_id = str(uuid.uuid4())
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800695 # try our best to predict which tests will be run.
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800696 self.scheduled_run_tests = self.test_list_iterator.GetPendingTests()
Ricky Liang4bff3e32014-02-20 18:46:11 +0800697 self.state_instance.set_shared_data('run_id', self.run_id)
698 self.state_instance.set_shared_data('scheduled_run_tests',
699 self.scheduled_run_tests)
700
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800701 def run_tests(self, subtree, status_filter=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800702 """Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800703
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800704 Run tests under a given subtree.
Jon Salzb1b39092012-05-03 02:05:09 +0800705
Ricky Liang6fe218c2013-12-27 15:17:17 +0800706 Args:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800707 subtree: root of subtree to run or None to run nothing.
Chih-Yu Huang85dc63c2015-08-12 15:21:28 +0800708 status_filter: List of available test states. Only run the tests which
709 states are in the list. Set to None if all test states are available.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800710 """
Hung-Te Lin410f70a2015-12-15 14:53:42 +0800711 self.dut.hooks.OnTestStart()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800712 self.test_list_iterator = TestListIterator(
713 subtree, status_filter, self.test_list)
714 if subtree is not None:
Ricky Liang4bff3e32014-02-20 18:46:11 +0800715 self.set_active_run_state()
Jon Salz0697cbf2012-07-04 15:14:04 +0800716 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800717
Jon Salz0697cbf2012-07-04 15:14:04 +0800718 def reap_completed_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800719 """Removes completed tests from the set of active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800720
721 Also updates the visible test if it was reaped.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800722 """
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800723 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800724 for t, v in dict(self.invocations).iteritems():
725 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800726 test_completed = True
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800727 new_state = t.UpdateState(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800728 del self.invocations[t]
729
Johny Lin62ed2a32015-05-13 11:57:12 +0800730 # Stop on failure if flag is true and there is no retry chances.
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800731 if (self.test_list.options.stop_on_failure and
Johny Lin62ed2a32015-05-13 11:57:12 +0800732 new_state.retries_left < 0 and
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800733 new_state.status == TestState.FAILED):
734 # Clean all the tests to cause goofy to stop.
Ricky Liang45c73e72015-01-15 15:00:30 +0800735 factory.console.info('Stop on failure triggered. Empty the queue.')
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800736 self.cancel_pending_tests()
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800737
Jon Salz1acc8742012-07-17 17:45:55 +0800738 if new_state.iterations_left and new_state.status == TestState.PASSED:
739 # Play it again, Sam!
740 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800741 # new_state.retries_left is obtained after update.
742 # For retries_left == 0, test can still be run for the last time.
743 elif (new_state.retries_left >= 0 and
744 new_state.status == TestState.FAILED):
745 # Still have to retry, Sam!
746 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +0800747
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800748 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800749 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800750
Jon Salz0697cbf2012-07-04 15:14:04 +0800751 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +0800752 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +0800753 self.set_visible_test(None)
754 # Make the first running test, if any, the visible test
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800755 for t in self.test_list.Walk():
Jon Salz0697cbf2012-07-04 15:14:04 +0800756 if t in self.invocations:
757 self.set_visible_test(t)
758 break
759
Jon Salz6dc031d2013-06-19 13:06:23 +0800760 def kill_active_tests(self, abort, root=None, reason=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800761 """Kills and waits for all active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800762
Jon Salz85a39882012-07-05 16:45:04 +0800763 Args:
764 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800765 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800766 root: If set, only kills tests with root as an ancestor.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800767 reason: If set, the abort reason.
768 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800769 self.reap_completed_tests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800770 # since we remove objects while iterating, make a copy
771 for test, invoc in dict(self.invocations).iteritems():
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800772 if root and not test.HasAncestor(root):
Jon Salz85a39882012-07-05 16:45:04 +0800773 continue
774
Ricky Liang45c73e72015-01-15 15:00:30 +0800775 factory.console.info('Killing active test %s...', test.path)
Jon Salz6dc031d2013-06-19 13:06:23 +0800776 invoc.abort_and_join(reason)
Ricky Liang45c73e72015-01-15 15:00:30 +0800777 factory.console.info('Killed %s', test.path)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800778 test.UpdateState(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800779 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +0800780
Jon Salz0697cbf2012-07-04 15:14:04 +0800781 if not abort:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800782 test.UpdateState(status=TestState.UNTESTED)
Jon Salz0697cbf2012-07-04 15:14:04 +0800783 self.reap_completed_tests()
784
Jon Salz6dc031d2013-06-19 13:06:23 +0800785 def stop(self, root=None, fail=False, reason=None):
786 self.kill_active_tests(fail, root, reason)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800787
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800788 self.test_list_iterator.Stop(root)
Jon Salz85a39882012-07-05 16:45:04 +0800789 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +0800790
Jon Salz4712ac72013-02-07 17:12:05 +0800791 def clear_state(self, root=None):
Jon Salzd7550792013-07-12 05:49:27 +0800792 if root is None:
793 root = self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +0800794 self.stop(root, reason='Clearing test state')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800795 for f in root.Walk():
796 if f.IsLeaf():
797 f.UpdateState(status=TestState.UNTESTED)
Jon Salz4712ac72013-02-07 17:12:05 +0800798
Jon Salz6dc031d2013-06-19 13:06:23 +0800799 def abort_active_tests(self, reason=None):
800 self.kill_active_tests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +0800801
802 def main(self):
Jon Salzeff94182013-06-19 15:06:28 +0800803 syslog.openlog('goofy')
804
Jon Salz0697cbf2012-07-04 15:14:04 +0800805 try:
Jon Salzd7550792013-07-12 05:49:27 +0800806 self.status = Status.INITIALIZING
Jon Salz0697cbf2012-07-04 15:14:04 +0800807 self.init()
808 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800809 success=True)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800810 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800811 testlog.StationInit({
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800812 'stationDeviceId': testlog_goofy.GetDeviceID(),
Joel Kitching21bc69b2016-07-13 08:29:52 -0700813 'stationInstallationId': testlog_goofy.GetInstallationID(),
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800814 'count': testlog_goofy.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800815 'success': True}))
Hung-Te Linc8174b52017-06-02 11:11:45 +0800816 except Exception:
Joel Kitching9eb203a2016-04-21 15:36:30 +0800817 try:
818 if self.event_log:
Jon Salz0697cbf2012-07-04 15:14:04 +0800819 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800820 success=False,
821 trace=traceback.format_exc())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800822 if self.testlog:
823 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800824 testlog.StationInit({
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800825 'stationDeviceId': testlog_goofy.GetDeviceID(),
Joel Kitching21bc69b2016-07-13 08:29:52 -0700826 'stationInstallationId': testlog_goofy.GetInstallationID(),
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800827 'count': testlog_goofy.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800828 'success': False,
829 'failureMessage': traceback.format_exc()}))
Hung-Te Linc8174b52017-06-02 11:11:45 +0800830 except Exception:
Joel Kitching9eb203a2016-04-21 15:36:30 +0800831 pass
Jon Salz0697cbf2012-07-04 15:14:04 +0800832 raise
833
Jon Salzd7550792013-07-12 05:49:27 +0800834 self.status = Status.RUNNING
Jon Salzeff94182013-06-19 15:06:28 +0800835 syslog.syslog('Goofy (factory test harness) starting')
Chun-Ta Lin5d12b592015-06-30 00:54:23 -0700836 syslog.syslog('Boot sequence = %d' % GetBootSequence())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800837 syslog.syslog('Goofy init count = %d' % testlog_goofy.GetInitCount())
Jon Salz0697cbf2012-07-04 15:14:04 +0800838 self.run()
839
840 def update_system_info(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800841 """Updates system info."""
Shen-En Shihce8ffe02017-08-01 18:58:09 +0800842 logging.info('Received a notify to update system info.')
843 self.dut.info.Invalidate()
844
845 # Propagate this notify to goofy components
846 try:
847 status_monitor = plugin_controller.GetPluginRPCProxy(
848 'status_monitor.status_monitor')
849 status_monitor.UpdateDeviceInfo()
850 except Exception:
851 logging.debug('Failed to update status monitor plugin.')
Jon Salz0697cbf2012-07-04 15:14:04 +0800852
Jon Salzeb42f0d2012-07-27 19:14:04 +0800853 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800854 """Commences updating factory software.
Jon Salzeb42f0d2012-07-27 19:14:04 +0800855
856 Args:
857 auto_run_on_restart: Auto-run when the machine comes back up.
858 post_update_hook: Code to call after update but immediately before
859 restart.
860
861 Returns:
862 Never if the update was successful (we just reboot).
863 False if the update was unnecessary (no update available).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800864 """
Jon Salz6dc031d2013-06-19 13:06:23 +0800865 self.kill_active_tests(False, reason='Factory software update')
Jon Salza6711d72012-07-18 14:33:03 +0800866 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800867
Jon Salz5c344f62012-07-13 14:31:16 +0800868 def pre_update_hook():
869 if auto_run_on_restart:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800870 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN,
Jon Salz5c344f62012-07-13 14:31:16 +0800871 FORCE_AUTO_RUN)
872 self.state_instance.close()
873
Jon Salzeb42f0d2012-07-27 19:14:04 +0800874 if updater.TryUpdate(pre_update_hook=pre_update_hook):
875 if post_update_hook:
876 post_update_hook()
877 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +0800878
chuntsen9d675c62017-06-20 14:35:30 +0800879 def handle_signal(self, signum, unused_frame):
880 names = [signame for signame in dir(signal) if signame.startswith('SIG') and
881 getattr(signal, signame) == signum]
882 signal_name = ', '.join(names) if names else 'UNKNOWN'
883 logging.error('Received signal %s(%d)', signal_name, signum)
Peter Ammon1e1ec572014-06-26 17:56:32 -0700884 self.run_enqueue(None)
Jon Salz77c151e2012-08-28 07:20:37 +0800885 raise KeyboardInterrupt()
886
Jon Salz128b0932013-07-03 16:55:26 +0800887 def GetTestList(self, test_list_id):
888 """Returns the test list with the given ID.
889
890 Raises:
891 TestListError: The test list ID is not valid.
892 """
893 try:
894 return self.test_lists[test_list_id]
895 except KeyError:
896 raise test_lists.TestListError(
897 '%r is not a valid test list ID (available IDs are [%s])' % (
898 test_list_id, ', '.join(sorted(self.test_lists.keys()))))
899
Chih-Yu Huang1725d622017-03-24 16:08:35 +0800900 def _RecordStartError(self, error_message):
901 """Appends the startup error message into the shared data."""
902 KEY = 'startup_error'
903 data = self.state_instance.get_shared_data(KEY, optional=True)
904 new_data = '%s\n\n%s' % (data, error_message) if data else error_message
905 self.state_instance.set_shared_data(KEY, new_data)
906
Jon Salz128b0932013-07-03 16:55:26 +0800907 def InitTestLists(self):
Joel Kitching50a63ea2016-02-22 13:15:09 +0800908 """Reads in all test lists and sets the active test list.
909
910 Returns:
911 True if the active test list could be set, False if failed.
912 """
913 startup_errors = []
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +0800914
915 self.test_lists, failed_files = self.test_list_manager.BuildAllTestLists()
916
Jon Salzd7550792013-07-12 05:49:27 +0800917 logging.info('Loaded test lists: [%s]',
918 test_lists.DescribeTestLists(self.test_lists))
Jon Salz128b0932013-07-03 16:55:26 +0800919
Joel Kitching50a63ea2016-02-22 13:15:09 +0800920 # Check for any syntax errors in test list files.
921 if failed_files:
922 logging.info('Failed test list files: [%s]',
923 ' '.join(failed_files.keys()))
924 for f, exc_info in failed_files.iteritems():
925 logging.error('Error in test list file: %s', f,
926 exc_info=exc_info)
927
928 # Limit the stack trace to the very last entry.
929 exc_type, exc_value, exc_traceback = exc_info
930 while exc_traceback and exc_traceback.tb_next:
931 exc_traceback = exc_traceback.tb_next
932
933 exc_string = ''.join(
934 traceback.format_exception(
935 exc_type, exc_value, exc_traceback)).rstrip()
936 startup_errors.append('Error in test list file (%s):\n%s'
937 % (f, exc_string))
938
Jon Salz128b0932013-07-03 16:55:26 +0800939 if not self.options.test_list:
940 self.options.test_list = test_lists.GetActiveTestListId()
941
Joel Kitching50a63ea2016-02-22 13:15:09 +0800942 # Check for a non-existent test list ID.
943 try:
Wei-Han Chen84fee7c2016-08-26 21:56:25 +0800944 self.test_list = self.GetTestList(self.options.test_list)
Joel Kitching50a63ea2016-02-22 13:15:09 +0800945 logging.info('Active test list: %s', self.test_list.test_list_id)
946 except test_lists.TestListError as e:
947 logging.exception('Invalid active test list: %s',
948 self.options.test_list)
949 startup_errors.append(e.message)
Jon Salz128b0932013-07-03 16:55:26 +0800950
Joel Kitching50a63ea2016-02-22 13:15:09 +0800951 # We may have failed loading the active test list.
952 if self.test_list:
Joel Kitching50a63ea2016-02-22 13:15:09 +0800953 self.test_list.state_instance = self.state_instance
Jon Salz128b0932013-07-03 16:55:26 +0800954
Joel Kitching50a63ea2016-02-22 13:15:09 +0800955 # Show all startup errors.
956 if startup_errors:
Chih-Yu Huang1725d622017-03-24 16:08:35 +0800957 self._RecordStartError('\n\n'.join(startup_errors))
Joel Kitching50a63ea2016-02-22 13:15:09 +0800958
959 # Only return False if failed to load the active test list.
960 return bool(self.test_list)
Jon Salz128b0932013-07-03 16:55:26 +0800961
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +0800962 def init_hooks(self):
963 """Initializes hooks.
964
965 Must run after self.test_list ready.
966 """
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +0800967 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
968 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
969 assert isinstance(self.hooks, factory.Hooks), (
Ricky Liang45c73e72015-01-15 15:00:30 +0800970 'hooks should be of type Hooks but is %r' % type(self.hooks))
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +0800971 self.hooks.test_list = self.test_list
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +0800972 self.hooks.OnCreatedTestList()
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +0800973
Vic Yanga3cecf82014-12-26 00:44:21 -0800974 def init_ui(self):
975 """Initialize UI."""
976 self._ui_initialized = True
977 if self.options.ui == 'chrome':
Vic Yanga3cecf82014-12-26 00:44:21 -0800978 logging.info('Waiting for a web socket connection')
979 self.web_socket_manager.wait()
980
Jon Salz0697cbf2012-07-04 15:14:04 +0800981 def init(self, args=None, env=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800982 """Initializes Goofy.
Jon Salz0697cbf2012-07-04 15:14:04 +0800983
984 Args:
985 args: A list of command-line arguments. Uses sys.argv if
986 args is None.
987 env: An Environment instance to use (or None to choose
988 FakeChrootEnvironment or DUTEnvironment as appropriate).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800989 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800990 parser = OptionParser()
991 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +0800992 action='store_true',
993 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +0800994 parser.add_option('--print_test_list', dest='print_test_list',
Wei-Han Chen84fee7c2016-08-26 21:56:25 +0800995 metavar='TEST_LIST_ID',
996 help='Print the content of TEST_LIST_ID and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +0800997 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +0800998 action='store_true',
999 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001000 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz7b5482e2014-08-04 17:48:41 +08001001 choices=['none', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001002 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001003 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001004 parser.add_option('--test_list', dest='test_list',
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001005 metavar='TEST_LIST_ID',
1006 help='Use test list whose id is TEST_LIST_ID')
Jon Salzc79a9982012-08-30 04:42:01 +08001007 parser.add_option('--dummy_shopfloor', action='store_true',
1008 help='Use a dummy shopfloor server')
Ricky Liang6fe218c2013-12-27 15:17:17 +08001009 parser.add_option('--automation-mode',
1010 choices=[m.lower() for m in AutomationMode],
Ricky Liang45c73e72015-01-15 15:00:30 +08001011 default='none', help='Factory test automation mode.')
Ricky Liang117484a2014-04-14 11:14:41 +08001012 parser.add_option('--no-auto-run-on-start', dest='auto_run_on_start',
1013 action='store_false', default=True,
1014 help=('do not automatically run the test list on goofy '
1015 'start; this is only valid when factory test '
1016 'automation is enabled'))
Chun-Ta Lina8dd3172014-11-26 16:15:13 +08001017 parser.add_option('--handshake_timeout', dest='handshake_timeout',
1018 type='float', default=0.3,
1019 help=('RPC timeout when doing handshake between device '
1020 'and presenter.'))
Vic Yang7d693c42014-09-14 09:52:39 +08001021 parser.add_option('--standalone', dest='standalone',
1022 action='store_true', default=False,
1023 help=('Assume the presenter is running on the same '
1024 'machines.'))
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001025 parser.add_option('--monolithic', dest='monolithic',
1026 action='store_true', default=False,
1027 help='Run in monolithic mode (without presenter)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001028 (self.options, self.args) = parser.parse_args(args)
1029
Joel Kitching261e0422017-03-30 16:52:01 -07001030 signal.signal(signal.SIGINT, self.handle_signal)
1031 signal.signal(signal.SIGTERM, self.handle_signal)
Hung-Te Lina846f602014-07-04 20:32:22 +08001032 # TODO(hungte) SIGTERM does not work properly without Telemetry and should
1033 # be fixed.
Hung-Te Lina846f602014-07-04 20:32:22 +08001034
Jon Salz46b89562012-07-05 11:49:22 +08001035 # Make sure factory directories exist.
Peter Shihb4e49352017-05-25 17:35:11 +08001036 for path in [
1037 paths.DATA_LOG_DIR, paths.DATA_STATE_DIR, paths.DATA_TESTS_DIR]:
1038 file_utils.TryMakeDirs(path)
Jon Salz46b89562012-07-05 11:49:22 +08001039
Peter Shihfdf17682017-05-26 11:38:39 +08001040 global _inited_logging # pylint: disable=global-statement
Jon Salz0697cbf2012-07-04 15:14:04 +08001041 if not _inited_logging:
1042 factory.init_logging('goofy', verbose=self.options.verbose)
1043 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001044
Wei-Han Chen78f35f62017-03-06 20:11:20 +08001045 try:
1046 goofy_default_options = config_utils.LoadConfig(validate_schema=False)
1047 for key, value in goofy_default_options.iteritems():
1048 if getattr(self.options, key, None) is None:
1049 logging.info('self.options.%s = %r', key, value)
1050 setattr(self.options, key, value)
1051 except Exception:
1052 logging.exception('failed to load goofy overriding options')
1053
Jon Salz0f996602012-10-03 15:26:48 +08001054 if self.options.print_test_list:
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +08001055 all_test_lists, unused_errors = self.test_list_manager.BuildAllTestLists()
1056 test_list = (
1057 all_test_lists[self.options.print_test_list].ToFactoryTestList())
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001058 print(test_list.__repr__(recursive=True))
Jon Salz0f996602012-10-03 15:26:48 +08001059 sys.exit(0)
1060
Jon Salzee85d522012-07-17 14:34:46 +08001061 event_log.IncrementBootSequence()
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001062 testlog_goofy.IncrementInitCount()
1063
Jon Salzd15bbcf2013-05-21 17:33:57 +08001064 # Don't defer logging the initial event, so we can make sure
1065 # that device_id, reimage_id, etc. are all set up.
1066 self.event_log = EventLog('goofy', defer=False)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001067 self.testlog = testlog.Testlog(
Peter Shihb4e49352017-05-25 17:35:11 +08001068 log_root=paths.DATA_LOG_DIR, uuid=self.uuid,
Joel Kitchingfa847512017-03-31 02:26:14 +08001069 stationDeviceId=testlog_goofy.GetDeviceID(),
1070 stationInstallationId=testlog_goofy.GetInstallationID())
Jon Salz0697cbf2012-07-04 15:14:04 +08001071
Jon Salz0697cbf2012-07-04 15:14:04 +08001072 if env:
1073 self.env = env
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +08001074 elif sys_utils.InChroot():
Jon Salz0697cbf2012-07-04 15:14:04 +08001075 self.env = test_environment.FakeChrootEnvironment()
Hung-Te Lina846f602014-07-04 20:32:22 +08001076 elif self.options.ui == 'chrome':
Ricky Liang09d66d82014-09-25 11:20:54 +08001077 self.env = test_environment.DUTEnvironment()
Jon Salz0697cbf2012-07-04 15:14:04 +08001078 self.env.goofy = self
1079
1080 if self.options.restart:
1081 state.clear_state()
1082
Jon Salz0697cbf2012-07-04 15:14:04 +08001083 logging.info('Started')
1084
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001085 if not self.options.monolithic:
Hung-Te Lin7bd55312014-12-30 16:43:36 +08001086 self.link_manager = PresenterLinkManager(
1087 check_interval=1,
1088 handshake_timeout=self.options.handshake_timeout,
1089 standalone=self.options.standalone)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001090
Earl Ouacbe99c2017-02-21 16:04:19 +08001091 self.start_goofy_server()
1092 self.init_state_instance()
Peter Shih80e78b42017-03-10 17:00:56 +08001093 self.init_i18n()
Jon Salz0697cbf2012-07-04 15:14:04 +08001094 self.last_shutdown_time = (
Ricky Liang45c73e72015-01-15 15:00:30 +08001095 self.state_instance.get_shared_data('shutdown_time', optional=True))
Jon Salz0697cbf2012-07-04 15:14:04 +08001096 self.state_instance.del_shared_data('shutdown_time', optional=True)
Jon Salzb19ea072013-02-07 16:35:00 +08001097 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001098
Ricky Liang6fe218c2013-12-27 15:17:17 +08001099 self.options.automation_mode = ParseAutomationMode(
1100 self.options.automation_mode)
1101 self.state_instance.set_shared_data('automation_mode',
1102 self.options.automation_mode)
1103 self.state_instance.set_shared_data(
1104 'automation_mode_prompt',
1105 AutomationModePrompt[self.options.automation_mode])
1106
Joel Kitching50a63ea2016-02-22 13:15:09 +08001107 success = False
1108 exc_info = None
Jon Salz128b0932013-07-03 16:55:26 +08001109 try:
Joel Kitching50a63ea2016-02-22 13:15:09 +08001110 success = self.InitTestLists()
Hung-Te Linc8174b52017-06-02 11:11:45 +08001111 except Exception:
Joel Kitching50a63ea2016-02-22 13:15:09 +08001112 exc_info = sys.exc_info()
1113
1114 if not success:
1115 if exc_info:
1116 logging.exception('Unable to initialize test lists')
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001117 self._RecordStartError(
1118 'Unable to initialize test lists\n%s' % traceback.format_exc())
Jon Salzb19ea072013-02-07 16:35:00 +08001119 if self.options.ui == 'chrome':
1120 # Create an empty test list with default options so that the rest of
1121 # startup can proceed.
Peter Shihd4ad0c92017-08-14 16:21:54 +08001122 self.test_list = manager.LegacyTestList(factory.FactoryTestList(
1123 [], self.state_instance, factory.Options()))
Jon Salzb19ea072013-02-07 16:35:00 +08001124 else:
1125 # Bail with an error; no point in starting up.
1126 sys.exit('No valid test list; exiting.')
1127
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001128 self.init_hooks()
1129
Jon Salz822838b2013-03-25 17:32:33 +08001130 if self.test_list.options.clear_state_on_start:
1131 self.state_instance.clear_test_state()
1132
Jon Salz670ce062014-05-16 15:53:50 +08001133 # If the phase is invalid, this will raise a ValueError.
1134 phase.SetPersistentPhase(self.test_list.options.phase)
1135
Peter Shih3b0bb9f2017-03-21 16:23:32 +08001136 if not self.state_instance.has_shared_data('ui_locale'):
Hung-Te Lin134403c2017-08-23 17:30:17 +08001137 ui_locale = self.test_list.options.ui_locale
Peter Shih3b0bb9f2017-03-21 16:23:32 +08001138 self.state_instance.set_shared_data('ui_locale', ui_locale)
Jon Salz0697cbf2012-07-04 15:14:04 +08001139 self.state_instance.set_shared_data(
Ricky Liang45c73e72015-01-15 15:00:30 +08001140 'test_list_options',
Peter Shih90425db2017-08-02 15:53:48 +08001141 self.test_list.options.ToDict())
Jon Salz0697cbf2012-07-04 15:14:04 +08001142 self.state_instance.test_list = self.test_list
1143
Jon Salz23926422012-09-01 03:38:13 +08001144 if self.options.dummy_shopfloor:
Ricky Liang45c73e72015-01-15 15:00:30 +08001145 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1146 'http://%s:%d/' %
Joel Kitchingb85ed7f2014-10-08 18:24:39 +08001147 (net_utils.LOCALHOST, shopfloor.DEFAULT_SERVER_PORT))
Hung-Te Lin4e6357c2016-01-08 14:32:00 +08001148 self.dummy_shopfloor = process_utils.Spawn(
Peter Shihad166772017-05-31 11:36:17 +08001149 [os.path.join(paths.FACTORY_DIR, 'bin', 'shopfloor_server'),
Jon Salz23926422012-09-01 03:38:13 +08001150 '--dummy'])
1151 elif self.test_list.options.shopfloor_server_url:
1152 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001153 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001154
Jon Salz0697cbf2012-07-04 15:14:04 +08001155 self.init_states()
1156 self.start_event_server()
Hung-Te Lincc41d2a2014-10-29 13:35:20 +08001157
Earl Oua3bca122016-10-21 16:00:30 +08001158 # Load and run Goofy plugins.
1159 self.plugin_controller = plugin_controller.PluginController(
1160 self.test_list.options.plugin_config_name, self)
1161 self.plugin_controller.StartAllPlugins()
1162
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001163 # TODO(akahuang): Move this part into a pytest.
1164 # Prepare DUT link after the plugins start running, because the link might
1165 # need the network connection.
1166 if success:
1167 try:
1168 if self.test_list.options.dut_options:
1169 logging.info('dut_options set by %s: %r', self.test_list.test_list_id,
1170 self.test_list.options.dut_options)
1171 device_utils.PrepareDUTLink(**self.test_list.options.dut_options)
1172 except Exception:
1173 logging.exception('Unable to prepare DUT link.')
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001174 self._RecordStartError(
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001175 'Unable to prepare DUT link.\n%s' % traceback.format_exc())
1176
Jon Salz0697cbf2012-07-04 15:14:04 +08001177 # Note that we create a log watcher even if
1178 # sync_event_log_period_secs isn't set (no background
1179 # syncing), since we may use it to flush event logs as well.
1180 self.log_watcher = EventLogWatcher(
Ricky Liang45c73e72015-01-15 15:00:30 +08001181 self.test_list.options.sync_event_log_period_secs,
1182 event_log_db_file=None,
1183 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001184 if self.test_list.options.sync_event_log_period_secs:
1185 self.log_watcher.StartWatchThread()
1186
Shen-En Shihf4ad32f2017-07-31 15:56:39 +08001187 self.event_client.post_event(
1188 Event(Event.Type.UPDATE_SYSTEM_INFO))
Jon Salz0697cbf2012-07-04 15:14:04 +08001189
1190 os.environ['CROS_FACTORY'] = '1'
1191 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1192
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001193 # Should not move earlier.
1194 self.hooks.OnStartup()
1195
Ricky Liang36512a32014-07-25 11:47:04 +08001196 # Only after this point the Goofy backend is ready for UI connection.
1197 self.ready_for_ui_connection = True
1198
Jon Salz0697cbf2012-07-04 15:14:04 +08001199 def state_change_callback(test, test_state):
1200 self.event_client.post_event(
Ricky Liang4bff3e32014-02-20 18:46:11 +08001201 Event(Event.Type.STATE_CHANGE, path=test.path, state=test_state))
Jon Salz0697cbf2012-07-04 15:14:04 +08001202 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001203
Vic Yange2c76a82014-10-30 12:48:19 -07001204 self.pytest_prespawner = prespawner.PytestPrespawner()
1205 self.pytest_prespawner.start()
Jon Salza6711d72012-07-18 14:33:03 +08001206
Ricky Liang48e47f92014-02-26 19:31:51 +08001207 tests_after_shutdown = self.state_instance.get_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001208 TESTS_AFTER_SHUTDOWN, optional=True)
Jon Salz5c344f62012-07-13 14:31:16 +08001209 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001210
Jon Salz5c344f62012-07-13 14:31:16 +08001211 if not force_auto_run and tests_after_shutdown is not None:
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001212 logging.info('Resuming tests after shutdown: %r', tests_after_shutdown)
1213 self.test_list_iterator = tests_after_shutdown
Wei-Han Chenbcac7252017-04-21 19:46:51 +08001214 self.test_list_iterator.SetTestList(self.test_list)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001215 self.run_enqueue(self.run_next_test)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001216 elif force_auto_run or self.test_list.options.auto_run_on_start:
1217 # If automation mode is enabled, allow suppress auto_run_on_start.
1218 if (self.options.automation_mode == 'NONE' or
1219 self.options.auto_run_on_start):
1220 status_filter = [TestState.UNTESTED]
1221 if self.test_list.options.retry_failed_on_start:
1222 status_filter.append(TestState.FAILED)
1223 self.run_enqueue(lambda: self.run_tests(self.test_list, status_filter))
1224 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, None)
Ricky Liang4bff3e32014-02-20 18:46:11 +08001225 self.restore_active_run_state()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001226
Hung-Te Lin410f70a2015-12-15 14:53:42 +08001227 self.dut.hooks.OnTestStart()
Vic Yang08505c72015-01-06 17:01:53 -08001228
Dean Liao592e4d52013-01-10 20:06:39 +08001229 self.may_disable_cros_shortcut_keys()
1230
1231 def may_disable_cros_shortcut_keys(self):
1232 test_options = self.test_list.options
1233 if test_options.disable_cros_shortcut_keys:
1234 logging.info('Filter ChromeOS shortcut keys.')
1235 self.key_filter = KeyFilter(
1236 unmap_caps_lock=test_options.disable_caps_lock,
1237 caps_lock_keycode=test_options.caps_lock_keycode)
1238 self.key_filter.Start()
1239
Peter Ammon1e1ec572014-06-26 17:56:32 -07001240 def perform_periodic_tasks(self):
1241 """Override of base method to perform periodic work.
Vic Yang4953fc12012-07-26 16:19:53 +08001242
Peter Ammon1e1ec572014-06-26 17:56:32 -07001243 This method must not raise exceptions.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001244 """
Peter Ammon1e1ec572014-06-26 17:56:32 -07001245 super(Goofy, self).perform_periodic_tasks()
Jon Salzb22d1172012-08-06 10:38:57 +08001246
Earl Oua3bca122016-10-21 16:00:30 +08001247 self.check_plugins()
cychiang21886742012-07-05 15:16:32 +08001248 self.check_for_updates()
Jon Salz57717ca2012-04-04 16:47:25 +08001249
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001250 def handle_event_logs(self, chunks, periodic=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001251 """Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001252
Jon Salz0697cbf2012-07-04 15:14:04 +08001253 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001254
1255 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001256 chunks: A list of Chunk objects.
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001257 periodic: This event log handling is periodic. Error messages
1258 will only be shown for the first time.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001259 """
Vic Yang93027612013-05-06 02:42:49 +08001260 first_exception = None
1261 exception_count = 0
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001262 # Suppress error messages for periodic event syncing except for the
1263 # first time. If event syncing is not periodic, always show the error
1264 # messages.
1265 quiet = self._suppress_event_log_error_messages if periodic else False
Vic Yang93027612013-05-06 02:42:49 +08001266
Jon Salzd15bbcf2013-05-21 17:33:57 +08001267 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001268 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001269 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001270 start_time = time.time()
1271 shopfloor_client = shopfloor.get_instance(
Ricky Liang45c73e72015-01-15 15:00:30 +08001272 detect=True,
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001273 timeout=self.test_list.options.shopfloor_timeout_secs,
1274 quiet=quiet)
Ricky Liang45c73e72015-01-15 15:00:30 +08001275 shopfloor_client.UploadEvent(chunk.log_name + '.' +
Jon Salzd15bbcf2013-05-21 17:33:57 +08001276 event_log.GetReimageId(),
1277 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001278 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +08001279 'Successfully synced %s in %.03f s',
1280 description, time.time() - start_time)
Hung-Te Linc8174b52017-06-02 11:11:45 +08001281 except Exception:
Hung-Te Linf707b242016-01-08 23:11:42 +08001282 first_exception = (first_exception or
1283 (chunk.log_name + ': ' +
1284 debug_utils.FormatExceptionOnly()))
Vic Yang93027612013-05-06 02:42:49 +08001285 exception_count += 1
1286
1287 if exception_count:
1288 if exception_count == 1:
1289 msg = 'Log upload failed: %s' % first_exception
1290 else:
1291 msg = '%d log upload failed; first is: %s' % (
1292 exception_count, first_exception)
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001293 # For periodic event log syncing, only show the first error messages.
1294 if periodic:
1295 if not self._suppress_event_log_error_messages:
1296 self._suppress_event_log_error_messages = True
1297 logging.warning('Suppress periodic shopfloor error messages for '
1298 'event log syncing after the first one.')
1299 raise Exception(msg)
1300 # For event log syncing by request, show the error messages.
1301 else:
1302 raise Exception(msg)
Vic Yang93027612013-05-06 02:42:49 +08001303
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001304 def run_tests_with_status(self, statuses_to_run, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001305 """Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001306
Jon Salz0697cbf2012-07-04 15:14:04 +08001307 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001308
Jon Salz0697cbf2012-07-04 15:14:04 +08001309 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001310 statuses_to_run: The particular status that caller wants to run.
Jon Salz0697cbf2012-07-04 15:14:04 +08001311 starting_at: If provided, only auto-runs tests beginning with
1312 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001313 root: The root of tests to run. If not provided, it will be
1314 the root of all tests.
1315 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001316 root = root or self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +08001317 self.abort_active_tests('Operator requested run/re-run of certain tests')
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001318 self.run_tests(root, status_filter=statuses_to_run)
Jon Salz0405ab52012-03-16 15:26:52 +08001319
Jon Salz0697cbf2012-07-04 15:14:04 +08001320 def restart_tests(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001321 """Restarts all tests."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001322 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001323
Jon Salz6dc031d2013-06-19 13:06:23 +08001324 self.abort_active_tests('Operator requested restart of certain tests')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +08001325 for test in root.Walk():
1326 test.UpdateState(status=TestState.UNTESTED)
Jon Salz0697cbf2012-07-04 15:14:04 +08001327 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001328
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001329 def auto_run(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001330 """"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001331
Jon Salz0697cbf2012-07-04 15:14:04 +08001332 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001333 root: If provided, the root of tests to run. If not provided, the root
1334 will be test_list (root of all tests).
1335 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001336 root = root or self.test_list
1337 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Ricky Liang45c73e72015-01-15 15:00:30 +08001338 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001339
Wei-Ning Huang38b75f02015-02-25 18:25:14 +08001340 def handle_key_filter_mode(self, event):
1341 if self.key_filter:
1342 if getattr(event, 'enabled'):
1343 self.key_filter.Start()
1344 else:
1345 self.key_filter.Stop()
1346
Jon Salz0697cbf2012-07-04 15:14:04 +08001347 def wait(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001348 """Waits for all pending invocations.
Jon Salz0697cbf2012-07-04 15:14:04 +08001349
1350 Useful for testing.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001351 """
Jon Salz1acc8742012-07-17 17:45:55 +08001352 while self.invocations:
1353 for k, v in self.invocations.iteritems():
1354 logging.info('Waiting for %s to complete...', k)
1355 v.thread.join()
1356 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001357
Claire Changd1961a22015-08-05 16:15:55 +08001358 def test_fail(self, test):
Hung-Te Lin410f70a2015-12-15 14:53:42 +08001359 self.dut.hooks.OnTestFailure(test)
Claire Changd1961a22015-08-05 16:15:55 +08001360 if self.link_manager:
1361 self.link_manager.UpdateStatus(False)
1362
Wei-Han Chenced08ef2016-11-08 09:40:02 +08001363
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001364if __name__ == '__main__':
Peter Ammona3d298c2014-09-23 10:11:02 -07001365 Goofy.run_main_and_exit()