blob: 49f482fe426a434d10451f37463e41146b4f1f1b [file] [log] [blame]
Mike Frysinger63bb3c72019-09-01 15:16:26 -04001#!/usr/bin/env python2
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
Marco Chena20766e2018-09-27 18:29:35 +080076# Key to store active test list id.
77ACTIVE_TEST_LIST_ID = 'active_test_list_id'
78
Hung-Te Linf707b242016-01-08 23:11:42 +080079Status = type_utils.Enum(['UNINITIALIZED', 'INITIALIZING', 'RUNNING',
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080080 'TERMINATING', 'TERMINATED'])
Jon Salzd7550792013-07-12 05:49:27 +080081
Hung-Te Lina452d4d2017-10-25 17:46:14 +080082RUN_QUEUE_TIMEOUT_SECS = 10
83
Peter Shih658d41d2018-04-16 15:42:00 +080084
Hung-Te Lina452d4d2017-10-25 17:46:14 +080085class Goofy(object):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +080086 """The main factory flow.
Jon Salz0697cbf2012-07-04 15:14:04 +080087
88 Note that all methods in this class must be invoked from the main
89 (event) thread. Other threads, such as callbacks and TestInvocation
90 methods, should instead post events on the run queue.
91
92 TODO: Unit tests. (chrome-os-partner:7409)
93
94 Properties:
Hung-Te Lina452d4d2017-10-25 17:46:14 +080095 run_queue: A queue of callbacks to invoke from the main thread.
96 exceptions: List of exceptions encountered in invocation threads.
97 last_idle: The most recent time of invoking the idle queue handler, or none.
Jon Salz0697cbf2012-07-04 15:14:04 +080098 uuid: A unique UUID for this invocation of Goofy.
99 state_instance: An instance of FactoryState.
100 state_server: The FactoryState XML/RPC server.
101 state_server_thread: A thread running state_server.
102 event_server: The EventServer socket server.
103 event_server_thread: A thread running event_server.
104 event_client: A client to the event server.
Earl Oua3bca122016-10-21 16:00:30 +0800105 plugin_controller: The PluginController object.
Peter Shih06d08212018-01-19 17:15:57 +0800106 invocations: A map from TestInvocation uuid to the corresponding
Jon Salz0697cbf2012-07-04 15:14:04 +0800107 TestInvocations objects representing active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800108 options: Command-line options.
109 args: Command-line args.
110 test_list: The test list.
Jon Salz128b0932013-07-03 16:55:26 +0800111 test_lists: All new-style test lists.
Ricky Liang4bff3e32014-02-20 18:46:11 +0800112 run_id: The identifier for latest test run.
113 scheduled_run_tests: The list of tests scheduled for latest test run.
Jon Salz0697cbf2012-07-04 15:14:04 +0800114 event_handlers: Map of Event.Type to the method used to handle that
115 event. If the method has an 'event' argument, the event is passed
116 to the handler.
Jon Salz416f9cc2013-05-10 18:32:50 +0800117 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salzd7550792013-07-12 05:49:27 +0800118 status: The current Goofy status (a member of the Status enum).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800119 """
Ricky Liang45c73e72015-01-15 15:00:30 +0800120
Jon Salz0697cbf2012-07-04 15:14:04 +0800121 def __init__(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800122 self.run_queue = Queue.Queue()
123 self.exceptions = []
124 self.last_idle = None
125
Jon Salz0697cbf2012-07-04 15:14:04 +0800126 self.uuid = str(uuid.uuid4())
127 self.state_instance = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800128 self.goofy_server = None
129 self.goofy_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800130 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800131 self.event_server = None
132 self.event_server_thread = None
133 self.event_client = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800134 self.log_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800135 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800136 self.testlog = None
Earl Oua3bca122016-10-21 16:00:30 +0800137 self.plugin_controller = None
Vic Yange2c76a82014-10-30 12:48:19 -0700138 self.pytest_prespawner = None
Vic Yanga3cecf82014-12-26 00:44:21 -0800139 self._ui_initialized = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800140 self.invocations = {}
Jon Salz0697cbf2012-07-04 15:14:04 +0800141 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()
Jon Salzd7550792013-07-12 05:49:27 +0800157 self.status = Status.UNINITIALIZED
Ricky Liang36512a32014-07-25 11:47:04 +0800158 self.ready_for_ui_connection = False
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800159 self.is_restart_requested = False
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800160 self.test_list_iterator = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800161
Wei-Han Chen16cc5dd2017-04-27 17:38:53 +0800162 self.test_list_manager = manager.Manager()
163
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800164 # TODO(hungte) Support controlling remote DUT.
Hung-Te Linb6287242016-05-18 14:39:05 +0800165 self.dut = device_utils.CreateDUTInterface()
Hung-Te Lin6a72c642015-12-13 22:09:09 +0800166
Peter Shih658d41d2018-04-16 15:42:00 +0800167 def TestOrRoot(event, parent_or_group=True):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800168 """Returns the test affected by a particular event.
Jon Salz85a39882012-07-05 16:45:04 +0800169
170 Args:
171 event: The event containing an optional 'path' attribute.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800172 parent_or_group: If True, returns the top-level parent for a test (the
Jon Salz85a39882012-07-05 16:45:04 +0800173 root node of the tests that need to be run together if the given test
174 path is to be run).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800175 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800176 try:
177 path = event.path
178 except AttributeError:
179 path = None
180
181 if path:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800182 test = self.test_list.LookupPath(path)
Jon Salz85a39882012-07-05 16:45:04 +0800183 if parent_or_group:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800184 test = test.GetTopLevelParentOrGroup()
Jon Salz85a39882012-07-05 16:45:04 +0800185 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800186 else:
Peter Shih999faf72017-07-07 11:32:42 +0800187 return self.test_list.ToFactoryTestList()
Jon Salz0697cbf2012-07-04 15:14:04 +0800188
189 self.event_handlers = {
Ricky Liang45c73e72015-01-15 15:00:30 +0800190 Event.Type.RESTART_TESTS:
Peter Shih658d41d2018-04-16 15:42:00 +0800191 lambda event: self.RestartTests(root=TestOrRoot(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800192 Event.Type.AUTO_RUN:
Peter Shih658d41d2018-04-16 15:42:00 +0800193 lambda event: self._AutoRun(root=TestOrRoot(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800194 Event.Type.RUN_TESTS_WITH_STATUS:
Peter Shih658d41d2018-04-16 15:42:00 +0800195 lambda event: self._RunTestsWithStatus(
Ricky Liang45c73e72015-01-15 15:00:30 +0800196 event.status,
Peter Shih658d41d2018-04-16 15:42:00 +0800197 root=TestOrRoot(event)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800198 Event.Type.UPDATE_SYSTEM_INFO:
Peter Shih658d41d2018-04-16 15:42:00 +0800199 lambda event: self._UpdateSystemInfo(),
Ricky Liang45c73e72015-01-15 15:00:30 +0800200 Event.Type.STOP:
Peter Shih658d41d2018-04-16 15:42:00 +0800201 lambda event: self.Stop(root=TestOrRoot(event, False),
Ricky Liang45c73e72015-01-15 15:00:30 +0800202 fail=getattr(event, 'fail', False),
203 reason=getattr(event, 'reason', None)),
Ricky Liang45c73e72015-01-15 15:00:30 +0800204 Event.Type.CLEAR_STATE:
Peter Shih658d41d2018-04-16 15:42:00 +0800205 lambda event: self.ClearState(
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800206 self.test_list.LookupPath(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800207 }
208
Jon Salz0697cbf2012-07-04 15:14:04 +0800209 self.web_socket_manager = None
210
Peter Shih658d41d2018-04-16 15:42:00 +0800211 def Destroy(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800212 """Performs any shutdown tasks."""
chuntsen9d675c62017-06-20 14:35:30 +0800213 # To avoid race condition when running shutdown test.
Peter Shih06d08212018-01-19 17:15:57 +0800214 for invoc in self.invocations.itervalues():
215 logging.info('Waiting for %s to complete...', invoc.test)
chuntsen9d675c62017-06-20 14:35:30 +0800216 invoc.thread.join(3) # Timeout in 3 seconds.
217
Jon Salzd7550792013-07-12 05:49:27 +0800218 self.status = Status.TERMINATING
Jon Salz0697cbf2012-07-04 15:14:04 +0800219 if self.chrome:
220 self.chrome.kill()
221 self.chrome = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800222 if self.web_socket_manager:
223 logging.info('Stopping web sockets')
224 self.web_socket_manager.close()
225 self.web_socket_manager = None
Earl Ouacbe99c2017-02-21 16:04:19 +0800226 if self.goofy_server_thread:
227 logging.info('Stopping goofy server')
Peter Shih0a6ae8d2017-10-23 17:59:42 +0800228 net_utils.ShutdownTCPServer(self.goofy_server)
Earl Ouacbe99c2017-02-21 16:04:19 +0800229 self.goofy_server_thread.join()
230 self.goofy_server.server_close()
231 self.goofy_server_thread = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800232 if self.state_instance:
Fei Shaobcaa52b2018-09-28 13:27:15 +0800233 self.state_instance.Close()
Jon Salz0697cbf2012-07-04 15:14:04 +0800234 if self.event_server_thread:
235 logging.info('Stopping event server')
Peter Shihce9490e2017-05-11 14:32:12 +0800236 net_utils.ShutdownTCPServer(self.event_server)
Jon Salz0697cbf2012-07-04 15:14:04 +0800237 self.event_server_thread.join()
238 self.event_server.server_close()
239 self.event_server_thread = None
240 if self.log_watcher:
241 if self.log_watcher.IsThreadStarted():
242 self.log_watcher.StopWatchThread()
243 self.log_watcher = None
Vic Yange2c76a82014-10-30 12:48:19 -0700244 if self.pytest_prespawner:
245 logging.info('Stopping pytest prespawner')
246 self.pytest_prespawner.stop()
247 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800248 if self.event_client:
249 logging.info('Closing event client')
250 self.event_client.close()
251 self.event_client = None
252 if self.event_log:
253 self.event_log.Close()
254 self.event_log = None
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800255 if self.testlog:
256 self.testlog.Close()
257 self.testlog = None
Earl Oua3bca122016-10-21 16:00:30 +0800258 if self.plugin_controller:
259 self.plugin_controller.StopAndDestroyAllPlugins()
260 self.plugin_controller = None
Dean Liao592e4d52013-01-10 20:06:39 +0800261
Peter Shih658d41d2018-04-16 15:42:00 +0800262 self._CheckExceptions()
Jon Salz0697cbf2012-07-04 15:14:04 +0800263 logging.info('Done destroying Goofy')
Jon Salzd7550792013-07-12 05:49:27 +0800264 self.status = Status.TERMINATED
Jon Salz0697cbf2012-07-04 15:14:04 +0800265
Peter Shih658d41d2018-04-16 15:42:00 +0800266 def _InitGoofyServer(self):
Earl Ouacbe99c2017-02-21 16:04:19 +0800267 self.goofy_server = goofy_server.GoofyServer(
Shen-En Shihd5b96bf2017-08-09 17:47:21 +0800268 (goofy_proxy.DEFAULT_GOOFY_BIND, goofy_proxy.DEFAULT_GOOFY_PORT))
Earl Ouacbe99c2017-02-21 16:04:19 +0800269 self.goofy_server_thread = threading.Thread(
270 target=self.goofy_server.serve_forever,
271 name='GoofyServer')
Peter Shihdb06b092018-04-16 16:55:38 +0800272 self.goofy_server_thread.daemon = True
Earl Ouacbe99c2017-02-21 16:04:19 +0800273
Peter Shih658d41d2018-04-16 15:42:00 +0800274 def _InitStaticFiles(self):
Peter Shih7cc81b12017-08-24 13:04:46 +0800275 static_path = os.path.join(paths.FACTORY_PYTHON_PACKAGE_DIR, 'goofy/static')
Earl Ouacbe99c2017-02-21 16:04:19 +0800276 # Setup static file path
Peter Shih7cc81b12017-08-24 13:04:46 +0800277 self.goofy_server.RegisterPath('/', static_path)
Earl Ouacbe99c2017-02-21 16:04:19 +0800278
Peter Shih658d41d2018-04-16 15:42:00 +0800279 def _InitStateInstance(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800280 # Before starting state server, remount stateful partitions with
281 # no commit flag. The default commit time (commit=600) makes corruption
282 # too likely.
Hung-Te Lin1968d9c2016-01-08 22:55:46 +0800283 sys_utils.ResetCommitTime()
Marco Chena20766e2018-09-27 18:29:35 +0800284
Earl Ouacbe99c2017-02-21 16:04:19 +0800285 self.state_instance = state.FactoryState()
Yong Hongf879cbd2018-11-08 16:06:52 +0800286 self.last_shutdown_time = (
287 self.state_instance.DataShelfGetValue('shutdown_time', optional=True))
288 self.state_instance.DataShelfDeleteKeys('shutdown_time', optional=True)
289 self.state_instance.DataShelfDeleteKeys('startup_error', optional=True)
Marco Chenda8f6d42018-10-02 22:31:53 +0800290
Yong Hongf879cbd2018-11-08 16:06:52 +0800291 def _ResetStateInstance(self):
292 PRESERVED_KEYS = ['startup_error']
Marco Chena20766e2018-09-27 18:29:35 +0800293
Yong Hongf879cbd2018-11-08 16:06:52 +0800294 # Backup the required data.
295 preserved_data = {
296 key: self.state_instance.DataShelfGetValue(key, optional=True)
297 for key in PRESERVED_KEYS}
298
299 # Reset the state instance.
300 self.state_instance.Close()
301 state.ClearState()
302 self.state_instance = state.FactoryState()
303
304 # Write back the preserved data.
305 for key, value in preserved_data.iteritems():
306 if value is not None:
307 self.state_instance.DataShelfSetValue(key, value)
308
309 def _InitGoofyRPC(self):
Earl Ouacbe99c2017-02-21 16:04:19 +0800310 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 Shih658d41d2018-04-16 15:42:00 +0800318 def _InitI18n(self):
Peter Shih80e78b42017-03-10 17:00:56 +0800319 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
Peter Shih658d41d2018-04-16 15:42:00 +0800325 def _StartEventServer(self):
Jon Salz0697cbf2012-07-04 15:14:04 +0800326 self.event_server = EventServer()
327 logging.info('Starting factory event server')
Peter Shihdb06b092018-04-16 16:55:38 +0800328 self.event_server_thread = process_utils.StartDaemonThread(
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
Peter Shih533566a2018-09-05 17:48:03 +0800332 # pylint 1.5.6 has a false negative on nested lambda, see
333 # https://github.com/PyCQA/pylint/issues/760.
334 # pylint: disable=undefined-variable
Shen-En Shihd7359cc2017-10-03 16:01:47 +0800335 self.event_client = ThreadingEventClient(
Peter Shih658d41d2018-04-16 15:42:00 +0800336 callback=lambda event: self.RunEnqueue(lambda: self.HandleEvent(event)))
Peter Shih533566a2018-09-05 17:48:03 +0800337 # pylint: enable=undefined-variable
Jon Salz0697cbf2012-07-04 15:14:04 +0800338
339 self.web_socket_manager = WebSocketManager(self.uuid)
Earl Ouacbe99c2017-02-21 16:04:19 +0800340 self.goofy_server.AddHTTPGetHandler(
341 '/event', self.web_socket_manager.handle_web_socket)
Jon Salz0697cbf2012-07-04 15:14:04 +0800342
Peter Shih658d41d2018-04-16 15:42:00 +0800343 def Shutdown(self, operation):
Ricky Liang48e47f92014-02-26 19:31:51 +0800344 """Starts shutdown procedure.
345
346 Args:
Vic (Chun-Ju) Yang05b0d952014-04-28 17:39:09 +0800347 operation: The shutdown operation (reboot, full_reboot, or halt).
Ricky Liang48e47f92014-02-26 19:31:51 +0800348 """
349 active_tests = []
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800350 for test in self.test_list.Walk():
351 if not test.IsLeaf():
Ricky Liang48e47f92014-02-26 19:31:51 +0800352 continue
353
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800354 test_state = test.GetState()
Ricky Liang48e47f92014-02-26 19:31:51 +0800355 if test_state.status == TestState.ACTIVE:
356 active_tests.append(test)
357
Fei Shao243bd6f2019-07-10 20:28:22 +0800358 if operation == 'force_halt':
359 # force_halt is a special halt request that shuts DUT down without going
360 # through shutdown.py.
361 # The use case can be like: if operators need to temporarily shutdown all
362 # DUTs (e.g. charging or leaving production line) but don't want to press
363 # power button for 10s on each, they can now use 'DUT Shutdown' in CrOS
364 # Factory Menu to perform force shutdown.
365 if active_tests:
366 message = ('Can not force halt while tests are running. '
367 'Stop all the tests and try again.')
368 session.console.error(message)
369 return
370 operation = 'halt'
371 elif not (len(active_tests) == 1 and
372 isinstance(active_tests[0], test_object.ShutdownStep)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800373 logging.error(
374 'Calling Goofy shutdown outside of the shutdown factory test')
375 return
376
377 logging.info('Start Goofy shutdown (%s)', operation)
378 # Save pending test list in the state server
Fei Shaoee5943d2018-10-10 09:41:41 +0800379 self.state_instance.DataShelfSetValue(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800380 TESTS_AFTER_SHUTDOWN, self.test_list_iterator)
Ricky Liang48e47f92014-02-26 19:31:51 +0800381 # Save shutdown time
Fei Shaoee5943d2018-10-10 09:41:41 +0800382 self.state_instance.DataShelfSetValue('shutdown_time', time.time())
Ricky Liang48e47f92014-02-26 19:31:51 +0800383
384 with self.env.lock:
385 self.event_log.Log('shutdown', operation=operation)
386 shutdown_result = self.env.shutdown(operation)
387 if shutdown_result:
388 # That's all, folks!
Peter Shih658d41d2018-04-16 15:42:00 +0800389 self.RunEnqueue(None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800390 else:
391 # Just pass (e.g., in the chroot).
Fei Shaoee5943d2018-10-10 09:41:41 +0800392 self.state_instance.DataShelfSetValue(TESTS_AFTER_SHUTDOWN, None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800393 # Send event with no fields to indicate that there is no
394 # longer a pending shutdown.
395 self.event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
396
Peter Shih658d41d2018-04-16 15:42:00 +0800397 def _HandleShutdownComplete(self, test):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800398 """Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800399
Ricky Liang6fe218c2013-12-27 15:17:17 +0800400 Args:
401 test: The ShutdownStep.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800402 """
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800403 test_state = test.UpdateState(increment_shutdown_count=1)
Jon Salz0697cbf2012-07-04 15:14:04 +0800404 logging.info('Detected shutdown (%d of %d)',
Ricky Liang48e47f92014-02-26 19:31:51 +0800405 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800406
Fei Shaoee5943d2018-10-10 09:41:41 +0800407 tests_after_shutdown = self.state_instance.DataShelfGetValue(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800408 TESTS_AFTER_SHUTDOWN, optional=True)
409
410 # Make this shutdown test the next test to run. This is to continue on
411 # post-shutdown verification in the shutdown step.
Ricky Liang48e47f92014-02-26 19:31:51 +0800412 if not tests_after_shutdown:
Wei-Han Chen29663c12017-06-27 10:28:54 +0800413 goofy_error = 'TESTS_AFTER_SHTUDOWN is not set'
Fei Shaoee5943d2018-10-10 09:41:41 +0800414 self.state_instance.DataShelfSetValue(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800415 TESTS_AFTER_SHUTDOWN, TestListIterator(test))
416 else:
Wei-Han Chen29663c12017-06-27 10:28:54 +0800417 goofy_error = tests_after_shutdown.RestartLastTest()
Fei Shaoee5943d2018-10-10 09:41:41 +0800418 self.state_instance.DataShelfSetValue(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800419 TESTS_AFTER_SHUTDOWN, tests_after_shutdown)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800420
Ricky Liang48e47f92014-02-26 19:31:51 +0800421 # Set 'post_shutdown' to inform shutdown test that a shutdown just occurred.
Fei Shaoee5943d2018-10-10 09:41:41 +0800422 self.state_instance.DataShelfSetValue(
Wei-Han Chen29663c12017-06-27 10:28:54 +0800423 state.KEY_POST_SHUTDOWN % test.path,
Fei Shaobcaa52b2018-09-28 13:27:15 +0800424 {'invocation': self.state_instance.GetTestState(test.path).invocation,
Wei-Han Chen29663c12017-06-27 10:28:54 +0800425 'goofy_error': goofy_error})
Jon Salz258a40c2012-04-19 12:34:01 +0800426
Peter Shih658d41d2018-04-16 15:42:00 +0800427 def _InitStates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800428 """Initializes all states on startup."""
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800429 for test in self.test_list.GetAllTests():
Jon Salz0697cbf2012-07-04 15:14:04 +0800430 # Make sure the state server knows about all the tests,
431 # defaulting to an untested state.
Peter Shih6e578272017-09-12 17:41:43 +0800432 test.UpdateState(update_parent=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800433
Earl Ouf76e55c2017-03-07 11:48:34 +0800434 is_unexpected_shutdown = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800435
Jon Salz0697cbf2012-07-04 15:14:04 +0800436 # Any 'active' tests should be marked as failed now.
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800437 for test in self.test_list.Walk():
438 if not test.IsLeaf():
Jon Salza6711d72012-07-18 14:33:03 +0800439 # Don't bother with parents; they will be updated when their
440 # children are updated.
441 continue
442
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800443 test_state = test.GetState()
Jon Salz0697cbf2012-07-04 15:14:04 +0800444 if test_state.status != TestState.ACTIVE:
445 continue
Wei-Han Chen03113912017-09-29 15:58:25 +0800446 if isinstance(test, test_object.ShutdownStep):
Jon Salz0697cbf2012-07-04 15:14:04 +0800447 # Shutdown while the test was active - that's good.
Peter Shih658d41d2018-04-16 15:42:00 +0800448 self._HandleShutdownComplete(test)
Shen-En Shihef6c5ae2017-12-21 14:26:47 +0800449 elif test.allow_reboot:
450 is_unexpected_shutdown = True
451 test.UpdateState(status=TestState.UNTESTED)
452 # For "allow_reboot" tests (such as "Start"), don't cancel
453 # pending tests, since reboot is expected.
454 session.console.info('Unexpected shutdown while test %s was running. '
455 'The test is marked as allow_reboot, continuing '
456 'on pending tests.',
457 test.path)
Jon Salz0697cbf2012-07-04 15:14:04 +0800458 else:
Peter Shih658d41d2018-04-16 15:42:00 +0800459 def GetUnexpectedShutdownTestRun():
chuntsen61522442017-08-11 14:40:29 +0800460 """Returns a StationTestRun for test not collected properly"""
461 station_test_run = testlog.StationTestRun()
chuntsendf856b32018-06-14 15:49:47 +0800462 station_test_run['status'] = testlog.StationTestRun.STATUS.FAIL
chuntsen921aa872018-06-15 16:28:44 +0800463 station_test_run['endTime'] = time.time()
chuntsen61522442017-08-11 14:40:29 +0800464 station_test_run.AddFailure(
465 'GoofyErrorMsg', 'Unexpected shutdown while test was running')
466 return station_test_run
467
Earl Ouf76e55c2017-03-07 11:48:34 +0800468 is_unexpected_shutdown = True
Jon Salz0697cbf2012-07-04 15:14:04 +0800469 error_msg = 'Unexpected shutdown while test was running'
470 self.event_log.Log('end_test',
Ricky Liang45c73e72015-01-15 15:00:30 +0800471 path=test.path,
472 status=TestState.FAILED,
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800473 invocation=test.GetState().invocation,
Earl Ouf76e55c2017-03-07 11:48:34 +0800474 error_msg=error_msg)
chuntsen61522442017-08-11 14:40:29 +0800475 testlog.CollectExpiredSessions(paths.DATA_LOG_DIR,
Peter Shih658d41d2018-04-16 15:42:00 +0800476 GetUnexpectedShutdownTestRun())
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800477 test.UpdateState(
Ricky Liang45c73e72015-01-15 15:00:30 +0800478 status=TestState.FAILED,
479 error_msg=error_msg)
Chun-Ta Lin87c2dac2015-05-02 01:35:01 -0700480 # Trigger the OnTestFailure callback.
Peter Shih658d41d2018-04-16 15:42:00 +0800481 self.RunEnqueue(lambda: self._TestFail(test))
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800482
Shen-En Shihef6c5ae2017-12-21 14:26:47 +0800483 session.console.info('Unexpected shutdown while test %s '
484 'running; cancelling any pending tests',
485 test.path)
486 # cancel pending tests by replace the iterator with an empty one
Fei Shaoee5943d2018-10-10 09:41:41 +0800487 self.state_instance.DataShelfSetValue(
Shen-En Shihef6c5ae2017-12-21 14:26:47 +0800488 TESTS_AFTER_SHUTDOWN,
489 TestListIterator(None))
Jon Salz008f4ea2012-08-28 05:39:45 +0800490
Earl Ouf76e55c2017-03-07 11:48:34 +0800491 if is_unexpected_shutdown:
492 logging.warning("Unexpected shutdown.")
Wei-Han Chen8d7fbc42017-10-18 19:20:47 +0800493 self.hooks.OnUnexpectedReboot(self)
Earl Ouf76e55c2017-03-07 11:48:34 +0800494
Wei-Han Chen109d76f2017-08-08 18:50:35 +0800495 if self.test_list.options.read_device_data_from_vpd_on_init:
496 vpd_data = {}
497 for section in [device_data.NAME_RO, device_data.NAME_RW]:
498 try:
499 vpd_data[section] = self.dut.vpd.boot.GetPartition(section).GetAll()
500 except Exception:
Hung-Te Lin1be934e2018-03-29 17:59:49 +0800501 logging.warning('Failed to read %s_VPD, ignored...', section.upper())
Wei-Han Chen109d76f2017-08-08 18:50:35 +0800502 # using None for key_map will use default key_map
503 device_data.UpdateDeviceDataFromVPD(None, vpd_data)
504
Wei-Han Chen212d2af2017-08-03 18:12:23 +0800505 # state_instance is initialized, we can mark skipped and waived tests now.
506 self.test_list.SetSkippedAndWaivedTests()
507
Peter Shih658d41d2018-04-16 15:42:00 +0800508 def HandleEvent(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800509 """Handles an event from the event server."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800510 handler = self.event_handlers.get(event.type)
511 if handler:
512 handler(event)
513 else:
514 # We don't register handlers for all event types - just ignore
515 # this event.
516 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800517
Peter Shih658d41d2018-04-16 15:42:00 +0800518 def _CheckCriticalFactoryNote(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800519 """Returns True if the last factory note is critical."""
Fei Shaoee5943d2018-10-10 09:41:41 +0800520 notes = self.state_instance.DataShelfGetValue('factory_note', optional=True)
Vic Yangaabf9fd2013-04-09 18:56:13 +0800521 return notes and notes[-1]['level'] == 'CRITICAL'
522
Peter Shih658d41d2018-04-16 15:42:00 +0800523 def ScheduleRestart(self):
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800524 """Schedules a restart event when any invocation is completed."""
525 self.is_restart_requested = True
526
Peter Shih658d41d2018-04-16 15:42:00 +0800527 def _InvocationCompletion(self):
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800528 """Callback when an invocation is completed."""
529 if self.is_restart_requested:
530 logging.info('Restart by scheduled event.')
531 self.is_restart_requested = False
Peter Shih658d41d2018-04-16 15:42:00 +0800532 self.RestartTests()
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800533 else:
Peter Shih658d41d2018-04-16 15:42:00 +0800534 self._RunNextTest()
Hung-Te Linef7f2be2015-07-20 20:38:51 +0800535
Peter Shih658d41d2018-04-16 15:42:00 +0800536 def _RunNextTest(self):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800537 """Runs the next eligible test.
henryhsu4cc6b022014-04-22 17:12:42 +0800538
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800539 self.test_list_iterator (a TestListIterator object) will determine which
540 test should be run.
henryhsu4cc6b022014-04-22 17:12:42 +0800541 """
Peter Shih658d41d2018-04-16 15:42:00 +0800542 self.ReapCompletedTests()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800543
544 if self.invocations:
545 # there are tests still running, we cannot start new tests
Vic Yangaabf9fd2013-04-09 18:56:13 +0800546 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800547
Peter Shih658d41d2018-04-16 15:42:00 +0800548 if self._CheckCriticalFactoryNote():
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800549 logging.info('has critical factory note, stop running')
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800550 self.test_list_iterator.Stop()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800551 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800552
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800553 while True:
554 try:
555 path = self.test_list_iterator.next()
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800556 test = self.test_list.LookupPath(path)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800557 except StopIteration:
558 logging.info('no next test, stop running')
Jon Salz0697cbf2012-07-04 15:14:04 +0800559 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800560
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800561 # check if we have run all required tests
Jon Salz304a75d2012-07-06 11:14:15 +0800562 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800563 for requirement in test.require_run:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800564 for i in requirement.test.Walk():
Jon Salza1412922012-07-23 16:04:17 +0800565 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800566 # We've hit this test itself; stop checking
567 break
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800568 if ((i.GetState().status == TestState.UNTESTED) or
Wei-Han Chen3a160172017-07-11 17:31:28 +0800569 (requirement.passed and
570 i.GetState().status not in [TestState.SKIPPED,
571 TestState.PASSED])):
Jon Salz304a75d2012-07-06 11:14:15 +0800572 # Found an untested test; move on to the next
573 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800574 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800575 break
576
577 if untested:
578 untested_paths = ', '.join(sorted([x.path for x in untested]))
Fei Shaoee5943d2018-10-10 09:41:41 +0800579 if self.state_instance.DataShelfGetValue('engineering_mode',
580 optional=True):
581
Jon Salz304a75d2012-07-06 11:14:15 +0800582 # In engineering mode, we'll let it go.
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800583 session.console.warn('In engineering mode; running '
Jon Salz304a75d2012-07-06 11:14:15 +0800584 '%s even though required tests '
585 '[%s] have not completed',
586 test.path, untested_paths)
587 else:
588 # Not in engineering mode; mark it failed.
589 error_msg = ('Required tests [%s] have not been run yet'
590 % untested_paths)
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800591 session.console.error('Not running %s: %s',
Jon Salz304a75d2012-07-06 11:14:15 +0800592 test.path, error_msg)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800593 test.UpdateState(status=TestState.FAILED,
594 error_msg=error_msg)
Jon Salz304a75d2012-07-06 11:14:15 +0800595 continue
596
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800597 # okay, let's run the test
Wei-Han Chen03113912017-09-29 15:58:25 +0800598 if (isinstance(test, test_object.ShutdownStep) and
Fei Shaoee5943d2018-10-10 09:41:41 +0800599 self.state_instance.DataShelfGetValue(
Wei-Han Chen29663c12017-06-27 10:28:54 +0800600 state.KEY_POST_SHUTDOWN % test.path, optional=True)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800601 # Invoking post shutdown method of shutdown test. We should retain the
602 # iterations_left and retries_left of the original test state.
Fei Shaobcaa52b2018-09-28 13:27:15 +0800603 test_state = self.state_instance.GetTestState(test.path)
Peter Shih658d41d2018-04-16 15:42:00 +0800604 self._RunTest(test, test_state.iterations_left, test_state.retries_left)
Ricky Liang48e47f92014-02-26 19:31:51 +0800605 else:
606 # Starts a new test run; reset iterations and retries.
Peter Shih658d41d2018-04-16 15:42:00 +0800607 self._RunTest(test, test.iterations, test.retries)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800608 return # to leave while
Jon Salz1acc8742012-07-17 17:45:55 +0800609
Peter Shih658d41d2018-04-16 15:42:00 +0800610 def _RunTest(self, test, iterations_left=None, retries_left=None,
611 set_layout=True):
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800612 """Invokes the test.
613
614 The argument `test` should be either a leaf test (no subtests) or a parallel
615 test (all subtests should be run in parallel).
616 """
Peter Shih2c2bf262018-01-19 15:31:39 +0800617 if (self.options.goofy_ui and not self._ui_initialized and
618 not test.IsNoHost()):
Peter Shih658d41d2018-04-16 15:42:00 +0800619 self.InitUI()
Jon Salz1acc8742012-07-17 17:45:55 +0800620
Peter Shih13d2ced2017-09-25 16:25:09 +0800621 if set_layout:
622 self.event_client.post_event(
623 Event(
624 Event.Type.SET_TEST_UI_LAYOUT,
625 layout_type=test.layout_type,
626 layout_options=test.layout_options))
627
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800628 if test.IsLeaf():
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800629 invoc = TestInvocation(
Peter Shih658d41d2018-04-16 15:42:00 +0800630 self, test, on_completion=self._InvocationCompletion,
631 on_test_failure=lambda: self._TestFail(test))
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800632 new_state = test.UpdateState(
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800633 status=TestState.ACTIVE, increment_count=1, error_msg='',
634 invocation=invoc.uuid, iterations_left=iterations_left,
Peter Shihf5c048d2017-09-01 17:57:51 +0800635 retries_left=retries_left)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800636 invoc.count = new_state.count
Peter Shih06d08212018-01-19 17:15:57 +0800637 self.invocations[invoc.uuid] = invoc
Peter Shihb2ecd552017-08-24 17:48:58 +0800638 # Send a INIT_TEST_UI event here, so the test UI are initialized in
639 # order, and the tab order would be same as test list order when there
640 # are parallel tests with UI.
641 self.event_client.post_event(
642 Event(
643 Event.Type.INIT_TEST_UI,
Peter Shihb2ecd552017-08-24 17:48:58 +0800644 test=test.path,
645 invocation=invoc.uuid))
Peter Shih658d41d2018-04-16 15:42:00 +0800646 self._CheckPlugins()
Peter Shih0d4f77a2018-01-19 16:15:49 +0800647 invoc.Start()
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800648 elif test.parallel:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800649 for subtest in test.subtests:
650 # TODO(stimim): what if the subtests *must* be run in parallel?
651 # for example, stressapptest and countdown test.
652
653 # Make sure we don't need to skip it:
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800654 if not self.test_list_iterator.CheckSkip(subtest):
Peter Shih658d41d2018-04-16 15:42:00 +0800655 self._RunTest(subtest, subtest.iterations, subtest.retries,
656 set_layout=False)
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800657 else:
658 # This should never happen, there must be something wrong.
659 # However, we can't raise an exception, otherwise goofy will be closed
660 logging.critical(
661 'Goofy should not get a non-leaf test that is not parallel: %r',
662 test)
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800663 session.console.critical(
Wei-Han Chendc3e3ba2017-07-05 16:49:09 +0800664 'Goofy should not get a non-leaf test that is not parallel: %r',
665 test)
Jon Salz5f2a0672012-05-22 17:14:06 +0800666
Peter Shih658d41d2018-04-16 15:42:00 +0800667 def Run(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800668 """Runs Goofy."""
669 # Process events forever.
Peter Shih658d41d2018-04-16 15:42:00 +0800670 while self.RunOnce(True):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800671 pass
672
Peter Shih658d41d2018-04-16 15:42:00 +0800673 def RunEnqueue(self, val):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800674 """Enqueues an object on the event loop.
675
676 Generally this is a function. It may also be None to indicate that the
677 run queue should shut down.
678 """
679 self.run_queue.put(val)
680
Peter Shih658d41d2018-04-16 15:42:00 +0800681 def RunOnce(self, block=False):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800682 """Runs all items pending in the event loop.
683
684 Args:
685 block: If true, block until at least one event is processed.
686
687 Returns:
688 True to keep going or False to shut down.
689 """
690 events = type_utils.DrainQueue(self.run_queue)
691 while not events:
692 # Nothing on the run queue.
Peter Shih658d41d2018-04-16 15:42:00 +0800693 self._RunQueueIdle()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800694 if block:
695 # Block for at least one event...
696 try:
697 events.append(self.run_queue.get(timeout=RUN_QUEUE_TIMEOUT_SECS))
698 except Queue.Empty:
Peter Shih658d41d2018-04-16 15:42:00 +0800699 # Keep going (calling _RunQueueIdle() again at the top of
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800700 # the loop)
701 continue
702 # ...and grab anything else that showed up at the same
703 # time.
704 events.extend(type_utils.DrainQueue(self.run_queue))
705 else:
706 break
707
708 for event in events:
709 if not event:
710 # Shutdown request.
711 self.run_queue.task_done()
712 return False
713
714 try:
715 event()
716 except Exception:
717 logging.exception('Error in event loop')
Peter Shih658d41d2018-04-16 15:42:00 +0800718 self._RecordExceptions(
719 traceback.format_exception_only(*sys.exc_info()[:2]))
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800720 # But keep going
721 finally:
722 self.run_queue.task_done()
723 return True
724
Peter Shih658d41d2018-04-16 15:42:00 +0800725 def _RunQueueIdle(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800726 """Invoked when the run queue has no events.
727
728 This method must not raise exception.
729 """
730 now = time.time()
731 if (self.last_idle and
732 now < (self.last_idle + RUN_QUEUE_TIMEOUT_SECS - 1)):
733 # Don't run more often than once every (RUN_QUEUE_TIMEOUT_SECS -
734 # 1) seconds.
735 return
736
737 self.last_idle = now
Peter Shih658d41d2018-04-16 15:42:00 +0800738 self._PerformPeriodicTasks()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800739
Peter Shih658d41d2018-04-16 15:42:00 +0800740 def _CheckExceptions(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800741 """Raises an error if any exceptions have occurred in
742 invocation threads.
743 """
744 if self.exceptions:
745 raise RuntimeError('Exception in invocation thread: %r' %
746 self.exceptions)
747
Peter Shih658d41d2018-04-16 15:42:00 +0800748 def _RecordExceptions(self, msg):
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800749 """Records an exception in an invocation thread.
750
751 An exception with the given message will be rethrown when
752 Goofy is destroyed.
753 """
754 self.exceptions.append(msg)
755
756 @staticmethod
Peter Shih658d41d2018-04-16 15:42:00 +0800757 def DrainNondaemonThreads():
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800758 """Wait for all non-current non-daemon threads to exit.
759
760 This is performed by the Python runtime in an atexit handler,
761 but this implementation allows us to do more detailed logging, and
762 to support control-C for abrupt shutdown.
763 """
764 cur_thread = threading.current_thread()
765 all_threads_joined = False
766 while not all_threads_joined:
767 for thread in threading.enumerate():
768 if not thread.daemon and thread.is_alive() and thread is not cur_thread:
769 logging.info("Waiting for thread '%s'...", thread.name)
770 thread.join()
771 # We break rather than continue on because the thread list
772 # may have changed while we waited
773 break
774 else:
775 # No threads remain
776 all_threads_joined = True
777 return all_threads_joined
778
779 @staticmethod
Peter Shih658d41d2018-04-16 15:42:00 +0800780 def RunMainAndExit():
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800781 """Instantiate the receiver, run its main function, and exit when done.
782
783 This static method is the "entry point" for Goofy.
784 It instantiates the receiver and invokes its main function, while
785 handling exceptions. When main() finishes (normally or via an exception),
786 it exits the process.
787 """
788 try:
789 cls = Goofy
790 goofy = cls()
791 except Exception:
792 logging.info('Failed to instantiate %s, shutting down.', cls.__name__)
793 traceback.print_exc()
794 os._exit(1) # pylint: disable=protected-access
795 sys.exit(1)
796
797 try:
Peter Shih658d41d2018-04-16 15:42:00 +0800798 goofy.Main()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800799 except SystemExit:
800 # Propagate SystemExit without logging.
801 raise
802 except KeyboardInterrupt:
803 logging.info('Interrupted, shutting down...')
804 except Exception:
805 # Log the error before trying to shut down
806 logging.exception('Error in main loop')
807 raise
808 finally:
809 try:
810 # We drain threads manually, rather than letting Python do it,
811 # so that we can report to the user which threads are stuck
Peter Shih658d41d2018-04-16 15:42:00 +0800812 goofy.Destroy()
813 cls.DrainNondaemonThreads()
Hung-Te Lina452d4d2017-10-25 17:46:14 +0800814 except (KeyboardInterrupt, Exception):
815 # We got a keyboard interrupt while attempting to shut down.
816 # The user is waiting impatiently! This can happen if threads get stuck.
817 # We need to exit via os._exit, not sys.exit, because sys.exit() will
818 # run the main thread's atexit handler, which waits for all threads to
819 # exit, which is likely how we got stuck in the first place. However, we
820 # do want to capture all logs, so we shut down logging gracefully.
821 logging.info('Graceful shutdown interrupted, shutting down abruptly')
822 logging.shutdown()
823 os._exit(1) # pylint: disable=protected-access
824 # Normal exit path
825 sys.exit(0)
826
Peter Shih658d41d2018-04-16 15:42:00 +0800827 def _CheckPlugins(self):
Earl Oua3bca122016-10-21 16:00:30 +0800828 """Check plugins to be paused or resumed."""
829 exclusive_resources = set()
Peter Shih06d08212018-01-19 17:15:57 +0800830 for invoc in self.invocations.itervalues():
Earl Oua3bca122016-10-21 16:00:30 +0800831 exclusive_resources = exclusive_resources.union(
Peter Shih06d08212018-01-19 17:15:57 +0800832 invoc.test.GetExclusiveResources())
Earl Oua3bca122016-10-21 16:00:30 +0800833 self.plugin_controller.PauseAndResumePluginByResource(exclusive_resources)
834
Peter Shih658d41d2018-04-16 15:42:00 +0800835 def _CheckForUpdates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800836 """Schedules an asynchronous check for updates if necessary."""
cychiang21886742012-07-05 15:16:32 +0800837 if not self.test_list.options.update_period_secs:
838 # Not enabled.
839 return
840
841 now = time.time()
842 if self.last_update_check and (
843 now - self.last_update_check <
844 self.test_list.options.update_period_secs):
845 # Not yet time for another check.
846 return
847
848 self.last_update_check = now
849
Peter Shih658d41d2018-04-16 15:42:00 +0800850 def _HandleCheckForUpdate(reached_server, toolkit_version, needs_update):
Hung-Te Lind151bf32017-08-30 11:05:47 +0800851 if reached_server:
You-Cheng Syud4a24bf2017-08-21 17:56:48 +0800852 new_update_toolkit_version = toolkit_version if needs_update else None
853 if self.dut.info.update_toolkit_version != new_update_toolkit_version:
854 logging.info('Received new update TOOLKIT_VERSION: %s',
855 new_update_toolkit_version)
856 self.dut.info.Overrides('update_toolkit_version',
857 new_update_toolkit_version)
Peter Shih658d41d2018-04-16 15:42:00 +0800858 self.RunEnqueue(self._UpdateSystemInfo)
You-Cheng Syud4a24bf2017-08-21 17:56:48 +0800859 elif not self._suppress_periodic_update_messages:
860 logging.warning('Suppress error messages for periodic update checking '
861 'after the first one.')
862 self._suppress_periodic_update_messages = True
cychiang21886742012-07-05 15:16:32 +0800863
864 updater.CheckForUpdateAsync(
Peter Shih658d41d2018-04-16 15:42:00 +0800865 _HandleCheckForUpdate, None, self._suppress_periodic_update_messages)
cychiang21886742012-07-05 15:16:32 +0800866
Peter Shih658d41d2018-04-16 15:42:00 +0800867 def CancelPendingTests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800868 """Cancels any tests in the run queue."""
Peter Shih658d41d2018-04-16 15:42:00 +0800869 self._RunTests(None)
Jon Salza6711d72012-07-18 14:33:03 +0800870
Peter Shih658d41d2018-04-16 15:42:00 +0800871 def _RestoreActiveRunState(self):
Ricky Liang4bff3e32014-02-20 18:46:11 +0800872 """Restores active run id and the list of scheduled tests."""
Fei Shaoee5943d2018-10-10 09:41:41 +0800873 self.run_id = self.state_instance.DataShelfGetValue('run_id', optional=True)
874 self.scheduled_run_tests = self.state_instance.DataShelfGetValue(
Ricky Liang4bff3e32014-02-20 18:46:11 +0800875 'scheduled_run_tests', optional=True)
876
Peter Shih658d41d2018-04-16 15:42:00 +0800877 def _SetActiveRunState(self):
Ricky Liang4bff3e32014-02-20 18:46:11 +0800878 """Sets active run id and the list of scheduled tests."""
879 self.run_id = str(uuid.uuid4())
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800880 # try our best to predict which tests will be run.
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800881 self.scheduled_run_tests = self.test_list_iterator.GetPendingTests()
Fei Shaoee5943d2018-10-10 09:41:41 +0800882 self.state_instance.DataShelfSetValue('run_id', self.run_id)
883 self.state_instance.DataShelfSetValue('scheduled_run_tests',
884 self.scheduled_run_tests)
Ricky Liang4bff3e32014-02-20 18:46:11 +0800885
Peter Shih658d41d2018-04-16 15:42:00 +0800886 def _RunTests(self, subtree, status_filter=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800887 """Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800888
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800889 Run tests under a given subtree.
Jon Salzb1b39092012-05-03 02:05:09 +0800890
Ricky Liang6fe218c2013-12-27 15:17:17 +0800891 Args:
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800892 subtree: root of subtree to run or None to run nothing.
Chih-Yu Huang85dc63c2015-08-12 15:21:28 +0800893 status_filter: List of available test states. Only run the tests which
894 states are in the list. Set to None if all test states are available.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800895 """
Hung-Te Lin793d6572017-09-04 18:17:56 +0800896 self.hooks.OnTestStart()
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800897 self.test_list_iterator = TestListIterator(
898 subtree, status_filter, self.test_list)
899 if subtree is not None:
Peter Shih658d41d2018-04-16 15:42:00 +0800900 self._SetActiveRunState()
901 self._RunNextTest()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800902
Peter Shih658d41d2018-04-16 15:42:00 +0800903 def ReapCompletedTests(self):
Peter Shih0d4f77a2018-01-19 16:15:49 +0800904 """Removes completed tests from the set of active tests."""
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800905 test_completed = False
Peter Shih06d08212018-01-19 17:15:57 +0800906 # Since items are removed while iterating, make a copy using values()
907 # instead of itervalues().
908 for invoc in self.invocations.values():
909 test = invoc.test
910 if invoc.IsCompleted():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800911 test_completed = True
Peter Shih06d08212018-01-19 17:15:57 +0800912 new_state = test.UpdateState(**invoc.update_state_on_completion)
913 del self.invocations[invoc.uuid]
Jon Salz0697cbf2012-07-04 15:14:04 +0800914
Johny Lin62ed2a32015-05-13 11:57:12 +0800915 # Stop on failure if flag is true and there is no retry chances.
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800916 if (self.test_list.options.stop_on_failure and
Johny Lin62ed2a32015-05-13 11:57:12 +0800917 new_state.retries_left < 0 and
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800918 new_state.status == TestState.FAILED):
919 # Clean all the tests to cause goofy to stop.
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800920 session.console.info('Stop on failure triggered. Empty the queue.')
Peter Shih658d41d2018-04-16 15:42:00 +0800921 self.CancelPendingTests()
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800922
Jon Salz1acc8742012-07-17 17:45:55 +0800923 if new_state.iterations_left and new_state.status == TestState.PASSED:
924 # Play it again, Sam!
Peter Shih658d41d2018-04-16 15:42:00 +0800925 self._RunTest(test)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800926 # new_state.retries_left is obtained after update.
927 # For retries_left == 0, test can still be run for the last time.
928 elif (new_state.retries_left >= 0 and
929 new_state.status == TestState.FAILED):
930 # Still have to retry, Sam!
Peter Shih658d41d2018-04-16 15:42:00 +0800931 self._RunTest(test)
Jon Salz1acc8742012-07-17 17:45:55 +0800932
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800933 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +0800934 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800935
Peter Shih658d41d2018-04-16 15:42:00 +0800936 def _KillActiveTests(self, abort, root=None, reason=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800937 """Kills and waits for all active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800938
Jon Salz85a39882012-07-05 16:45:04 +0800939 Args:
940 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +0800941 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +0800942 root: If set, only kills tests with root as an ancestor.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800943 reason: If set, the abort reason.
944 """
Peter Shih658d41d2018-04-16 15:42:00 +0800945 self.ReapCompletedTests()
Peter Shih06d08212018-01-19 17:15:57 +0800946 # Since items are removed while iterating, make a copy using values()
947 # instead of itervalues().
948 for invoc in self.invocations.values():
949 test = invoc.test
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800950 if root and not test.HasAncestor(root):
Jon Salz85a39882012-07-05 16:45:04 +0800951 continue
952
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800953 session.console.info('Killing active test %s...', test.path)
Peter Shih0d4f77a2018-01-19 16:15:49 +0800954 invoc.AbortAndJoin(reason)
Hung-Te Lin03f1fc22017-10-16 16:38:31 +0800955 session.console.info('Killed %s', test.path)
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800956 test.UpdateState(**invoc.update_state_on_completion)
Peter Shih06d08212018-01-19 17:15:57 +0800957 del self.invocations[invoc.uuid]
Jon Salz1acc8742012-07-17 17:45:55 +0800958
Jon Salz0697cbf2012-07-04 15:14:04 +0800959 if not abort:
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800960 test.UpdateState(status=TestState.UNTESTED)
Peter Shih658d41d2018-04-16 15:42:00 +0800961 self.ReapCompletedTests()
Jon Salz0697cbf2012-07-04 15:14:04 +0800962
Peter Shih658d41d2018-04-16 15:42:00 +0800963 def Stop(self, root=None, fail=False, reason=None):
964 self._KillActiveTests(fail, root, reason)
Wei-Han Chenc17b4112016-11-22 14:56:51 +0800965
Wei-Han Chenbcac7252017-04-21 19:46:51 +0800966 self.test_list_iterator.Stop(root)
Peter Shih658d41d2018-04-16 15:42:00 +0800967 self._RunNextTest()
Jon Salz0697cbf2012-07-04 15:14:04 +0800968
Peter Shih658d41d2018-04-16 15:42:00 +0800969 def ClearState(self, root=None):
Jon Salzd7550792013-07-12 05:49:27 +0800970 if root is None:
971 root = self.test_list
Peter Shih658d41d2018-04-16 15:42:00 +0800972 self.Stop(root, reason='Clearing test state')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +0800973 for f in root.Walk():
974 if f.IsLeaf():
975 f.UpdateState(status=TestState.UNTESTED)
Jon Salz4712ac72013-02-07 17:12:05 +0800976
Peter Shih658d41d2018-04-16 15:42:00 +0800977 def _AbortActiveTests(self, reason=None):
978 self._KillActiveTests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +0800979
Peter Shih658d41d2018-04-16 15:42:00 +0800980 def Main(self):
Jon Salzeff94182013-06-19 15:06:28 +0800981 syslog.openlog('goofy')
982
Jon Salz0697cbf2012-07-04 15:14:04 +0800983 try:
Jon Salzd7550792013-07-12 05:49:27 +0800984 self.status = Status.INITIALIZING
Peter Shih658d41d2018-04-16 15:42:00 +0800985 self.Init()
Jon Salz0697cbf2012-07-04 15:14:04 +0800986 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800987 success=True)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +0800988 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +0800989 testlog.StationInit({
Hung-Te Linda8eb992017-09-28 03:27:12 +0800990 'stationDeviceId': session.GetDeviceID(),
991 'stationInstallationId': session.GetInstallationID(),
992 'count': session.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +0800993 'success': True}))
Hung-Te Linc8174b52017-06-02 11:11:45 +0800994 except Exception:
Joel Kitching9eb203a2016-04-21 15:36:30 +0800995 try:
996 if self.event_log:
Jon Salz0697cbf2012-07-04 15:14:04 +0800997 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +0800998 success=False,
999 trace=traceback.format_exc())
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001000 if self.testlog:
1001 testlog.Log(
Joel Kitching9eb203a2016-04-21 15:36:30 +08001002 testlog.StationInit({
Hung-Te Linda8eb992017-09-28 03:27:12 +08001003 'stationDeviceId': session.GetDeviceID(),
1004 'stationInstallationId': session.GetInstallationID(),
1005 'count': session.GetInitCount(),
Joel Kitching9eb203a2016-04-21 15:36:30 +08001006 'success': False,
1007 'failureMessage': traceback.format_exc()}))
Hung-Te Linc8174b52017-06-02 11:11:45 +08001008 except Exception:
Joel Kitching9eb203a2016-04-21 15:36:30 +08001009 pass
Jon Salz0697cbf2012-07-04 15:14:04 +08001010 raise
1011
Jon Salzd7550792013-07-12 05:49:27 +08001012 self.status = Status.RUNNING
Jon Salzeff94182013-06-19 15:06:28 +08001013 syslog.syslog('Goofy (factory test harness) starting')
Chun-Ta Lin5d12b592015-06-30 00:54:23 -07001014 syslog.syslog('Boot sequence = %d' % GetBootSequence())
Hung-Te Linda8eb992017-09-28 03:27:12 +08001015 syslog.syslog('Goofy init count = %d' % session.GetInitCount())
Peter Shih658d41d2018-04-16 15:42:00 +08001016 self.Run()
Jon Salz0697cbf2012-07-04 15:14:04 +08001017
Peter Shih658d41d2018-04-16 15:42:00 +08001018 def _UpdateSystemInfo(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001019 """Updates system info."""
Shen-En Shihce8ffe02017-08-01 18:58:09 +08001020 logging.info('Received a notify to update system info.')
1021 self.dut.info.Invalidate()
1022
1023 # Propagate this notify to goofy components
1024 try:
1025 status_monitor = plugin_controller.GetPluginRPCProxy(
1026 'status_monitor.status_monitor')
1027 status_monitor.UpdateDeviceInfo()
1028 except Exception:
1029 logging.debug('Failed to update status monitor plugin.')
Jon Salz0697cbf2012-07-04 15:14:04 +08001030
Peter Shih658d41d2018-04-16 15:42:00 +08001031 def SetForceAutoRun(self):
Fei Shaoee5943d2018-10-10 09:41:41 +08001032 self.state_instance.DataShelfSetValue(TESTS_AFTER_SHUTDOWN, FORCE_AUTO_RUN)
Wei-Han Chen8d7fbc42017-10-18 19:20:47 +08001033
Peter Shih658d41d2018-04-16 15:42:00 +08001034 def UpdateFactory(self, auto_run_on_restart=False, post_update_hook=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001035 """Commences updating factory software.
Jon Salzeb42f0d2012-07-27 19:14:04 +08001036
1037 Args:
1038 auto_run_on_restart: Auto-run when the machine comes back up.
1039 post_update_hook: Code to call after update but immediately before
1040 restart.
1041
1042 Returns:
1043 Never if the update was successful (we just reboot).
1044 False if the update was unnecessary (no update available).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001045 """
Peter Shih658d41d2018-04-16 15:42:00 +08001046 self._KillActiveTests(False, reason='Factory software update')
1047 self.CancelPendingTests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001048
Peter Shih658d41d2018-04-16 15:42:00 +08001049 def PreUpdateHook():
Jon Salz5c344f62012-07-13 14:31:16 +08001050 if auto_run_on_restart:
Peter Shih658d41d2018-04-16 15:42:00 +08001051 self.SetForceAutoRun()
Marco Chen13394912018-10-11 22:00:34 +08001052 self.state_instance.Close()
Jon Salz5c344f62012-07-13 14:31:16 +08001053
Peter Shih658d41d2018-04-16 15:42:00 +08001054 if updater.TryUpdate(pre_update_hook=PreUpdateHook):
Jon Salzeb42f0d2012-07-27 19:14:04 +08001055 if post_update_hook:
1056 post_update_hook()
1057 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +08001058
Peter Shih658d41d2018-04-16 15:42:00 +08001059 def _HandleSignal(self, signum, unused_frame):
chuntsen9d675c62017-06-20 14:35:30 +08001060 names = [signame for signame in dir(signal) if signame.startswith('SIG') and
1061 getattr(signal, signame) == signum]
1062 signal_name = ', '.join(names) if names else 'UNKNOWN'
1063 logging.error('Received signal %s(%d)', signal_name, signum)
Peter Shih658d41d2018-04-16 15:42:00 +08001064 self.RunEnqueue(None)
Peter Shihbe962972018-02-23 12:46:41 +08001065 raise KeyboardInterrupt
Jon Salz77c151e2012-08-28 07:20:37 +08001066
Jon Salz128b0932013-07-03 16:55:26 +08001067 def GetTestList(self, test_list_id):
1068 """Returns the test list with the given ID.
1069
1070 Raises:
1071 TestListError: The test list ID is not valid.
1072 """
1073 try:
1074 return self.test_lists[test_list_id]
1075 except KeyError:
Peter Shihd14623e2017-11-14 15:12:54 +08001076 raise type_utils.TestListError(
Wei-Han Chendbc0fa22017-09-28 10:29:53 +08001077 '%r is not a valid test list ID (available IDs are %r)' % (
1078 test_list_id, sorted(self.test_lists.keys())))
Jon Salz128b0932013-07-03 16:55:26 +08001079
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001080 def _RecordStartError(self, error_message):
1081 """Appends the startup error message into the shared data."""
1082 KEY = 'startup_error'
Fei Shaoee5943d2018-10-10 09:41:41 +08001083 data = self.state_instance.DataShelfGetValue(KEY, optional=True)
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001084 new_data = '%s\n\n%s' % (data, error_message) if data else error_message
Fei Shaoee5943d2018-10-10 09:41:41 +08001085 self.state_instance.DataShelfSetValue(KEY, new_data)
Chih-Yu Huang1725d622017-03-24 16:08:35 +08001086
Peter Shih658d41d2018-04-16 15:42:00 +08001087 def _InitTestLists(self):
Joel Kitching50a63ea2016-02-22 13:15:09 +08001088 """Reads in all test lists and sets the active test list.
1089
1090 Returns:
1091 True if the active test list could be set, False if failed.
1092 """
Joel Kitching50a63ea2016-02-22 13:15:09 +08001093 try:
Yong Hongf879cbd2018-11-08 16:06:52 +08001094 startup_errors = []
Jon Salz128b0932013-07-03 16:55:26 +08001095
Yong Hong5ec7a7a2018-11-08 17:00:32 +08001096 self.test_lists, failed_test_lists = (
1097 self.test_list_manager.BuildAllTestLists())
Yong Hongf879cbd2018-11-08 16:06:52 +08001098
1099 logging.info('Loaded test lists: %r', sorted(self.test_lists.keys()))
1100
1101 # Check for any syntax errors in test list files.
Yong Hong5ec7a7a2018-11-08 17:00:32 +08001102 if failed_test_lists:
1103 logging.info('Failed test list IDs: [%s]',
1104 ' '.join(failed_test_lists.keys()))
1105 for test_list_id, reason in failed_test_lists.iteritems():
1106 logging.error('Error in test list %s: %s', test_list_id, reason)
1107 startup_errors.append('Error in test list %s:\n%s'
1108 % (test_list_id, reason))
Yong Hongf879cbd2018-11-08 16:06:52 +08001109
1110 active_test_list = self.test_list_manager.GetActiveTestListId()
1111
1112 # Check for a non-existent test list ID.
1113 try:
1114 self.test_list = self.GetTestList(active_test_list)
1115 logging.info('Active test list: %s', self.test_list.test_list_id)
1116 except type_utils.TestListError as e:
1117 logging.exception('Invalid active test list: %s', active_test_list)
1118 startup_errors.append(e.message)
1119
1120 # Show all startup errors.
1121 if startup_errors:
1122 self._RecordStartError('\n\n'.join(startup_errors))
1123
1124 except Exception:
1125 logging.exception('Unable to initialize test lists')
1126 self._RecordStartError(
1127 'Unable to initialize test lists\n%s' % traceback.format_exc())
1128
1129 success = bool(self.test_list)
1130 if not success:
1131 # Create an empty test list with default options so that the rest of
1132 # startup can proceed.
1133 # A message box will pop up in UI for the error details.
1134 self.test_list = manager.DummyTestList(self.test_list_manager)
1135
1136 # After SKU ID is updated and DUT is reboot, test list might be switched
1137 # because model name is changed too. In this case, shared state should be
1138 # cleared; otherwise shared data like TESTS_AFTER_SHUTDOWN prevents tests
1139 # from running automatically.
1140 previous_id = self.state_instance.DataShelfGetValue(ACTIVE_TEST_LIST_ID,
1141 optional=True)
1142 if previous_id != self.test_list.test_list_id:
1143 logging.info('Test list is changed from %s to %s.',
1144 previous_id, self.test_list.test_list_id)
1145 if previous_id:
1146 self._ResetStateInstance()
1147
1148 self.state_instance.DataShelfSetValue(ACTIVE_TEST_LIST_ID,
1149 self.test_list.test_list_id)
1150
1151 self.test_list.state_instance = self.state_instance
Joel Kitching50a63ea2016-02-22 13:15:09 +08001152
1153 # Only return False if failed to load the active test list.
Yong Hongf879cbd2018-11-08 16:06:52 +08001154 return success
Jon Salz128b0932013-07-03 16:55:26 +08001155
Peter Shih658d41d2018-04-16 15:42:00 +08001156 def _InitHooks(self):
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001157 """Initializes hooks.
1158
1159 Must run after self.test_list ready.
1160 """
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001161 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1162 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
Wei-Han Chen1a114682017-10-02 10:33:54 +08001163 assert isinstance(self.hooks, hooks.Hooks), (
Ricky Liang45c73e72015-01-15 15:00:30 +08001164 'hooks should be of type Hooks but is %r' % type(self.hooks))
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001165 self.hooks.test_list = self.test_list
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001166 self.hooks.OnCreatedTestList()
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001167
Peter Shih658d41d2018-04-16 15:42:00 +08001168 def InitUI(self):
Vic Yanga3cecf82014-12-26 00:44:21 -08001169 """Initialize UI."""
Shen-En Shih65619f42017-10-02 16:52:10 +08001170 logging.info('Waiting for a web socket connection')
1171 self.web_socket_manager.wait()
Vic Yanga3cecf82014-12-26 00:44:21 -08001172 self._ui_initialized = True
Vic Yanga3cecf82014-12-26 00:44:21 -08001173
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001174 @staticmethod
1175 def GetCommandLineArgsParser():
1176 """Returns a parser for Goofy command line arguments."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001177 parser = OptionParser()
1178 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001179 action='store_true',
1180 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001181 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001182 action='store_true',
1183 help='Clear all test state')
Peter Shih2c2bf262018-01-19 15:31:39 +08001184 parser.add_option('--no-goofy-ui', dest='goofy_ui',
1185 action='store_false', default=True,
1186 help='start without Goofy UI')
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001187 return parser
1188
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001189 def _PrepareDUTLink(self):
1190 # TODO(akahuang): Move this part into a pytest.
1191 # Prepare DUT link after the plugins start running, because the link might
1192 # need the network connection.
1193
1194 dut_options = self.test_list.options.dut_options
1195 if dut_options:
1196 logging.info('dut_options set by %s: %r', self.test_list.test_list_id,
1197 self.test_list.options.dut_options)
1198
Peter Shih658d41d2018-04-16 15:42:00 +08001199 def PrepareLink():
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001200 try:
1201 device_utils.PrepareDUTLink(**dut_options)
1202 except Exception:
1203 logging.exception('Unable to prepare DUT link.')
1204
Peter Shihdb06b092018-04-16 16:55:38 +08001205 process_utils.StartDaemonThread(target=PrepareLink)
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001206
Peter Shih658d41d2018-04-16 15:42:00 +08001207 def Init(self, args=None, env=None):
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001208 """Initializes Goofy.
1209
1210 Args:
Shen-En Shihe05cbbc2017-10-02 16:47:30 +08001211 args: A list of command-line arguments. Uses sys.argv if args is None.
1212 env: An Environment instance to use (or None to use DUTEnvironment).
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001213 """
1214 (self.options, self.args) = self.GetCommandLineArgsParser().parse_args(args)
Jon Salz0697cbf2012-07-04 15:14:04 +08001215
Peter Shih658d41d2018-04-16 15:42:00 +08001216 signal.signal(signal.SIGINT, self._HandleSignal)
1217 signal.signal(signal.SIGTERM, self._HandleSignal)
Hung-Te Lina846f602014-07-04 20:32:22 +08001218 # TODO(hungte) SIGTERM does not work properly without Telemetry and should
1219 # be fixed.
Hung-Te Lina846f602014-07-04 20:32:22 +08001220
Jon Salz46b89562012-07-05 11:49:22 +08001221 # Make sure factory directories exist.
Peter Shihb4e49352017-05-25 17:35:11 +08001222 for path in [
1223 paths.DATA_LOG_DIR, paths.DATA_STATE_DIR, paths.DATA_TESTS_DIR]:
1224 file_utils.TryMakeDirs(path)
Jon Salz46b89562012-07-05 11:49:22 +08001225
Wei-Han Chen78f35f62017-03-06 20:11:20 +08001226 try:
1227 goofy_default_options = config_utils.LoadConfig(validate_schema=False)
1228 for key, value in goofy_default_options.iteritems():
1229 if getattr(self.options, key, None) is None:
1230 logging.info('self.options.%s = %r', key, value)
1231 setattr(self.options, key, value)
1232 except Exception:
1233 logging.exception('failed to load goofy overriding options')
1234
Jon Salzee85d522012-07-17 14:34:46 +08001235 event_log.IncrementBootSequence()
Hung-Te Linda8eb992017-09-28 03:27:12 +08001236 session.IncrementInitCount()
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001237
Jon Salzd15bbcf2013-05-21 17:33:57 +08001238 # Don't defer logging the initial event, so we can make sure
1239 # that device_id, reimage_id, etc. are all set up.
1240 self.event_log = EventLog('goofy', defer=False)
Chun-Ta Lin53cbbd52016-06-08 21:42:19 +08001241 self.testlog = testlog.Testlog(
Peter Shihb4e49352017-05-25 17:35:11 +08001242 log_root=paths.DATA_LOG_DIR, uuid=self.uuid,
Hung-Te Linda8eb992017-09-28 03:27:12 +08001243 stationDeviceId=session.GetDeviceID(),
1244 stationInstallationId=session.GetInstallationID())
Jon Salz0697cbf2012-07-04 15:14:04 +08001245
Jon Salz0697cbf2012-07-04 15:14:04 +08001246 if env:
1247 self.env = env
Shen-En Shihe05cbbc2017-10-02 16:47:30 +08001248 else:
Ricky Liang09d66d82014-09-25 11:20:54 +08001249 self.env = test_environment.DUTEnvironment()
Jon Salz0697cbf2012-07-04 15:14:04 +08001250 self.env.goofy = self
1251
1252 if self.options.restart:
Fei Shaobcaa52b2018-09-28 13:27:15 +08001253 state.ClearState()
Jon Salz0697cbf2012-07-04 15:14:04 +08001254
Peter Shih658d41d2018-04-16 15:42:00 +08001255 self._InitGoofyServer()
Peter Shihf821c6a2018-03-05 13:31:22 +08001256 # Both the i18n file and index.html should be registered to Goofy before we
1257 # start the Goofy server, to avoid race condition that Goofy would return
1258 # 404 not found before index.html is registered.
Peter Shih658d41d2018-04-16 15:42:00 +08001259 self._InitI18n()
1260 self._InitStaticFiles()
Peter Shihf821c6a2018-03-05 13:31:22 +08001261
1262 logging.info('Starting goofy server')
1263 self.goofy_server_thread.start()
1264
Marco Chena20766e2018-09-27 18:29:35 +08001265 self._InitStateInstance()
Marco Chena20766e2018-09-27 18:29:35 +08001266
Yong Hongf879cbd2018-11-08 16:06:52 +08001267 # _InitTestLists might reset the state_instance, all initializations which
1268 # rely on the state_instance need to be done after this step.
1269 success = self._InitTestLists()
1270
1271 self._InitGoofyRPC()
Jon Salzb19ea072013-02-07 16:35:00 +08001272
Peter Shih658d41d2018-04-16 15:42:00 +08001273 self._InitHooks()
chuntsen6780ea22017-12-12 14:53:53 +08001274 self.testlog.init_hooks(self.test_list.options.testlog_hooks)
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001275
Jon Salz822838b2013-03-25 17:32:33 +08001276 if self.test_list.options.clear_state_on_start:
Wei-Han Chendcecbea2018-03-14 19:00:23 +08001277 # TODO(stimim): Perhaps we should check if we are running `shutdown` test?
Fei Shaobcaa52b2018-09-28 13:27:15 +08001278 self.state_instance.ClearTestState()
Jon Salz822838b2013-03-25 17:32:33 +08001279
Jon Salz670ce062014-05-16 15:53:50 +08001280 # If the phase is invalid, this will raise a ValueError.
1281 phase.SetPersistentPhase(self.test_list.options.phase)
1282
Fei Shaoee5943d2018-10-10 09:41:41 +08001283 if not self.state_instance.DataShelfHasKey('ui_locale'):
Hung-Te Lin134403c2017-08-23 17:30:17 +08001284 ui_locale = self.test_list.options.ui_locale
Fei Shaoee5943d2018-10-10 09:41:41 +08001285 self.state_instance.DataShelfSetValue('ui_locale', ui_locale)
1286 self.state_instance.DataShelfSetValue(
Ricky Liang45c73e72015-01-15 15:00:30 +08001287 'test_list_options',
Peter Shih90425db2017-08-02 15:53:48 +08001288 self.test_list.options.ToDict())
Jon Salz0697cbf2012-07-04 15:14:04 +08001289 self.state_instance.test_list = self.test_list
1290
Peter Shih658d41d2018-04-16 15:42:00 +08001291 self._InitStates()
1292 self._StartEventServer()
Hung-Te Lincc41d2a2014-10-29 13:35:20 +08001293
Earl Oua3bca122016-10-21 16:00:30 +08001294 # Load and run Goofy plugins.
1295 self.plugin_controller = plugin_controller.PluginController(
1296 self.test_list.options.plugin_config_name, self)
1297 self.plugin_controller.StartAllPlugins()
1298
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001299 if success:
Shen-En Shih8227d5d2018-02-06 19:45:39 +08001300 self._PrepareDUTLink()
Chih-Yu Huang97103ae2017-03-20 18:22:54 +08001301
Jon Salz0697cbf2012-07-04 15:14:04 +08001302 # Note that we create a log watcher even if
1303 # sync_event_log_period_secs isn't set (no background
1304 # syncing), since we may use it to flush event logs as well.
1305 self.log_watcher = EventLogWatcher(
Ricky Liang45c73e72015-01-15 15:00:30 +08001306 self.test_list.options.sync_event_log_period_secs,
1307 event_log_db_file=None,
Peter Shih658d41d2018-04-16 15:42:00 +08001308 handle_event_logs_callback=self._HandleEventLogs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001309 if self.test_list.options.sync_event_log_period_secs:
1310 self.log_watcher.StartWatchThread()
1311
Shen-En Shihf4ad32f2017-07-31 15:56:39 +08001312 self.event_client.post_event(
1313 Event(Event.Type.UPDATE_SYSTEM_INFO))
Jon Salz0697cbf2012-07-04 15:14:04 +08001314
1315 os.environ['CROS_FACTORY'] = '1'
1316 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1317
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001318 # Should not move earlier.
1319 self.hooks.OnStartup()
1320
Ricky Liang36512a32014-07-25 11:47:04 +08001321 # Only after this point the Goofy backend is ready for UI connection.
1322 self.ready_for_ui_connection = True
1323
Jon Salz0697cbf2012-07-04 15:14:04 +08001324 def state_change_callback(test, test_state):
1325 self.event_client.post_event(
Peter Shihd46c5fd2017-09-22 15:28:42 +08001326 Event(
1327 Event.Type.STATE_CHANGE,
1328 path=test.path,
1329 state=test_state.ToStruct()))
Jon Salz0697cbf2012-07-04 15:14:04 +08001330 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001331
Vic Yange2c76a82014-10-30 12:48:19 -07001332 self.pytest_prespawner = prespawner.PytestPrespawner()
1333 self.pytest_prespawner.start()
Jon Salza6711d72012-07-18 14:33:03 +08001334
Fei Shaoee5943d2018-10-10 09:41:41 +08001335 tests_after_shutdown = self.state_instance.DataShelfGetValue(
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001336 TESTS_AFTER_SHUTDOWN, optional=True)
Jon Salz5c344f62012-07-13 14:31:16 +08001337 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001338
Jon Salz5c344f62012-07-13 14:31:16 +08001339 if not force_auto_run and tests_after_shutdown is not None:
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001340 logging.info('Resuming tests after shutdown: %r', tests_after_shutdown)
1341 self.test_list_iterator = tests_after_shutdown
Wei-Han Chenbcac7252017-04-21 19:46:51 +08001342 self.test_list_iterator.SetTestList(self.test_list)
Peter Shih658d41d2018-04-16 15:42:00 +08001343 self.RunEnqueue(self._RunNextTest)
Wei-Han Chenc17b4112016-11-22 14:56:51 +08001344 elif force_auto_run or self.test_list.options.auto_run_on_start:
Peter Shih53323922018-01-02 15:02:21 +08001345 status_filter = [TestState.UNTESTED]
1346 if self.test_list.options.retry_failed_on_start:
1347 status_filter.append(TestState.FAILED)
Peter Shih658d41d2018-04-16 15:42:00 +08001348 self.RunEnqueue(lambda: self._RunTests(self.test_list, status_filter))
Fei Shaoee5943d2018-10-10 09:41:41 +08001349 self.state_instance.DataShelfSetValue(TESTS_AFTER_SHUTDOWN, None)
Peter Shih658d41d2018-04-16 15:42:00 +08001350 self._RestoreActiveRunState()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001351
Hung-Te Lin793d6572017-09-04 18:17:56 +08001352 self.hooks.OnTestStart()
Vic Yang08505c72015-01-06 17:01:53 -08001353
Peter Shih658d41d2018-04-16 15:42:00 +08001354 def _PerformPeriodicTasks(self):
Hung-Te Lina452d4d2017-10-25 17:46:14 +08001355 """Perform any periodic work.
Vic Yang4953fc12012-07-26 16:19:53 +08001356
Peter Ammon1e1ec572014-06-26 17:56:32 -07001357 This method must not raise exceptions.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001358 """
Peter Shih658d41d2018-04-16 15:42:00 +08001359 self._CheckPlugins()
1360 self._CheckForUpdates()
Jon Salz57717ca2012-04-04 16:47:25 +08001361
Peter Shih658d41d2018-04-16 15:42:00 +08001362 def _HandleEventLogs(self, chunks, periodic=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001363 """Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001364
Hung-Te Lind151bf32017-08-30 11:05:47 +08001365 Attempts to upload the event logs to the factory server.
Vic Yang93027612013-05-06 02:42:49 +08001366
1367 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001368 chunks: A list of Chunk objects.
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001369 periodic: This event log handling is periodic. Error messages
1370 will only be shown for the first time.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001371 """
Vic Yang93027612013-05-06 02:42:49 +08001372 first_exception = None
1373 exception_count = 0
1374
Jon Salzd15bbcf2013-05-21 17:33:57 +08001375 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001376 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001377 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001378 start_time = time.time()
Peter Shihfdc09c72018-08-06 17:04:49 +08001379 proxy = server_proxy.GetServerProxy()
Hung-Te Lind151bf32017-08-30 11:05:47 +08001380 proxy.UploadEvent(
1381 chunk.log_name + '.' + event_log.GetReimageId(),
1382 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001383 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +08001384 'Successfully synced %s in %.03f s',
1385 description, time.time() - start_time)
Hung-Te Linc8174b52017-06-02 11:11:45 +08001386 except Exception:
Hung-Te Linf707b242016-01-08 23:11:42 +08001387 first_exception = (first_exception or
1388 (chunk.log_name + ': ' +
1389 debug_utils.FormatExceptionOnly()))
Vic Yang93027612013-05-06 02:42:49 +08001390 exception_count += 1
1391
1392 if exception_count:
1393 if exception_count == 1:
1394 msg = 'Log upload failed: %s' % first_exception
1395 else:
1396 msg = '%d log upload failed; first is: %s' % (
1397 exception_count, first_exception)
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001398 # For periodic event log syncing, only show the first error messages.
1399 if periodic:
1400 if not self._suppress_event_log_error_messages:
1401 self._suppress_event_log_error_messages = True
Hung-Te Lind151bf32017-08-30 11:05:47 +08001402 logging.warning('Suppress periodic factory server error messages for '
Cheng-Yi Chiangf5b21012015-03-17 15:37:14 +08001403 'event log syncing after the first one.')
1404 raise Exception(msg)
1405 # For event log syncing by request, show the error messages.
1406 else:
1407 raise Exception(msg)
Vic Yang93027612013-05-06 02:42:49 +08001408
Peter Shih658d41d2018-04-16 15:42:00 +08001409 def _RunTestsWithStatus(self, statuses_to_run, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001410 """Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001411
Jon Salz0697cbf2012-07-04 15:14:04 +08001412 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001413
Jon Salz0697cbf2012-07-04 15:14:04 +08001414 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001415 statuses_to_run: The particular status that caller wants to run.
Jon Salz0697cbf2012-07-04 15:14:04 +08001416 starting_at: If provided, only auto-runs tests beginning with
1417 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001418 root: The root of tests to run. If not provided, it will be
1419 the root of all tests.
1420 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001421 root = root or self.test_list
Peter Shih658d41d2018-04-16 15:42:00 +08001422 self._AbortActiveTests('Operator requested run/re-run of certain tests')
1423 self._RunTests(root, status_filter=statuses_to_run)
Jon Salz0405ab52012-03-16 15:26:52 +08001424
Peter Shih658d41d2018-04-16 15:42:00 +08001425 def RestartTests(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001426 """Restarts all tests."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001427 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001428
Peter Shih658d41d2018-04-16 15:42:00 +08001429 self._AbortActiveTests('Operator requested restart of certain tests')
Wei-Han Chen3ae204c2017-04-28 19:36:55 +08001430 for test in root.Walk():
1431 test.UpdateState(status=TestState.UNTESTED)
Peter Shih658d41d2018-04-16 15:42:00 +08001432 self._RunTests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001433
Peter Shih658d41d2018-04-16 15:42:00 +08001434 def _AutoRun(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001435 """"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001436
Jon Salz0697cbf2012-07-04 15:14:04 +08001437 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001438 root: If provided, the root of tests to run. If not provided, the root
1439 will be test_list (root of all tests).
1440 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001441 root = root or self.test_list
Peter Shih658d41d2018-04-16 15:42:00 +08001442 self._RunTestsWithStatus([TestState.UNTESTED, TestState.ACTIVE], root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001443
Peter Shih658d41d2018-04-16 15:42:00 +08001444 def Wait(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001445 """Waits for all pending invocations.
Jon Salz0697cbf2012-07-04 15:14:04 +08001446
1447 Useful for testing.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001448 """
Jon Salz1acc8742012-07-17 17:45:55 +08001449 while self.invocations:
Peter Shih06d08212018-01-19 17:15:57 +08001450 for invoc in self.invocations.itervalues():
1451 logging.info('Waiting for %s to complete...', invoc.test)
1452 invoc.thread.join()
Peter Shih658d41d2018-04-16 15:42:00 +08001453 self.ReapCompletedTests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001454
Peter Shih658d41d2018-04-16 15:42:00 +08001455 def _TestFail(self, test):
Hung-Te Lin793d6572017-09-04 18:17:56 +08001456 self.hooks.OnTestFailure(test)
Claire Changd1961a22015-08-05 16:15:55 +08001457
Wei-Han Chenced08ef2016-11-08 09:40:02 +08001458
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001459def main():
1460 # Logging should be solved first.
1461 (options, unused_args) = Goofy.GetCommandLineArgsParser().parse_args()
Hung-Te Lin8fc0d652017-09-21 13:05:44 +08001462 log_utils.InitLogging(verbose=options.verbose)
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001463
Peter Shih658d41d2018-04-16 15:42:00 +08001464 Goofy.RunMainAndExit()
Hung-Te Lin9d81ac72017-09-21 12:58:01 +08001465
1466
1467if __name__ == '__main__':
1468 main()