blob: 972212d38a0f049594404df136a3db16f66450c9 [file] [log] [blame]
You-Cheng Syud5692942018-01-04 14:40:59 +08001#!/usr/bin/env python
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
Hung-Te Lina452d4d2017-10-25 17:46:14 +080013import Queue
Jon Salz77c151e2012-08-28 07:20:37 +080014import signal
Jon Salz0405ab52012-03-16 15:26:52 +080015import sys
Jon Salz0405ab52012-03-16 15:26:52 +080016import threading
17import time
18import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080019import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080020from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080021
Peter Shihfdf17682017-05-26 11:38:39 +080022import factory_common # pylint: disable=unused-import
Hung-Te Linb6287242016-05-18 14:39:05 +080023from cros.factory.device import device_utils
Vic Yangd80ea752014-09-24 16:07:14 +080024from cros.factory.goofy.goofy_rpc import GoofyRPC
Earl Ouacbe99c2017-02-21 16:04:19 +080025from cros.factory.goofy import goofy_server
Wei-Han Chen1a114682017-10-02 10:33:54 +080026from cros.factory.goofy import hooks
Vic Yangd80ea752014-09-24 16:07:14 +080027from cros.factory.goofy.invocation import TestInvocation
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
Earl Ouacbe99c2017-02-21 16:04:19 +080035from cros.factory.test.env import goofy_proxy
Hung-Te Linb6287242016-05-18 14:39:05 +080036from cros.factory.test.env import paths
Jon Salz83591782012-06-26 11:09:58 +080037from cros.factory.test.event import Event
Jon Salz83591782012-06-26 11:09:58 +080038from cros.factory.test.event import EventServer
Peter Shih67c7c0f2018-02-26 11:23:59 +080039from cros.factory.test.event import ThreadingEventClient
Hung-Te Linb6287242016-05-18 14:39:05 +080040from cros.factory.test import event_log
41from cros.factory.test.event_log import EventLog
Hung-Te Linb6287242016-05-18 14:39:05 +080042from cros.factory.test.event_log import GetBootSequence
Hung-Te Lin91492a12014-11-25 18:56:30 +080043from cros.factory.test.event_log_watcher import EventLogWatcher
Peter Shihf65db932017-03-22 17:06:34 +080044from cros.factory.test.i18n import test_ui as i18n_test_ui
Peter Shih80e78b42017-03-10 17:00:56 +080045from cros.factory.test.i18n import translation
Hung-Te Lin3f096842016-01-13 17:37:06 +080046from cros.factory.test.rules import phase
Hung-Te Lind151bf32017-08-30 11:05:47 +080047from cros.factory.test import server_proxy
Hung-Te Linda8eb992017-09-28 03:27:12 +080048from cros.factory.test import session
Earl Oua3bca122016-10-21 16:00:30 +080049from cros.factory.test import state
Wei-Han Chen3b030492017-10-12 11:43:27 +080050from cros.factory.test.state import TestState
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +080051from cros.factory.test.test_lists import manager
Wei-Han Chen03113912017-09-29 15:58:25 +080052from cros.factory.test.test_lists import test_object
chuntseneb33f9d2017-05-12 13:38:17 +080053from cros.factory.testlog import testlog
Wei-Han Chen78f35f62017-03-06 20:11:20 +080054from cros.factory.utils import config_utils
Hung-Te Linf707b242016-01-08 23:11:42 +080055from cros.factory.utils import debug_utils
Jon Salz2af235d2013-06-24 14:47:21 +080056from cros.factory.utils import file_utils
Hung-Te Lin8fc0d652017-09-21 13:05:44 +080057from cros.factory.utils import log_utils
Joel Kitchingb85ed7f2014-10-08 18:24:39 +080058from cros.factory.utils import net_utils
Peter Shihdb06b092018-04-16 16:55:38 +080059from cros.factory.utils import process_utils
Hung-Te Lin4e6357c2016-01-08 14:32:00 +080060from cros.factory.utils import sys_utils
Hung-Te Linf707b242016-01-08 23:11:42 +080061from cros.factory.utils import type_utils
Hung-Te Linf2f78f72012-02-08 19:27:11 +080062
Earl Ou6de96c02017-05-19 18:51:28 +080063from cros.factory.external import syslog
64
Hung-Te Linf2f78f72012-02-08 19:27:11 +080065
Hung-Te Linf2f78f72012-02-08 19:27:11 +080066HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Peter Shihb4e49352017-05-25 17:35:11 +080067CACHES_DIR = os.path.join(paths.DATA_STATE_DIR, 'caches')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080068
Jon Salz5c344f62012-07-13 14:31:16 +080069# Value for tests_after_shutdown that forces auto-run (e.g., after
70# a factory update, when the available set of tests might change).
71FORCE_AUTO_RUN = 'force_auto_run'
72
Wei-Han Chenc17b4112016-11-22 14:56:51 +080073# Key to load the test list iterator after shutdown test
74TESTS_AFTER_SHUTDOWN = 'tests_after_shutdown'
75
Hung-Te Linf707b242016-01-08 23:11:42 +080076Status = type_utils.Enum(['UNINITIALIZED', 'INITIALIZING', 'RUNNING',
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080077 'TERMINATING', 'TERMINATED'])
Jon Salzd7550792013-07-12 05:49:27 +080078
Hung-Te Lina452d4d2017-10-25 17:46:14 +080079RUN_QUEUE_TIMEOUT_SECS = 10
80
Peter Shih658d41d2018-04-16 15:42:00 +080081
Hung-Te Lina452d4d2017-10-25 17:46:14 +080082class Goofy(object):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +080083 """The main factory flow.
Jon Salz0697cbf2012-07-04 15:14:04 +080084
85 Note that all methods in this class must be invoked from the main
86 (event) thread. Other threads, such as callbacks and TestInvocation
87 methods, should instead post events on the run queue.
88
89 TODO: Unit tests. (chrome-os-partner:7409)
90
91 Properties:
Hung-Te Lina452d4d2017-10-25 17:46:14 +080092 run_queue: A queue of callbacks to invoke from the main thread.
93 exceptions: List of exceptions encountered in invocation threads.
94 last_idle: The most recent time of invoking the idle queue handler, or none.
Jon Salz0697cbf2012-07-04 15:14:04 +080095 uuid: A unique UUID for this invocation of Goofy.
96 state_instance: An instance of FactoryState.
97 state_server: The FactoryState XML/RPC server.
98 state_server_thread: A thread running state_server.
99 event_server: The EventServer socket server.
100 event_server_thread: A thread running event_server.
101 event_client: A client to the event server.
Earl Oua3bca122016-10-21 16:00:30 +0800102 plugin_controller: The PluginController object.
Peter Shih06d08212018-01-19 17:15:57 +0800103 invocations: A map from TestInvocation uuid to the corresponding
Jon Salz0697cbf2012-07-04 15:14:04 +0800104 TestInvocations objects representing active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800105 options: Command-line options.
106 args: Command-line args.
107 test_list: The test list.
Jon Salz128b0932013-07-03 16:55:26 +0800108 test_lists: All new-style test lists.
Ricky Liang4bff3e32014-02-20 18:46:11 +0800109 run_id: The identifier for latest test run.
110 scheduled_run_tests: The list of tests scheduled for latest test run.
Jon Salz0697cbf2012-07-04 15:14:04 +0800111 event_handlers: Map of Event.Type to the method used to handle that
112 event. If the method has an 'event' argument, the event is passed
113 to the handler.
Jon Salz416f9cc2013-05-10 18:32:50 +0800114 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salzd7550792013-07-12 05:49:27 +0800115 status: The current Goofy status (a member of the Status enum).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800116 """
Ricky Liang45c73e72015-01-15 15:00:30 +0800117
Jon Salz0697cbf2012-07-04 15:14:04 +0800118 def __init__(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800119 self.run_queue = Queue.Queue()
120 self.exceptions = []
121 self.last_idle = None
122
Jon Salz0697cbf2012-07-04 15:14:04 +0800123 self.uuid = str(uuid.uuid4())
124 self.state_instance = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800125 self.goofy_server = None
126 self.goofy_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800127 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800128 self.event_server = None
129 self.event_server_thread = None
130 self.event_client = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800131 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800132 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800133 self.testlog = None
Earl Oua3bca122016-10-21 16:00:30 +0800134 self.plugin_controller = None
Vic Yange2c76a82014-10-30 12:48:19 -0700135 self.pytest_prespawner = None
Vic Yanga3cecf82014-12-26 00:44:21 -0800136 self._ui_initialized = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800137 self.invocations = {}
Jon Salz0697cbf2012-07-04 15:14:04 +0800138 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800139 self.hooks = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800140
141 self.options = None
142 self.args = None
143 self.test_list = None
Jon Salz128b0932013-07-03 16:55:26 +0800144 self.test_lists = None
Ricky Liang4bff3e32014-02-20 18:46:11 +0800145 self.run_id = None
146 self.scheduled_run_tests = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800147 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800148 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800149 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800150 self.last_update_check = None
Cheng-Yi Chiang194d3c02015-03-16 14:37:15 +0800151 self._suppress_periodic_update_messages = False
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +0800152 self._suppress_event_log_error_messages = False
Earl Ouab979142016-10-25 16:48:06 +0800153 self.exclusive_resources = set()
Jon Salzd7550792013-07-12 05:49:27 +0800154 self.status = Status.UNINITIALIZED
Ricky Liang36512a32014-07-25 11:47:04 +0800155 self.ready_for_ui_connection = False
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800156 self.is_restart_requested = False
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800157 self.test_list_iterator = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800158
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +0800159 self.test_list_manager = manager.Manager()
160
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800161 # TODO(hungte) Support controlling remote DUT.
Hung-Te Linb6287242016-05-18 14:39:05 +0800162 self.dut = device_utils.CreateDUTInterface()
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800163
Peter Shih658d41d2018-04-16 15:42:00 +0800164 def TestOrRoot(event, parent_or_group=True):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800165 """Returns the test affected by a particular event.
Jon Salz85a39882012-07-05 16:45:04 +0800166
167 Args:
168 event: The event containing an optional 'path' attribute.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800169 parent_or_group: If True, returns the top-level parent for a test (the
Jon Salz85a39882012-07-05 16:45:04 +0800170 root node of the tests that need to be run together if the given test
171 path is to be run).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800172 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800173 try:
174 path = event.path
175 except AttributeError:
176 path = None
177
178 if path:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800179 test = self.test_list.LookupPath(path)
Jon Salz85a39882012-07-05 16:45:04 +0800180 if parent_or_group:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800181 test = test.GetTopLevelParentOrGroup()
Jon Salz85a39882012-07-05 16:45:04 +0800182 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800183 else:
Peter Shih999faf72017-07-07 11:32:42 +0800184 return self.test_list.ToFactoryTestList()
Jon Salz0697cbf2012-07-04 15:14:04 +0800185
186 self.event_handlers = {
Ricky Liang45c73e72015-01-15 15:00:30 +0800187 Event.Type.RESTART_TESTS:
Peter Shih658d41d2018-04-16 15:42:00 +0800188 lambda event: self.RestartTests(root=TestOrRoot(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800189 Event.Type.AUTO_RUN:
Peter Shih658d41d2018-04-16 15:42:00 +0800190 lambda event: self._AutoRun(root=TestOrRoot(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800191 Event.Type.RUN_TESTS_WITH_STATUS:
Peter Shih658d41d2018-04-16 15:42:00 +0800192 lambda event: self._RunTestsWithStatus(
Ricky Liang45c73e72015-01-15 15:00:30 +0800193 event.status,
Peter Shih658d41d2018-04-16 15:42:00 +0800194 root=TestOrRoot(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800195 Event.Type.UPDATE_SYSTEM_INFO:
Peter Shih658d41d2018-04-16 15:42:00 +0800196 lambda event: self._UpdateSystemInfo(),
Ricky Liang45c73e72015-01-15 15:00:30 +0800197 Event.Type.STOP:
Peter Shih658d41d2018-04-16 15:42:00 +0800198 lambda event: self.Stop(root=TestOrRoot(event, False),
Ricky Liang45c73e72015-01-15 15:00:30 +0800199 fail=getattr(event, 'fail', False),
200 reason=getattr(event, 'reason', None)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800201 Event.Type.CLEAR_STATE:
Peter Shih658d41d2018-04-16 15:42:00 +0800202 lambda event: self.ClearState(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800203 self.test_list.LookupPath(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800204 }
205
Jon Salz0697cbf2012-07-04 15:14:04 +0800206 self.web_socket_manager = None
207
Peter Shih658d41d2018-04-16 15:42:00 +0800208 def Destroy(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800209 """Performs any shutdown tasks."""
chuntsen9d675c62017-06-20 14:35:30 +0800210 # To avoid race condition when running shutdown test.
Peter Shih06d08212018-01-19 17:15:57 +0800211 for invoc in self.invocations.itervalues():
212 logging.info('Waiting for %s to complete...', invoc.test)
chuntsen9d675c62017-06-20 14:35:30 +0800213 invoc.thread.join(3) # Timeout in 3 seconds.
214
Jon Salzd7550792013-07-12 05:49:27 +0800215 self.status = Status.TERMINATING
Jon Salz0697cbf2012-07-04 15:14:04 +0800216 if self.chrome:
217 self.chrome.kill()
218 self.chrome = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800219 if self.web_socket_manager:
220 logging.info('Stopping web sockets')
221 self.web_socket_manager.close()
222 self.web_socket_manager = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800223 if self.goofy_server_thread:
224 logging.info('Stopping goofy server')
Peter Shih0a6ae8d2017-10-23 17:59:42 +0800225 net_utils.ShutdownTCPServer(self.goofy_server)
Earl Ouacbe99c2017-02-21 16:04:19 +0800226 self.goofy_server_thread.join()
227 self.goofy_server.server_close()
228 self.goofy_server_thread = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800229 if self.state_instance:
230 self.state_instance.close()
231 if self.event_server_thread:
232 logging.info('Stopping event server')
Peter Shihce9490e2017-05-11 14:32:12 +0800233 net_utils.ShutdownTCPServer(self.event_server)
Jon Salz0697cbf2012-07-04 15:14:04 +0800234 self.event_server_thread.join()
235 self.event_server.server_close()
236 self.event_server_thread = None
237 if self.log_watcher:
238 if self.log_watcher.IsThreadStarted():
239 self.log_watcher.StopWatchThread()
240 self.log_watcher = None
Vic Yange2c76a82014-10-30 12:48:19 -0700241 if self.pytest_prespawner:
242 logging.info('Stopping pytest prespawner')
243 self.pytest_prespawner.stop()
244 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800245 if self.event_client:
246 logging.info('Closing event client')
247 self.event_client.close()
248 self.event_client = None
249 if self.event_log:
250 self.event_log.Close()
251 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800252 if self.testlog:
253 self.testlog.Close()
254 self.testlog = None
Earl Oua3bca122016-10-21 16:00:30 +0800255 if self.plugin_controller:
256 self.plugin_controller.StopAndDestroyAllPlugins()
257 self.plugin_controller = None
Dean Liao592e4d52013-01-10 20:06:39 +0800258
Peter Shih658d41d2018-04-16 15:42:00 +0800259 self._CheckExceptions()
Jon Salz0697cbf2012-07-04 15:14:04 +0800260 logging.info('Done destroying Goofy')
Jon Salzd7550792013-07-12 05:49:27 +0800261 self.status = Status.TERMINATED
Jon Salz0697cbf2012-07-04 15:14:04 +0800262
Peter Shih658d41d2018-04-16 15:42:00 +0800263 def _InitGoofyServer(self):
Earl Ouacbe99c2017-02-21 16:04:19 +0800264 self.goofy_server = goofy_server.GoofyServer(
Shen-En Shihd5b96bf2017-08-09 17:47:21 +0800265 (goofy_proxy.DEFAULT_GOOFY_BIND, goofy_proxy.DEFAULT_GOOFY_PORT))
Earl Ouacbe99c2017-02-21 16:04:19 +0800266 self.goofy_server_thread = threading.Thread(
267 target=self.goofy_server.serve_forever,
268 name='GoofyServer')
Peter Shihdb06b092018-04-16 16:55:38 +0800269 self.goofy_server_thread.daemon = True
Earl Ouacbe99c2017-02-21 16:04:19 +0800270
Peter Shih658d41d2018-04-16 15:42:00 +0800271 def _InitStaticFiles(self):
Peter Shih7cc81b12017-08-24 13:04:46 +0800272 static_path = os.path.join(paths.FACTORY_PYTHON_PACKAGE_DIR, 'goofy/static')
Earl Ouacbe99c2017-02-21 16:04:19 +0800273 # Setup static file path
Peter Shih7cc81b12017-08-24 13:04:46 +0800274 self.goofy_server.RegisterPath('/', static_path)
Earl Ouacbe99c2017-02-21 16:04:19 +0800275
Peter Shih658d41d2018-04-16 15:42:00 +0800276 def _InitStateInstance(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800277 # Before starting state server, remount stateful partitions with
278 # no commit flag. The default commit time (commit=600) makes corruption
279 # too likely.
Hung-Te Lin1968d9c2016-01-08 22:55:46 +0800280 sys_utils.ResetCommitTime()
Earl Ouacbe99c2017-02-21 16:04:19 +0800281 self.state_instance = state.FactoryState()
282 self.goofy_server.AddRPCInstance(goofy_proxy.STATE_URL, self.state_instance)
Jon Salz2af235d2013-06-24 14:47:21 +0800283
Earl Ouacbe99c2017-02-21 16:04:19 +0800284 # Setup Goofy RPC.
285 # TODO(shunhsingou): separate goofy_rpc and state server instead of
286 # injecting goofy_rpc functions into state.
Jon Salz16d10542012-07-23 12:18:45 +0800287 self.goofy_rpc = GoofyRPC(self)
288 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800289
Peter Shih658d41d2018-04-16 15:42:00 +0800290 def _InitI18n(self):
Peter Shih80e78b42017-03-10 17:00:56 +0800291 js_data = 'var goofy_i18n_data = %s;' % translation.GetAllI18nDataJS()
Peter Shihce03c2e2017-03-21 17:36:10 +0800292 self.goofy_server.RegisterData('/js/goofy-translations.js',
293 'application/javascript', js_data)
Peter Shihf65db932017-03-22 17:06:34 +0800294 self.goofy_server.RegisterData('/css/i18n.css',
295 'text/css', i18n_test_ui.GetStyleSheet())
Peter Shih80e78b42017-03-10 17:00:56 +0800296
Peter Shih658d41d2018-04-16 15:42:00 +0800297 def _StartEventServer(self):
Jon Salz0697cbf2012-07-04 15:14:04 +0800298 self.event_server = EventServer()
299 logging.info('Starting factory event server')
Peter Shihdb06b092018-04-16 16:55:38 +0800300 self.event_server_thread = process_utils.StartDaemonThread(
Ricky Liang45c73e72015-01-15 15:00:30 +0800301 target=self.event_server.serve_forever,
Peter Shihfdf17682017-05-26 11:38:39 +0800302 name='EventServer')
Jon Salz0697cbf2012-07-04 15:14:04 +0800303
Shen-En Shihd7359cc2017-10-03 16:01:47 +0800304 self.event_client = ThreadingEventClient(
Peter Shih658d41d2018-04-16 15:42:00 +0800305 callback=lambda event: self.RunEnqueue(lambda: self.HandleEvent(event)))
Jon Salz0697cbf2012-07-04 15:14:04 +0800306
307 self.web_socket_manager = WebSocketManager(self.uuid)
Earl Ouacbe99c2017-02-21 16:04:19 +0800308 self.goofy_server.AddHTTPGetHandler(
309 '/event', self.web_socket_manager.handle_web_socket)
Jon Salz0697cbf2012-07-04 15:14:04 +0800310
Peter Shih658d41d2018-04-16 15:42:00 +0800311 def Shutdown(self, operation):
Ricky Liang48e47f92014-02-26 19:31:51 +0800312 """Starts shutdown procedure.
313
314 Args:
Vic (Chun-Ju) Yang05b0d952014-04-28 17:39:09 +0800315 operation: The shutdown operation (reboot, full_reboot, or halt).
Ricky Liang48e47f92014-02-26 19:31:51 +0800316 """
317 active_tests = []
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800318 for test in self.test_list.Walk():
319 if not test.IsLeaf():
Ricky Liang48e47f92014-02-26 19:31:51 +0800320 continue
321
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800322 test_state = test.GetState()
Ricky Liang48e47f92014-02-26 19:31:51 +0800323 if test_state.status == TestState.ACTIVE:
324 active_tests.append(test)
325
Ricky Liang48e47f92014-02-26 19:31:51 +0800326 if not (len(active_tests) == 1 and
Wei-Han Chen03113912017-09-29 15:58:25 +0800327 isinstance(active_tests[0], test_object.ShutdownStep)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800328 logging.error(
329 'Calling Goofy shutdown outside of the shutdown factory test')
330 return
331
332 logging.info('Start Goofy shutdown (%s)', operation)
333 # Save pending test list in the state server
334 self.state_instance.set_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800335 TESTS_AFTER_SHUTDOWN, self.test_list_iterator)
Ricky Liang48e47f92014-02-26 19:31:51 +0800336 # Save shutdown time
337 self.state_instance.set_shared_data('shutdown_time', time.time())
338
339 with self.env.lock:
340 self.event_log.Log('shutdown', operation=operation)
341 shutdown_result = self.env.shutdown(operation)
342 if shutdown_result:
343 # That's all, folks!
Peter Shih658d41d2018-04-16 15:42:00 +0800344 self.RunEnqueue(None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800345 else:
346 # Just pass (e.g., in the chroot).
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800347 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800348 # Send event with no fields to indicate that there is no
349 # longer a pending shutdown.
350 self.event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
351
Peter Shih658d41d2018-04-16 15:42:00 +0800352 def _HandleShutdownComplete(self, test):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800353 """Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800354
Ricky Liang6fe218c2013-12-27 15:17:17 +0800355 Args:
356 test: The ShutdownStep.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800357 """
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800358 test_state = test.UpdateState(increment_shutdown_count=1)
Jon Salz0697cbf2012-07-04 15:14:04 +0800359 logging.info('Detected shutdown (%d of %d)',
Ricky Liang48e47f92014-02-26 19:31:51 +0800360 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800361
Ricky Liang48e47f92014-02-26 19:31:51 +0800362 tests_after_shutdown = self.state_instance.get_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800363 TESTS_AFTER_SHUTDOWN, optional=True)
364
365 # Make this shutdown test the next test to run. This is to continue on
366 # post-shutdown verification in the shutdown step.
Ricky Liang48e47f92014-02-26 19:31:51 +0800367 if not tests_after_shutdown:
Wei-Han Chen29663c12017-06-27 10:28:54 +0800368 goofy_error = 'TESTS_AFTER_SHTUDOWN is not set'
Ricky Liang48e47f92014-02-26 19:31:51 +0800369 self.state_instance.set_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800370 TESTS_AFTER_SHUTDOWN, TestListIterator(test))
371 else:
Wei-Han Chen29663c12017-06-27 10:28:54 +0800372 goofy_error = tests_after_shutdown.RestartLastTest()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800373 self.state_instance.set_shared_data(
374 TESTS_AFTER_SHUTDOWN, tests_after_shutdown)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800375
Ricky Liang48e47f92014-02-26 19:31:51 +0800376 # Set 'post_shutdown' to inform shutdown test that a shutdown just occurred.
Ricky Liangb7eb8772014-09-15 18:05:22 +0800377 self.state_instance.set_shared_data(
Wei-Han Chen29663c12017-06-27 10:28:54 +0800378 state.KEY_POST_SHUTDOWN % test.path,
379 {'invocation': self.state_instance.get_test_state(test.path).invocation,
380 'goofy_error': goofy_error})
Jon Salz258a40c2012-04-19 12:34:01 +0800381
Peter Shih658d41d2018-04-16 15:42:00 +0800382 def _InitStates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800383 """Initializes all states on startup."""
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800384 for test in self.test_list.GetAllTests():
Jon Salz0697cbf2012-07-04 15:14:04 +0800385 # Make sure the state server knows about all the tests,
386 # defaulting to an untested state.
Peter Shih6e578272017-09-12 17:41:43 +0800387 test.UpdateState(update_parent=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800388
Earl Ouf76e55c2017-03-07 11:48:34 +0800389 is_unexpected_shutdown = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800390
Jon Salz0697cbf2012-07-04 15:14:04 +0800391 # Any 'active' tests should be marked as failed now.
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800392 for test in self.test_list.Walk():
393 if not test.IsLeaf():
Jon Salza6711d72012-07-18 14:33:03 +0800394 # Don't bother with parents; they will be updated when their
395 # children are updated.
396 continue
397
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800398 test_state = test.GetState()
Jon Salz0697cbf2012-07-04 15:14:04 +0800399 if test_state.status != TestState.ACTIVE:
400 continue
Wei-Han Chen03113912017-09-29 15:58:25 +0800401 if isinstance(test, test_object.ShutdownStep):
Jon Salz0697cbf2012-07-04 15:14:04 +0800402 # Shutdown while the test was active - that's good.
Peter Shih658d41d2018-04-16 15:42:00 +0800403 self._HandleShutdownComplete(test)
Shen-En Shihef6c5ae2017-12-21 14:26:47 +0800404 elif test.allow_reboot:
405 is_unexpected_shutdown = True
406 test.UpdateState(status=TestState.UNTESTED)
407 # For "allow_reboot" tests (such as "Start"), don't cancel
408 # pending tests, since reboot is expected.
409 session.console.info('Unexpected shutdown while test %s was running. '
410 'The test is marked as allow_reboot, continuing '
411 'on pending tests.',
412 test.path)
Jon Salz0697cbf2012-07-04 15:14:04 +0800413 else:
Peter Shih658d41d2018-04-16 15:42:00 +0800414 def GetUnexpectedShutdownTestRun():
chuntsen61522442017-08-11 14:40:29 +0800415 """Returns a StationTestRun for test not collected properly"""
416 station_test_run = testlog.StationTestRun()
chuntsendf856b32018-06-14 15:49:47 +0800417 station_test_run['status'] = testlog.StationTestRun.STATUS.FAIL
chuntsen921aa872018-06-15 16:28:44 +0800418 station_test_run['endTime'] = time.time()
chuntsen61522442017-08-11 14:40:29 +0800419 station_test_run.AddFailure(
420 'GoofyErrorMsg', 'Unexpected shutdown while test was running')
421 return station_test_run
422
Earl Ouf76e55c2017-03-07 11:48:34 +0800423 is_unexpected_shutdown = True
Jon Salz0697cbf2012-07-04 15:14:04 +0800424 error_msg = 'Unexpected shutdown while test was running'
425 self.event_log.Log('end_test',
Ricky Liang45c73e72015-01-15 15:00:30 +0800426 path=test.path,
427 status=TestState.FAILED,
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800428 invocation=test.GetState().invocation,
Earl Ouf76e55c2017-03-07 11:48:34 +0800429 error_msg=error_msg)
chuntsen61522442017-08-11 14:40:29 +0800430 testlog.CollectExpiredSessions(paths.DATA_LOG_DIR,
Peter Shih658d41d2018-04-16 15:42:00 +0800431 GetUnexpectedShutdownTestRun())
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800432 test.UpdateState(
Ricky Liang45c73e72015-01-15 15:00:30 +0800433 status=TestState.FAILED,
434 error_msg=error_msg)
Chun-Ta Lin87c2dac2015-05-02 01:35:01 -0700435 # Trigger the OnTestFailure callback.
Peter Shih658d41d2018-04-16 15:42:00 +0800436 self.RunEnqueue(lambda: self._TestFail(test))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800437
Shen-En Shihef6c5ae2017-12-21 14:26:47 +0800438 session.console.info('Unexpected shutdown while test %s '
439 'running; cancelling any pending tests',
440 test.path)
441 # cancel pending tests by replace the iterator with an empty one
442 self.state_instance.set_shared_data(
443 TESTS_AFTER_SHUTDOWN,
444 TestListIterator(None))
Jon Salz008f4ea2012-08-28 05:39:45 +0800445
Earl Ouf76e55c2017-03-07 11:48:34 +0800446 if is_unexpected_shutdown:
447 logging.warning("Unexpected shutdown.")
Wei-Han Chen8d7fbc42017-10-18 19:20:47 +0800448 self.hooks.OnUnexpectedReboot(self)
Earl Ouf76e55c2017-03-07 11:48:34 +0800449
Wei-Han Chen109d76f2017-08-08 18:50:35 +0800450 if self.test_list.options.read_device_data_from_vpd_on_init:
451 vpd_data = {}
452 for section in [device_data.NAME_RO, device_data.NAME_RW]:
453 try:
454 vpd_data[section] = self.dut.vpd.boot.GetPartition(section).GetAll()
455 except Exception:
Hung-Te Lin1be934e2018-03-29 17:59:49 +0800456 logging.warning('Failed to read %s_VPD, ignored...', section.upper())
Wei-Han Chen109d76f2017-08-08 18:50:35 +0800457 # using None for key_map will use default key_map
458 device_data.UpdateDeviceDataFromVPD(None, vpd_data)
459
Wei-Han Chen212d2af2017-08-03 18:12:23 +0800460 # state_instance is initialized, we can mark skipped and waived tests now.
461 self.test_list.SetSkippedAndWaivedTests()
462
Peter Shih658d41d2018-04-16 15:42:00 +0800463 def HandleEvent(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800464 """Handles an event from the event server."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800465 handler = self.event_handlers.get(event.type)
466 if handler:
467 handler(event)
468 else:
469 # We don't register handlers for all event types - just ignore
470 # this event.
471 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800472
Peter Shih658d41d2018-04-16 15:42:00 +0800473 def _CheckCriticalFactoryNote(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800474 """Returns True if the last factory note is critical."""
Vic Yangaabf9fd2013-04-09 18:56:13 +0800475 notes = self.state_instance.get_shared_data('factory_note', True)
476 return notes and notes[-1]['level'] == 'CRITICAL'
477
Peter Shih658d41d2018-04-16 15:42:00 +0800478 def ScheduleRestart(self):
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800479 """Schedules a restart event when any invocation is completed."""
480 self.is_restart_requested = True
481
Peter Shih658d41d2018-04-16 15:42:00 +0800482 def _InvocationCompletion(self):
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800483 """Callback when an invocation is completed."""
484 if self.is_restart_requested:
485 logging.info('Restart by scheduled event.')
486 self.is_restart_requested = False
Peter Shih658d41d2018-04-16 15:42:00 +0800487 self.RestartTests()
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800488 else:
Peter Shih658d41d2018-04-16 15:42:00 +0800489 self._RunNextTest()
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800490
Peter Shih658d41d2018-04-16 15:42:00 +0800491 def _RunNextTest(self):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800492 """Runs the next eligible test.
henryhsu4cc6b022014-04-22 17:12:42 +0800493
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800494 self.test_list_iterator (a TestListIterator object) will determine which
495 test should be run.
henryhsu4cc6b022014-04-22 17:12:42 +0800496 """
Peter Shih658d41d2018-04-16 15:42:00 +0800497 self.ReapCompletedTests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800498
499 if self.invocations:
500 # there are tests still running, we cannot start new tests
Vic Yangaabf9fd2013-04-09 18:56:13 +0800501 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800502
Peter Shih658d41d2018-04-16 15:42:00 +0800503 if self._CheckCriticalFactoryNote():
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800504 logging.info('has critical factory note, stop running')
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800505 self.test_list_iterator.Stop()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800506 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800507
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800508 while True:
509 try:
510 path = self.test_list_iterator.next()
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800511 test = self.test_list.LookupPath(path)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800512 except StopIteration:
513 logging.info('no next test, stop running')
Jon Salz0697cbf2012-07-04 15:14:04 +0800514 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800515
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800516 # check if we have run all required tests
Jon Salz304a75d2012-07-06 11:14:15 +0800517 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800518 for requirement in test.require_run:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800519 for i in requirement.test.Walk():
Jon Salza1412922012-07-23 16:04:17 +0800520 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800521 # We've hit this test itself; stop checking
522 break
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800523 if ((i.GetState().status == TestState.UNTESTED) or
Wei-Han Chen3a160172017-07-11 17:31:28 +0800524 (requirement.passed and
525 i.GetState().status not in [TestState.SKIPPED,
526 TestState.PASSED])):
Jon Salz304a75d2012-07-06 11:14:15 +0800527 # Found an untested test; move on to the next
528 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800529 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800530 break
531
532 if untested:
533 untested_paths = ', '.join(sorted([x.path for x in untested]))
534 if self.state_instance.get_shared_data('engineering_mode',
535 optional=True):
536 # In engineering mode, we'll let it go.
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800537 session.console.warn('In engineering mode; running '
Jon Salz304a75d2012-07-06 11:14:15 +0800538 '%s even though required tests '
539 '[%s] have not completed',
540 test.path, untested_paths)
541 else:
542 # Not in engineering mode; mark it failed.
543 error_msg = ('Required tests [%s] have not been run yet'
544 % untested_paths)
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800545 session.console.error('Not running %s: %s',
Jon Salz304a75d2012-07-06 11:14:15 +0800546 test.path, error_msg)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800547 test.UpdateState(status=TestState.FAILED,
548 error_msg=error_msg)
Jon Salz304a75d2012-07-06 11:14:15 +0800549 continue
550
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800551 # okay, let's run the test
Wei-Han Chen03113912017-09-29 15:58:25 +0800552 if (isinstance(test, test_object.ShutdownStep) and
Ricky Liangb7eb8772014-09-15 18:05:22 +0800553 self.state_instance.get_shared_data(
Wei-Han Chen29663c12017-06-27 10:28:54 +0800554 state.KEY_POST_SHUTDOWN % test.path, optional=True)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800555 # Invoking post shutdown method of shutdown test. We should retain the
556 # iterations_left and retries_left of the original test state.
557 test_state = self.state_instance.get_test_state(test.path)
Peter Shih658d41d2018-04-16 15:42:00 +0800558 self._RunTest(test, test_state.iterations_left, test_state.retries_left)
Ricky Liang48e47f92014-02-26 19:31:51 +0800559 else:
560 # Starts a new test run; reset iterations and retries.
Peter Shih658d41d2018-04-16 15:42:00 +0800561 self._RunTest(test, test.iterations, test.retries)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800562 return # to leave while
Jon Salz1acc8742012-07-17 17:45:55 +0800563
Peter Shih658d41d2018-04-16 15:42:00 +0800564 def _RunTest(self, test, iterations_left=None, retries_left=None,
565 set_layout=True):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800566 """Invokes the test.
567
568 The argument `test` should be either a leaf test (no subtests) or a parallel
569 test (all subtests should be run in parallel).
570 """
Peter Shih2c2bf262018-01-19 15:31:39 +0800571 if (self.options.goofy_ui and not self._ui_initialized and
572 not test.IsNoHost()):
Peter Shih658d41d2018-04-16 15:42:00 +0800573 self.InitUI()
Jon Salz1acc8742012-07-17 17:45:55 +0800574
Peter Shih13d2ced2017-09-25 16:25:09 +0800575 if set_layout:
576 self.event_client.post_event(
577 Event(
578 Event.Type.SET_TEST_UI_LAYOUT,
579 layout_type=test.layout_type,
580 layout_options=test.layout_options))
581
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800582 if test.IsLeaf():
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800583 invoc = TestInvocation(
Peter Shih658d41d2018-04-16 15:42:00 +0800584 self, test, on_completion=self._InvocationCompletion,
585 on_test_failure=lambda: self._TestFail(test))
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800586 new_state = test.UpdateState(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800587 status=TestState.ACTIVE, increment_count=1, error_msg='',
588 invocation=invoc.uuid, iterations_left=iterations_left,
Peter Shihf5c048d2017-09-01 17:57:51 +0800589 retries_left=retries_left)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800590 invoc.count = new_state.count
Peter Shih06d08212018-01-19 17:15:57 +0800591 self.invocations[invoc.uuid] = invoc
Peter Shihb2ecd552017-08-24 17:48:58 +0800592 # Send a INIT_TEST_UI event here, so the test UI are initialized in
593 # order, and the tab order would be same as test list order when there
594 # are parallel tests with UI.
595 self.event_client.post_event(
596 Event(
597 Event.Type.INIT_TEST_UI,
Peter Shihb2ecd552017-08-24 17:48:58 +0800598 test=test.path,
599 invocation=invoc.uuid))
Peter Shih658d41d2018-04-16 15:42:00 +0800600 self._CheckPlugins()
Peter Shih0d4f77a2018-01-19 16:15:49 +0800601 invoc.Start()
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800602 elif test.parallel:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800603 for subtest in test.subtests:
604 # TODO(stimim): what if the subtests *must* be run in parallel?
605 # for example, stressapptest and countdown test.
606
607 # Make sure we don't need to skip it:
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800608 if not self.test_list_iterator.CheckSkip(subtest):
Peter Shih658d41d2018-04-16 15:42:00 +0800609 self._RunTest(subtest, subtest.iterations, subtest.retries,
610 set_layout=False)
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800611 else:
612 # This should never happen, there must be something wrong.
613 # However, we can't raise an exception, otherwise goofy will be closed
614 logging.critical(
615 'Goofy should not get a non-leaf test that is not parallel: %r',
616 test)
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800617 session.console.critical(
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800618 'Goofy should not get a non-leaf test that is not parallel: %r',
619 test)
Jon Salz5f2a0672012-05-22 17:14:06 +0800620
Peter Shih658d41d2018-04-16 15:42:00 +0800621 def Run(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800622 """Runs Goofy."""
623 # Process events forever.
Peter Shih658d41d2018-04-16 15:42:00 +0800624 while self.RunOnce(True):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800625 pass
626
Peter Shih658d41d2018-04-16 15:42:00 +0800627 def RunEnqueue(self, val):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800628 """Enqueues an object on the event loop.
629
630 Generally this is a function. It may also be None to indicate that the
631 run queue should shut down.
632 """
633 self.run_queue.put(val)
634
Peter Shih658d41d2018-04-16 15:42:00 +0800635 def RunOnce(self, block=False):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800636 """Runs all items pending in the event loop.
637
638 Args:
639 block: If true, block until at least one event is processed.
640
641 Returns:
642 True to keep going or False to shut down.
643 """
644 events = type_utils.DrainQueue(self.run_queue)
645 while not events:
646 # Nothing on the run queue.
Peter Shih658d41d2018-04-16 15:42:00 +0800647 self._RunQueueIdle()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800648 if block:
649 # Block for at least one event...
650 try:
651 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
652 except Queue.Empty:
Peter Shih658d41d2018-04-16 15:42:00 +0800653 # Keep going (calling _RunQueueIdle() again at the top of
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800654 # the loop)
655 continue
656 # ...and grab anything else that showed up at the same
657 # time.
658 events.extend(type_utils.DrainQueue(self.run_queue))
659 else:
660 break
661
662 for event in events:
663 if not event:
664 # Shutdown request.
665 self.run_queue.task_done()
666 return False
667
668 try:
669 event()
670 except Exception:
671 logging.exception('Error in event loop')
Peter Shih658d41d2018-04-16 15:42:00 +0800672 self._RecordExceptions(
673 traceback.format_exception_only(*sys.exc_info()[:2]))
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800674 # But keep going
675 finally:
676 self.run_queue.task_done()
677 return True
678
Peter Shih658d41d2018-04-16 15:42:00 +0800679 def _RunQueueIdle(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800680 """Invoked when the run queue has no events.
681
682 This method must not raise exception.
683 """
684 now = time.time()
685 if (self.last_idle and
686 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
687 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
688 # 1) seconds.
689 return
690
691 self.last_idle = now
Peter Shih658d41d2018-04-16 15:42:00 +0800692 self._PerformPeriodicTasks()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800693
Peter Shih658d41d2018-04-16 15:42:00 +0800694 def _CheckExceptions(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800695 """Raises an error if any exceptions have occurred in
696 invocation threads.
697 """
698 if self.exceptions:
699 raise RuntimeError('Exception in invocation thread: %r' %
700 self.exceptions)
701
Peter Shih658d41d2018-04-16 15:42:00 +0800702 def _RecordExceptions(self, msg):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800703 """Records an exception in an invocation thread.
704
705 An exception with the given message will be rethrown when
706 Goofy is destroyed.
707 """
708 self.exceptions.append(msg)
709
710 @staticmethod
Peter Shih658d41d2018-04-16 15:42:00 +0800711 def DrainNondaemonThreads():
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800712 """Wait for all non-current non-daemon threads to exit.
713
714 This is performed by the Python runtime in an atexit handler,
715 but this implementation allows us to do more detailed logging, and
716 to support control-C for abrupt shutdown.
717 """
718 cur_thread = threading.current_thread()
719 all_threads_joined = False
720 while not all_threads_joined:
721 for thread in threading.enumerate():
722 if not thread.daemon and thread.is_alive() and thread is not cur_thread:
723 logging.info("Waiting for thread '%s'...", thread.name)
724 thread.join()
725 # We break rather than continue on because the thread list
726 # may have changed while we waited
727 break
728 else:
729 # No threads remain
730 all_threads_joined = True
731 return all_threads_joined
732
733 @staticmethod
Peter Shih658d41d2018-04-16 15:42:00 +0800734 def RunMainAndExit():
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800735 """Instantiate the receiver, run its main function, and exit when done.
736
737 This static method is the "entry point" for Goofy.
738 It instantiates the receiver and invokes its main function, while
739 handling exceptions. When main() finishes (normally or via an exception),
740 it exits the process.
741 """
742 try:
743 cls = Goofy
744 goofy = cls()
745 except Exception:
746 logging.info('Failed to instantiate %s, shutting down.', cls.__name__)
747 traceback.print_exc()
748 os._exit(1) # pylint: disable=protected-access
749 sys.exit(1)
750
751 try:
Peter Shih658d41d2018-04-16 15:42:00 +0800752 goofy.Main()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800753 except SystemExit:
754 # Propagate SystemExit without logging.
755 raise
756 except KeyboardInterrupt:
757 logging.info('Interrupted, shutting down...')
758 except Exception:
759 # Log the error before trying to shut down
760 logging.exception('Error in main loop')
761 raise
762 finally:
763 try:
764 # We drain threads manually, rather than letting Python do it,
765 # so that we can report to the user which threads are stuck
Peter Shih658d41d2018-04-16 15:42:00 +0800766 goofy.Destroy()
767 cls.DrainNondaemonThreads()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800768 except (KeyboardInterrupt, Exception):
769 # We got a keyboard interrupt while attempting to shut down.
770 # The user is waiting impatiently! This can happen if threads get stuck.
771 # We need to exit via os._exit, not sys.exit, because sys.exit() will
772 # run the main thread's atexit handler, which waits for all threads to
773 # exit, which is likely how we got stuck in the first place. However, we
774 # do want to capture all logs, so we shut down logging gracefully.
775 logging.info('Graceful shutdown interrupted, shutting down abruptly')
776 logging.shutdown()
777 os._exit(1) # pylint: disable=protected-access
778 # Normal exit path
779 sys.exit(0)
780
Peter Shih658d41d2018-04-16 15:42:00 +0800781 def _CheckPlugins(self):
Earl Oua3bca122016-10-21 16:00:30 +0800782 """Check plugins to be paused or resumed."""
783 exclusive_resources = set()
Peter Shih06d08212018-01-19 17:15:57 +0800784 for invoc in self.invocations.itervalues():
Earl Oua3bca122016-10-21 16:00:30 +0800785 exclusive_resources = exclusive_resources.union(
Peter Shih06d08212018-01-19 17:15:57 +0800786 invoc.test.GetExclusiveResources())
Earl Oua3bca122016-10-21 16:00:30 +0800787 self.plugin_controller.PauseAndResumePluginByResource(exclusive_resources)
788
Peter Shih658d41d2018-04-16 15:42:00 +0800789 def _CheckForUpdates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800790 """Schedules an asynchronous check for updates if necessary."""
cychiang21886742012-07-05 15:16:32 +0800791 if not self.test_list.options.update_period_secs:
792 # Not enabled.
793 return
794
795 now = time.time()
796 if self.last_update_check and (
797 now - self.last_update_check <
798 self.test_list.options.update_period_secs):
799 # Not yet time for another check.
800 return
801
802 self.last_update_check = now
803
Peter Shih658d41d2018-04-16 15:42:00 +0800804 def _HandleCheckForUpdate(reached_server, toolkit_version, needs_update):
Hung-Te Lind151bf32017-08-30 11:05:47 +0800805 if reached_server:
You-Cheng Syud4a24bf2017-08-21 17:56:48 +0800806 new_update_toolkit_version = toolkit_version if needs_update else None
807 if self.dut.info.update_toolkit_version != new_update_toolkit_version:
808 logging.info('Received new update TOOLKIT_VERSION: %s',
809 new_update_toolkit_version)
810 self.dut.info.Overrides('update_toolkit_version',
811 new_update_toolkit_version)
Peter Shih658d41d2018-04-16 15:42:00 +0800812 self.RunEnqueue(self._UpdateSystemInfo)
You-Cheng Syud4a24bf2017-08-21 17:56:48 +0800813 elif not self._suppress_periodic_update_messages:
814 logging.warning('Suppress error messages for periodic update checking '
815 'after the first one.')
816 self._suppress_periodic_update_messages = True
cychiang21886742012-07-05 15:16:32 +0800817
818 updater.CheckForUpdateAsync(
Peter Shih658d41d2018-04-16 15:42:00 +0800819 _HandleCheckForUpdate, None, self._suppress_periodic_update_messages)
cychiang21886742012-07-05 15:16:32 +0800820
Peter Shih658d41d2018-04-16 15:42:00 +0800821 def CancelPendingTests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800822 """Cancels any tests in the run queue."""
Peter Shih658d41d2018-04-16 15:42:00 +0800823 self._RunTests(None)
Jon Salza6711d72012-07-18 14:33:03 +0800824
Peter Shih658d41d2018-04-16 15:42:00 +0800825 def _RestoreActiveRunState(self):
Ricky Liang4bff3e32014-02-20 18:46:11 +0800826 """Restores active run id and the list of scheduled tests."""
827 self.run_id = self.state_instance.get_shared_data('run_id', optional=True)
828 self.scheduled_run_tests = self.state_instance.get_shared_data(
829 'scheduled_run_tests', optional=True)
830
Peter Shih658d41d2018-04-16 15:42:00 +0800831 def _SetActiveRunState(self):
Ricky Liang4bff3e32014-02-20 18:46:11 +0800832 """Sets active run id and the list of scheduled tests."""
833 self.run_id = str(uuid.uuid4())
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800834 # try our best to predict which tests will be run.
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800835 self.scheduled_run_tests = self.test_list_iterator.GetPendingTests()
Ricky Liang4bff3e32014-02-20 18:46:11 +0800836 self.state_instance.set_shared_data('run_id', self.run_id)
837 self.state_instance.set_shared_data('scheduled_run_tests',
838 self.scheduled_run_tests)
839
Peter Shih658d41d2018-04-16 15:42:00 +0800840 def _RunTests(self, subtree, status_filter=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800841 """Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800842
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800843 Run tests under a given subtree.
Jon Salzb1b39092012-05-03 02:05:09 +0800844
Ricky Liang6fe218c2013-12-27 15:17:17 +0800845 Args:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800846 subtree: root of subtree to run or None to run nothing.
Chih-Yu Huang85dc63c2015-08-12 15:21:28 +0800847 status_filter: List of available test states. Only run the tests which
848 states are in the list. Set to None if all test states are available.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800849 """
Hung-Te Lin793d6572017-09-04 18:17:56 +0800850 self.hooks.OnTestStart()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800851 self.test_list_iterator = TestListIterator(
852 subtree, status_filter, self.test_list)
853 if subtree is not None:
Peter Shih658d41d2018-04-16 15:42:00 +0800854 self._SetActiveRunState()
855 self._RunNextTest()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800856
Peter Shih658d41d2018-04-16 15:42:00 +0800857 def ReapCompletedTests(self):
Peter Shih0d4f77a2018-01-19 16:15:49 +0800858 """Removes completed tests from the set of active tests."""
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800859 test_completed = False
Peter Shih06d08212018-01-19 17:15:57 +0800860 # Since items are removed while iterating, make a copy using values()
861 # instead of itervalues().
862 for invoc in self.invocations.values():
863 test = invoc.test
864 if invoc.IsCompleted():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800865 test_completed = True
Peter Shih06d08212018-01-19 17:15:57 +0800866 new_state = test.UpdateState(**invoc.update_state_on_completion)
867 del self.invocations[invoc.uuid]
Jon Salz0697cbf2012-07-04 15:14:04 +0800868
Johny Lin62ed2a32015-05-13 11:57:12 +0800869 # Stop on failure if flag is true and there is no retry chances.
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800870 if (self.test_list.options.stop_on_failure and
Johny Lin62ed2a32015-05-13 11:57:12 +0800871 new_state.retries_left < 0 and
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800872 new_state.status == TestState.FAILED):
873 # Clean all the tests to cause goofy to stop.
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800874 session.console.info('Stop on failure triggered. Empty the queue.')
Peter Shih658d41d2018-04-16 15:42:00 +0800875 self.CancelPendingTests()
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800876
Jon Salz1acc8742012-07-17 17:45:55 +0800877 if new_state.iterations_left and new_state.status == TestState.PASSED:
878 # Play it again, Sam!
Peter Shih658d41d2018-04-16 15:42:00 +0800879 self._RunTest(test)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800880 # new_state.retries_left is obtained after update.
881 # For retries_left == 0, test can still be run for the last time.
882 elif (new_state.retries_left >= 0 and
883 new_state.status == TestState.FAILED):
884 # Still have to retry, Sam!
Peter Shih658d41d2018-04-16 15:42:00 +0800885 self._RunTest(test)
Jon Salz1acc8742012-07-17 17:45:55 +0800886
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800887 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800888 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800889
Peter Shih658d41d2018-04-16 15:42:00 +0800890 def _KillActiveTests(self, abort, root=None, reason=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800891 """Kills and waits for all active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800892
Jon Salz85a39882012-07-05 16:45:04 +0800893 Args:
894 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800895 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800896 root: If set, only kills tests with root as an ancestor.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800897 reason: If set, the abort reason.
898 """
Peter Shih658d41d2018-04-16 15:42:00 +0800899 self.ReapCompletedTests()
Peter Shih06d08212018-01-19 17:15:57 +0800900 # Since items are removed while iterating, make a copy using values()
901 # instead of itervalues().
902 for invoc in self.invocations.values():
903 test = invoc.test
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800904 if root and not test.HasAncestor(root):
Jon Salz85a39882012-07-05 16:45:04 +0800905 continue
906
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800907 session.console.info('Killing active test %s...', test.path)
Peter Shih0d4f77a2018-01-19 16:15:49 +0800908 invoc.AbortAndJoin(reason)
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800909 session.console.info('Killed %s', test.path)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800910 test.UpdateState(**invoc.update_state_on_completion)
Peter Shih06d08212018-01-19 17:15:57 +0800911 del self.invocations[invoc.uuid]
Jon Salz1acc8742012-07-17 17:45:55 +0800912
Jon Salz0697cbf2012-07-04 15:14:04 +0800913 if not abort:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800914 test.UpdateState(status=TestState.UNTESTED)
Peter Shih658d41d2018-04-16 15:42:00 +0800915 self.ReapCompletedTests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800916
Peter Shih658d41d2018-04-16 15:42:00 +0800917 def Stop(self, root=None, fail=False, reason=None):
918 self._KillActiveTests(fail, root, reason)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800919
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800920 self.test_list_iterator.Stop(root)
Peter Shih658d41d2018-04-16 15:42:00 +0800921 self._RunNextTest()
Jon Salz0697cbf2012-07-04 15:14:04 +0800922
Peter Shih658d41d2018-04-16 15:42:00 +0800923 def ClearState(self, root=None):
Jon Salzd7550792013-07-12 05:49:27 +0800924 if root is None:
925 root = self.test_list
Peter Shih658d41d2018-04-16 15:42:00 +0800926 self.Stop(root, reason='Clearing test state')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800927 for f in root.Walk():
928 if f.IsLeaf():
929 f.UpdateState(status=TestState.UNTESTED)
Jon Salz4712ac72013-02-07 17:12:05 +0800930
Peter Shih658d41d2018-04-16 15:42:00 +0800931 def _AbortActiveTests(self, reason=None):
932 self._KillActiveTests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +0800933
Peter Shih658d41d2018-04-16 15:42:00 +0800934 def Main(self):
Jon Salzeff94182013-06-19 15:06:28 +0800935 syslog.openlog('goofy')
936
Jon Salz0697cbf2012-07-04 15:14:04 +0800937 try:
Jon Salzd7550792013-07-12 05:49:27 +0800938 self.status = Status.INITIALIZING
Peter Shih658d41d2018-04-16 15:42:00 +0800939 self.Init()
Jon Salz0697cbf2012-07-04 15:14:04 +0800940 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800941 success=True)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800942 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800943 testlog.StationInit({
Hung-Te Linda8eb992017-09-28 03:27:12 +0800944 'stationDeviceId': session.GetDeviceID(),
945 'stationInstallationId': session.GetInstallationID(),
946 'count': session.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800947 'success': True}))
Hung-Te Linc8174b52017-06-02 11:11:45 +0800948 except Exception:
Joel Kitching9eb203a2016-04-21 15:36:30 +0800949 try:
950 if self.event_log:
Jon Salz0697cbf2012-07-04 15:14:04 +0800951 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800952 success=False,
953 trace=traceback.format_exc())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800954 if self.testlog:
955 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800956 testlog.StationInit({
Hung-Te Linda8eb992017-09-28 03:27:12 +0800957 'stationDeviceId': session.GetDeviceID(),
958 'stationInstallationId': session.GetInstallationID(),
959 'count': session.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800960 'success': False,
961 'failureMessage': traceback.format_exc()}))
Hung-Te Linc8174b52017-06-02 11:11:45 +0800962 except Exception:
Joel Kitching9eb203a2016-04-21 15:36:30 +0800963 pass
Jon Salz0697cbf2012-07-04 15:14:04 +0800964 raise
965
Jon Salzd7550792013-07-12 05:49:27 +0800966 self.status = Status.RUNNING
Jon Salzeff94182013-06-19 15:06:28 +0800967 syslog.syslog('Goofy (factory test harness) starting')
Chun-Ta Lin5d12b592015-06-30 00:54:23 -0700968 syslog.syslog('Boot sequence = %d' % GetBootSequence())
Hung-Te Linda8eb992017-09-28 03:27:12 +0800969 syslog.syslog('Goofy init count = %d' % session.GetInitCount())
Peter Shih658d41d2018-04-16 15:42:00 +0800970 self.Run()
Jon Salz0697cbf2012-07-04 15:14:04 +0800971
Peter Shih658d41d2018-04-16 15:42:00 +0800972 def _UpdateSystemInfo(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800973 """Updates system info."""
Shen-En Shihce8ffe02017-08-01 18:58:09 +0800974 logging.info('Received a notify to update system info.')
975 self.dut.info.Invalidate()
976
977 # Propagate this notify to goofy components
978 try:
979 status_monitor = plugin_controller.GetPluginRPCProxy(
980 'status_monitor.status_monitor')
981 status_monitor.UpdateDeviceInfo()
982 except Exception:
983 logging.debug('Failed to update status monitor plugin.')
Jon Salz0697cbf2012-07-04 15:14:04 +0800984
Peter Shih658d41d2018-04-16 15:42:00 +0800985 def SetForceAutoRun(self):
Wei-Han Chen8d7fbc42017-10-18 19:20:47 +0800986 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, FORCE_AUTO_RUN)
987
Peter Shih658d41d2018-04-16 15:42:00 +0800988 def UpdateFactory(self, auto_run_on_restart=False, post_update_hook=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800989 """Commences updating factory software.
Jon Salzeb42f0d2012-07-27 19:14:04 +0800990
991 Args:
992 auto_run_on_restart: Auto-run when the machine comes back up.
993 post_update_hook: Code to call after update but immediately before
994 restart.
995
996 Returns:
997 Never if the update was successful (we just reboot).
998 False if the update was unnecessary (no update available).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800999 """
Peter Shih658d41d2018-04-16 15:42:00 +08001000 self._KillActiveTests(False, reason='Factory software update')
1001 self.CancelPendingTests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001002
Peter Shih658d41d2018-04-16 15:42:00 +08001003 def PreUpdateHook():
Jon Salz5c344f62012-07-13 14:31:16 +08001004 if auto_run_on_restart:
Peter Shih658d41d2018-04-16 15:42:00 +08001005 self.SetForceAutoRun()
Jon Salz5c344f62012-07-13 14:31:16 +08001006 self.state_instance.close()
1007
Peter Shih658d41d2018-04-16 15:42:00 +08001008 if updater.TryUpdate(pre_update_hook=PreUpdateHook):
Jon Salzeb42f0d2012-07-27 19:14:04 +08001009 if post_update_hook:
1010 post_update_hook()
1011 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +08001012
Peter Shih658d41d2018-04-16 15:42:00 +08001013 def _HandleSignal(self, signum, unused_frame):
chuntsen9d675c62017-06-20 14:35:30 +08001014 names = [signame for signame in dir(signal) if signame.startswith('SIG') and
1015 getattr(signal, signame) == signum]
1016 signal_name = ', '.join(names) if names else 'UNKNOWN'
1017 logging.error('Received signal %s(%d)', signal_name, signum)
Peter Shih658d41d2018-04-16 15:42:00 +08001018 self.RunEnqueue(None)
Peter Shihbe962972018-02-23 12:46:41 +08001019 raise KeyboardInterrupt
Jon Salz77c151e2012-08-28 07:20:37 +08001020
Jon Salz128b0932013-07-03 16:55:26 +08001021 def GetTestList(self, test_list_id):
1022 """Returns the test list with the given ID.
1023
1024 Raises:
1025 TestListError: The test list ID is not valid.
1026 """
1027 try:
1028 return self.test_lists[test_list_id]
1029 except KeyError:
Peter Shihd14623e2017-11-14 15:12:54 +08001030 raise type_utils.TestListError(
Wei-Han Chendbc0fa22017-09-28 10:29:53 +08001031 '%r is not a valid test list ID (available IDs are %r)' % (
1032 test_list_id, sorted(self.test_lists.keys())))
Jon Salz128b0932013-07-03 16:55:26 +08001033
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001034 def _RecordStartError(self, error_message):
1035 """Appends the startup error message into the shared data."""
1036 KEY = 'startup_error'
1037 data = self.state_instance.get_shared_data(KEY, optional=True)
1038 new_data = '%s\n\n%s' % (data, error_message) if data else error_message
1039 self.state_instance.set_shared_data(KEY, new_data)
1040
Peter Shih658d41d2018-04-16 15:42:00 +08001041 def _InitTestLists(self):
Joel Kitching50a63ea2016-02-22 13:15:09 +08001042 """Reads in all test lists and sets the active test list.
1043
1044 Returns:
1045 True if the active test list could be set, False if failed.
1046 """
1047 startup_errors = []
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +08001048
1049 self.test_lists, failed_files = self.test_list_manager.BuildAllTestLists()
1050
Wei-Han Chendbc0fa22017-09-28 10:29:53 +08001051 logging.info('Loaded test lists: %r', sorted(self.test_lists.keys()))
Jon Salz128b0932013-07-03 16:55:26 +08001052
Joel Kitching50a63ea2016-02-22 13:15:09 +08001053 # Check for any syntax errors in test list files.
1054 if failed_files:
1055 logging.info('Failed test list files: [%s]',
1056 ' '.join(failed_files.keys()))
1057 for f, exc_info in failed_files.iteritems():
1058 logging.error('Error in test list file: %s', f,
1059 exc_info=exc_info)
1060
1061 # Limit the stack trace to the very last entry.
1062 exc_type, exc_value, exc_traceback = exc_info
1063 while exc_traceback and exc_traceback.tb_next:
1064 exc_traceback = exc_traceback.tb_next
1065
1066 exc_string = ''.join(
1067 traceback.format_exception(
1068 exc_type, exc_value, exc_traceback)).rstrip()
1069 startup_errors.append('Error in test list file (%s):\n%s'
1070 % (f, exc_string))
1071
Jon Salz128b0932013-07-03 16:55:26 +08001072 if not self.options.test_list:
Hung-Te Linab78fc42017-09-22 00:21:57 +08001073 self.options.test_list = self.test_list_manager.GetActiveTestListId()
Jon Salz128b0932013-07-03 16:55:26 +08001074
Joel Kitching50a63ea2016-02-22 13:15:09 +08001075 # Check for a non-existent test list ID.
1076 try:
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001077 self.test_list = self.GetTestList(self.options.test_list)
Joel Kitching50a63ea2016-02-22 13:15:09 +08001078 logging.info('Active test list: %s', self.test_list.test_list_id)
Peter Shihd14623e2017-11-14 15:12:54 +08001079 except type_utils.TestListError as e:
Joel Kitching50a63ea2016-02-22 13:15:09 +08001080 logging.exception('Invalid active test list: %s',
1081 self.options.test_list)
1082 startup_errors.append(e.message)
Jon Salz128b0932013-07-03 16:55:26 +08001083
Joel Kitching50a63ea2016-02-22 13:15:09 +08001084 # Show all startup errors.
1085 if startup_errors:
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001086 self._RecordStartError('\n\n'.join(startup_errors))
Joel Kitching50a63ea2016-02-22 13:15:09 +08001087
1088 # Only return False if failed to load the active test list.
1089 return bool(self.test_list)
Jon Salz128b0932013-07-03 16:55:26 +08001090
Peter Shih658d41d2018-04-16 15:42:00 +08001091 def _InitHooks(self):
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001092 """Initializes hooks.
1093
1094 Must run after self.test_list ready.
1095 """
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001096 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1097 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
Wei-Han Chen1a114682017-10-02 10:33:54 +08001098 assert isinstance(self.hooks, hooks.Hooks), (
Ricky Liang45c73e72015-01-15 15:00:30 +08001099 'hooks should be of type Hooks but is %r' % type(self.hooks))
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001100 self.hooks.test_list = self.test_list
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001101 self.hooks.OnCreatedTestList()
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001102
Peter Shih658d41d2018-04-16 15:42:00 +08001103 def InitUI(self):
Vic Yanga3cecf82014-12-26 00:44:21 -08001104 """Initialize UI."""
Shen-En Shih65619f42017-10-02 16:52:10 +08001105 logging.info('Waiting for a web socket connection')
1106 self.web_socket_manager.wait()
Vic Yanga3cecf82014-12-26 00:44:21 -08001107 self._ui_initialized = True
Vic Yanga3cecf82014-12-26 00:44:21 -08001108
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001109 @staticmethod
1110 def GetCommandLineArgsParser():
1111 """Returns a parser for Goofy command line arguments."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001112 parser = OptionParser()
1113 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001114 action='store_true',
1115 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001116 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001117 action='store_true',
1118 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001119 parser.add_option('--test_list', dest='test_list',
Wei-Han Chen84fee7c2016-08-26 21:56:25 +08001120 metavar='TEST_LIST_ID',
1121 help='Use test list whose id is TEST_LIST_ID')
Peter Shih2c2bf262018-01-19 15:31:39 +08001122 parser.add_option('--no-goofy-ui', dest='goofy_ui',
1123 action='store_false', default=True,
1124 help='start without Goofy UI')
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001125 return parser
1126
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001127 def _PrepareDUTLink(self):
1128 # TODO(akahuang): Move this part into a pytest.
1129 # Prepare DUT link after the plugins start running, because the link might
1130 # need the network connection.
1131
1132 dut_options = self.test_list.options.dut_options
1133 if dut_options:
1134 logging.info('dut_options set by %s: %r', self.test_list.test_list_id,
1135 self.test_list.options.dut_options)
1136
Peter Shih658d41d2018-04-16 15:42:00 +08001137 def PrepareLink():
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001138 try:
1139 device_utils.PrepareDUTLink(**dut_options)
1140 except Exception:
1141 logging.exception('Unable to prepare DUT link.')
1142
Peter Shihdb06b092018-04-16 16:55:38 +08001143 process_utils.StartDaemonThread(target=PrepareLink)
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001144
Peter Shih658d41d2018-04-16 15:42:00 +08001145 def Init(self, args=None, env=None):
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001146 """Initializes Goofy.
1147
1148 Args:
Shen-En Shihe05cbbc2017-10-02 16:47:30 +08001149 args: A list of command-line arguments. Uses sys.argv if args is None.
1150 env: An Environment instance to use (or None to use DUTEnvironment).
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001151 """
1152 (self.options, self.args) = self.GetCommandLineArgsParser().parse_args(args)
Jon Salz0697cbf2012-07-04 15:14:04 +08001153
Peter Shih658d41d2018-04-16 15:42:00 +08001154 signal.signal(signal.SIGINT, self._HandleSignal)
1155 signal.signal(signal.SIGTERM, self._HandleSignal)
Hung-Te Lina846f602014-07-04 20:32:22 +08001156 # TODO(hungte) SIGTERM does not work properly without Telemetry and should
1157 # be fixed.
Hung-Te Lina846f602014-07-04 20:32:22 +08001158
Jon Salz46b89562012-07-05 11:49:22 +08001159 # Make sure factory directories exist.
Peter Shihb4e49352017-05-25 17:35:11 +08001160 for path in [
1161 paths.DATA_LOG_DIR, paths.DATA_STATE_DIR, paths.DATA_TESTS_DIR]:
1162 file_utils.TryMakeDirs(path)
Jon Salz46b89562012-07-05 11:49:22 +08001163
Wei-Han Chen78f35f62017-03-06 20:11:20 +08001164 try:
1165 goofy_default_options = config_utils.LoadConfig(validate_schema=False)
1166 for key, value in goofy_default_options.iteritems():
1167 if getattr(self.options, key, None) is None:
1168 logging.info('self.options.%s = %r', key, value)
1169 setattr(self.options, key, value)
1170 except Exception:
1171 logging.exception('failed to load goofy overriding options')
1172
Jon Salzee85d522012-07-17 14:34:46 +08001173 event_log.IncrementBootSequence()
Hung-Te Linda8eb992017-09-28 03:27:12 +08001174 session.IncrementInitCount()
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001175
Jon Salzd15bbcf2013-05-21 17:33:57 +08001176 # Don't defer logging the initial event, so we can make sure
1177 # that device_id, reimage_id, etc. are all set up.
1178 self.event_log = EventLog('goofy', defer=False)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001179 self.testlog = testlog.Testlog(
Peter Shihb4e49352017-05-25 17:35:11 +08001180 log_root=paths.DATA_LOG_DIR, uuid=self.uuid,
Hung-Te Linda8eb992017-09-28 03:27:12 +08001181 stationDeviceId=session.GetDeviceID(),
1182 stationInstallationId=session.GetInstallationID())
Jon Salz0697cbf2012-07-04 15:14:04 +08001183
Jon Salz0697cbf2012-07-04 15:14:04 +08001184 if env:
1185 self.env = env
Shen-En Shihe05cbbc2017-10-02 16:47:30 +08001186 else:
Ricky Liang09d66d82014-09-25 11:20:54 +08001187 self.env = test_environment.DUTEnvironment()
Jon Salz0697cbf2012-07-04 15:14:04 +08001188 self.env.goofy = self
1189
1190 if self.options.restart:
1191 state.clear_state()
1192
Peter Shih658d41d2018-04-16 15:42:00 +08001193 self._InitGoofyServer()
Peter Shihf821c6a2018-03-05 13:31:22 +08001194 # Both the i18n file and index.html should be registered to Goofy before we
1195 # start the Goofy server, to avoid race condition that Goofy would return
1196 # 404 not found before index.html is registered.
Peter Shih658d41d2018-04-16 15:42:00 +08001197 self._InitI18n()
1198 self._InitStaticFiles()
Peter Shihf821c6a2018-03-05 13:31:22 +08001199
1200 logging.info('Starting goofy server')
1201 self.goofy_server_thread.start()
1202
Peter Shih658d41d2018-04-16 15:42:00 +08001203 self._InitStateInstance()
Jon Salz0697cbf2012-07-04 15:14:04 +08001204 self.last_shutdown_time = (
Ricky Liang45c73e72015-01-15 15:00:30 +08001205 self.state_instance.get_shared_data('shutdown_time', optional=True))
Jon Salz0697cbf2012-07-04 15:14:04 +08001206 self.state_instance.del_shared_data('shutdown_time', optional=True)
Jon Salzb19ea072013-02-07 16:35:00 +08001207 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001208
Joel Kitching50a63ea2016-02-22 13:15:09 +08001209 success = False
Jon Salz128b0932013-07-03 16:55:26 +08001210 try:
Peter Shih658d41d2018-04-16 15:42:00 +08001211 success = self._InitTestLists()
Hung-Te Linc8174b52017-06-02 11:11:45 +08001212 except Exception:
Shen-En Shih44107d12017-10-02 11:31:12 +08001213 logging.exception('Unable to initialize test lists')
1214 self._RecordStartError(
1215 'Unable to initialize test lists\n%s' % traceback.format_exc())
Joel Kitching50a63ea2016-02-22 13:15:09 +08001216
1217 if not success:
Shen-En Shih65619f42017-10-02 16:52:10 +08001218 # Create an empty test list with default options so that the rest of
1219 # startup can proceed.
1220 # A message box will pop up in UI for the error details.
Wei-Han Chena14210e2017-10-16 14:07:52 +08001221 self.test_list = manager.DummyTestList(self.test_list_manager)
1222
1223 self.test_list.state_instance = self.state_instance
Jon Salzb19ea072013-02-07 16:35:00 +08001224
Peter Shih658d41d2018-04-16 15:42:00 +08001225 self._InitHooks()
chuntsen6780ea22017-12-12 14:53:53 +08001226 self.testlog.init_hooks(self.test_list.options.testlog_hooks)
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001227
Jon Salz822838b2013-03-25 17:32:33 +08001228 if self.test_list.options.clear_state_on_start:
Wei-Han Chendcecbea2018-03-14 19:00:23 +08001229 # TODO(stimim): Perhaps we should check if we are running `shutdown` test?
Jon Salz822838b2013-03-25 17:32:33 +08001230 self.state_instance.clear_test_state()
1231
Jon Salz670ce062014-05-16 15:53:50 +08001232 # If the phase is invalid, this will raise a ValueError.
1233 phase.SetPersistentPhase(self.test_list.options.phase)
1234
Peter Shih3b0bb9f2017-03-21 16:23:32 +08001235 if not self.state_instance.has_shared_data('ui_locale'):
Hung-Te Lin134403c2017-08-23 17:30:17 +08001236 ui_locale = self.test_list.options.ui_locale
Peter Shih3b0bb9f2017-03-21 16:23:32 +08001237 self.state_instance.set_shared_data('ui_locale', ui_locale)
Jon Salz0697cbf2012-07-04 15:14:04 +08001238 self.state_instance.set_shared_data(
Ricky Liang45c73e72015-01-15 15:00:30 +08001239 'test_list_options',
Peter Shih90425db2017-08-02 15:53:48 +08001240 self.test_list.options.ToDict())
Jon Salz0697cbf2012-07-04 15:14:04 +08001241 self.state_instance.test_list = self.test_list
1242
Peter Shih658d41d2018-04-16 15:42:00 +08001243 self._InitStates()
1244 self._StartEventServer()
Hung-Te Lincc41d2a2014-10-29 13:35:20 +08001245
Earl Oua3bca122016-10-21 16:00:30 +08001246 # Load and run Goofy plugins.
1247 self.plugin_controller = plugin_controller.PluginController(
1248 self.test_list.options.plugin_config_name, self)
1249 self.plugin_controller.StartAllPlugins()
1250
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001251 if success:
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001252 self._PrepareDUTLink()
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001253
Jon Salz0697cbf2012-07-04 15:14:04 +08001254 # Note that we create a log watcher even if
1255 # sync_event_log_period_secs isn't set (no background
1256 # syncing), since we may use it to flush event logs as well.
1257 self.log_watcher = EventLogWatcher(
Ricky Liang45c73e72015-01-15 15:00:30 +08001258 self.test_list.options.sync_event_log_period_secs,
1259 event_log_db_file=None,
Peter Shih658d41d2018-04-16 15:42:00 +08001260 handle_event_logs_callback=self._HandleEventLogs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001261 if self.test_list.options.sync_event_log_period_secs:
1262 self.log_watcher.StartWatchThread()
1263
Shen-En Shihf4ad32f2017-07-31 15:56:39 +08001264 self.event_client.post_event(
1265 Event(Event.Type.UPDATE_SYSTEM_INFO))
Jon Salz0697cbf2012-07-04 15:14:04 +08001266
1267 os.environ['CROS_FACTORY'] = '1'
1268 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1269
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001270 # Should not move earlier.
1271 self.hooks.OnStartup()
1272
Ricky Liang36512a32014-07-25 11:47:04 +08001273 # Only after this point the Goofy backend is ready for UI connection.
1274 self.ready_for_ui_connection = True
1275
Jon Salz0697cbf2012-07-04 15:14:04 +08001276 def state_change_callback(test, test_state):
1277 self.event_client.post_event(
Peter Shihd46c5fd2017-09-22 15:28:42 +08001278 Event(
1279 Event.Type.STATE_CHANGE,
1280 path=test.path,
1281 state=test_state.ToStruct()))
Jon Salz0697cbf2012-07-04 15:14:04 +08001282 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001283
Vic Yange2c76a82014-10-30 12:48:19 -07001284 self.pytest_prespawner = prespawner.PytestPrespawner()
1285 self.pytest_prespawner.start()
Jon Salza6711d72012-07-18 14:33:03 +08001286
Ricky Liang48e47f92014-02-26 19:31:51 +08001287 tests_after_shutdown = self.state_instance.get_shared_data(
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001288 TESTS_AFTER_SHUTDOWN, optional=True)
Jon Salz5c344f62012-07-13 14:31:16 +08001289 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001290
Jon Salz5c344f62012-07-13 14:31:16 +08001291 if not force_auto_run and tests_after_shutdown is not None:
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001292 logging.info('Resuming tests after shutdown: %r', tests_after_shutdown)
1293 self.test_list_iterator = tests_after_shutdown
Wei-Han Chenbcac7252017-04-21 19:46:51 +08001294 self.test_list_iterator.SetTestList(self.test_list)
Peter Shih658d41d2018-04-16 15:42:00 +08001295 self.RunEnqueue(self._RunNextTest)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001296 elif force_auto_run or self.test_list.options.auto_run_on_start:
Peter Shih53323922018-01-02 15:02:21 +08001297 status_filter = [TestState.UNTESTED]
1298 if self.test_list.options.retry_failed_on_start:
1299 status_filter.append(TestState.FAILED)
Peter Shih658d41d2018-04-16 15:42:00 +08001300 self.RunEnqueue(lambda: self._RunTests(self.test_list, status_filter))
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001301 self.state_instance.set_shared_data(TESTS_AFTER_SHUTDOWN, None)
Peter Shih658d41d2018-04-16 15:42:00 +08001302 self._RestoreActiveRunState()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001303
Hung-Te Lin793d6572017-09-04 18:17:56 +08001304 self.hooks.OnTestStart()
Vic Yang08505c72015-01-06 17:01:53 -08001305
Peter Shih658d41d2018-04-16 15:42:00 +08001306 def _PerformPeriodicTasks(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +08001307 """Perform any periodic work.
Vic Yang4953fc12012-07-26 16:19:53 +08001308
Peter Ammon1e1ec572014-06-26 17:56:32 -07001309 This method must not raise exceptions.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001310 """
Peter Shih658d41d2018-04-16 15:42:00 +08001311 self._CheckPlugins()
1312 self._CheckForUpdates()
Jon Salz57717ca2012-04-04 16:47:25 +08001313
Peter Shih658d41d2018-04-16 15:42:00 +08001314 def _HandleEventLogs(self, chunks, periodic=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001315 """Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001316
Hung-Te Lind151bf32017-08-30 11:05:47 +08001317 Attempts to upload the event logs to the factory server.
Vic Yang93027612013-05-06 02:42:49 +08001318
1319 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001320 chunks: A list of Chunk objects.
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001321 periodic: This event log handling is periodic. Error messages
1322 will only be shown for the first time.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001323 """
Vic Yang93027612013-05-06 02:42:49 +08001324 first_exception = None
1325 exception_count = 0
1326
Jon Salzd15bbcf2013-05-21 17:33:57 +08001327 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001328 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001329 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001330 start_time = time.time()
Peter Shihfdc09c72018-08-06 17:04:49 +08001331 proxy = server_proxy.GetServerProxy()
Hung-Te Lind151bf32017-08-30 11:05:47 +08001332 proxy.UploadEvent(
1333 chunk.log_name + '.' + event_log.GetReimageId(),
1334 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001335 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +08001336 'Successfully synced %s in %.03f s',
1337 description, time.time() - start_time)
Hung-Te Linc8174b52017-06-02 11:11:45 +08001338 except Exception:
Hung-Te Linf707b242016-01-08 23:11:42 +08001339 first_exception = (first_exception or
1340 (chunk.log_name + ': ' +
1341 debug_utils.FormatExceptionOnly()))
Vic Yang93027612013-05-06 02:42:49 +08001342 exception_count += 1
1343
1344 if exception_count:
1345 if exception_count == 1:
1346 msg = 'Log upload failed: %s' % first_exception
1347 else:
1348 msg = '%d log upload failed; first is: %s' % (
1349 exception_count, first_exception)
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001350 # For periodic event log syncing, only show the first error messages.
1351 if periodic:
1352 if not self._suppress_event_log_error_messages:
1353 self._suppress_event_log_error_messages = True
Hung-Te Lind151bf32017-08-30 11:05:47 +08001354 logging.warning('Suppress periodic factory server error messages for '
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001355 'event log syncing after the first one.')
1356 raise Exception(msg)
1357 # For event log syncing by request, show the error messages.
1358 else:
1359 raise Exception(msg)
Vic Yang93027612013-05-06 02:42:49 +08001360
Peter Shih658d41d2018-04-16 15:42:00 +08001361 def _RunTestsWithStatus(self, statuses_to_run, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001362 """Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001363
Jon Salz0697cbf2012-07-04 15:14:04 +08001364 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001365
Jon Salz0697cbf2012-07-04 15:14:04 +08001366 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001367 statuses_to_run: The particular status that caller wants to run.
Jon Salz0697cbf2012-07-04 15:14:04 +08001368 starting_at: If provided, only auto-runs tests beginning with
1369 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001370 root: The root of tests to run. If not provided, it will be
1371 the root of all tests.
1372 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001373 root = root or self.test_list
Peter Shih658d41d2018-04-16 15:42:00 +08001374 self._AbortActiveTests('Operator requested run/re-run of certain tests')
1375 self._RunTests(root, status_filter=statuses_to_run)
Jon Salz0405ab52012-03-16 15:26:52 +08001376
Peter Shih658d41d2018-04-16 15:42:00 +08001377 def RestartTests(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001378 """Restarts all tests."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001379 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001380
Peter Shih658d41d2018-04-16 15:42:00 +08001381 self._AbortActiveTests('Operator requested restart of certain tests')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +08001382 for test in root.Walk():
1383 test.UpdateState(status=TestState.UNTESTED)
Peter Shih658d41d2018-04-16 15:42:00 +08001384 self._RunTests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001385
Peter Shih658d41d2018-04-16 15:42:00 +08001386 def _AutoRun(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001387 """"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001388
Jon Salz0697cbf2012-07-04 15:14:04 +08001389 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001390 root: If provided, the root of tests to run. If not provided, the root
1391 will be test_list (root of all tests).
1392 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001393 root = root or self.test_list
Peter Shih658d41d2018-04-16 15:42:00 +08001394 self._RunTestsWithStatus([TestState.UNTESTED, TestState.ACTIVE], root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001395
Peter Shih658d41d2018-04-16 15:42:00 +08001396 def Wait(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001397 """Waits for all pending invocations.
Jon Salz0697cbf2012-07-04 15:14:04 +08001398
1399 Useful for testing.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001400 """
Jon Salz1acc8742012-07-17 17:45:55 +08001401 while self.invocations:
Peter Shih06d08212018-01-19 17:15:57 +08001402 for invoc in self.invocations.itervalues():
1403 logging.info('Waiting for %s to complete...', invoc.test)
1404 invoc.thread.join()
Peter Shih658d41d2018-04-16 15:42:00 +08001405 self.ReapCompletedTests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001406
Peter Shih658d41d2018-04-16 15:42:00 +08001407 def _TestFail(self, test):
Hung-Te Lin793d6572017-09-04 18:17:56 +08001408 self.hooks.OnTestFailure(test)
Claire Changd1961a22015-08-05 16:15:55 +08001409
Wei-Han Chenced08ef2016-11-08 09:40:02 +08001410
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001411def main():
1412 # Logging should be solved first.
1413 (options, unused_args) = Goofy.GetCommandLineArgsParser().parse_args()
Hung-Te Lin8fc0d652017-09-21 13:05:44 +08001414 log_utils.InitLogging(verbose=options.verbose)
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001415
Peter Shih658d41d2018-04-16 15:42:00 +08001416 Goofy.RunMainAndExit()
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001417
1418
1419if __name__ == '__main__':
1420 main()