blob: 45b05510b2cc4043905c0bce36ec310058b125dd [file] [log] [blame]
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001#!/usr/bin/python -u
Hung-Te Linf2f78f72012-02-08 19:27:11 +08002# -*- coding: utf-8 -*-
3#
Jon Salz37eccbd2012-05-25 16:06:52 +08004# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Hung-Te Linf2f78f72012-02-08 19:27:11 +08005# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08008"""The main factory flow that runs the factory test and finalizes a device."""
Hung-Te Linf2f78f72012-02-08 19:27:11 +08009
Joel Kitchingb85ed7f2014-10-08 18:24:39 +080010from __future__ import print_function
11
Jon Salze12c2b32013-06-25 16:24:34 +080012import glob
Jon Salz0405ab52012-03-16 15:26:52 +080013import logging
14import os
Jon Salze12c2b32013-06-25 16:24:34 +080015import shutil
Jon Salz77c151e2012-08-28 07:20:37 +080016import signal
Jon Salz0405ab52012-03-16 15:26:52 +080017import sys
Jon Salzeff94182013-06-19 15:06:28 +080018import syslog
Jon Salz0405ab52012-03-16 15:26:52 +080019import threading
20import time
21import traceback
Jon Salz258a40c2012-04-19 12:34:01 +080022import uuid
Jon Salzb10cf512012-08-09 17:29:21 +080023from xmlrpclib import Binary
Hung-Te Linf2f78f72012-02-08 19:27:11 +080024from collections import deque
25from optparse import OptionParser
Hung-Te Linf2f78f72012-02-08 19:27:11 +080026
Jon Salz0697cbf2012-07-04 15:14:04 +080027import factory_common # pylint: disable=W0611
Hung-Te Lin91492a12014-11-25 18:56:30 +080028from cros.factory.test import event_log
jcliangcd688182012-08-20 21:01:26 +080029from cros.factory import system
Hung-Te Lin91492a12014-11-25 18:56:30 +080030from cros.factory.test.event_log import EventLog, FloatDigit
Hung-Te Lincc41d2a2014-10-29 13:35:20 +080031from cros.factory.goofy import connection_manager
Vic Yangd80ea752014-09-24 16:07:14 +080032from cros.factory.goofy import test_environment
33from cros.factory.goofy import time_sanitizer
34from cros.factory.goofy import updater
35from cros.factory.goofy.goofy_base import GoofyBase
36from cros.factory.goofy.goofy_rpc import GoofyRPC
37from cros.factory.goofy.invocation import TestArgEnv
38from cros.factory.goofy.invocation import TestInvocation
39from cros.factory.goofy.link_manager import PresenterLinkManager
Vic Yange2c76a82014-10-30 12:48:19 -070040from cros.factory.goofy import prespawner
Vic Yangd80ea752014-09-24 16:07:14 +080041from cros.factory.goofy.system_log_manager import SystemLogManager
42from cros.factory.goofy.web_socket_manager import WebSocketManager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +080043from cros.factory.system.board import Board, BoardException
jcliangcd688182012-08-20 21:01:26 +080044from cros.factory.system.charge_manager import ChargeManager
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +080045from cros.factory.system.core_dump_manager import CoreDumpManager
Jon Salzce6a7f82013-06-10 18:22:54 +080046from cros.factory.system.cpufreq_manager import CpufreqManager
Jon Salzb92c5112012-09-21 15:40:11 +080047from cros.factory.system import disk_space
jcliangcd688182012-08-20 21:01:26 +080048from cros.factory.test import factory
Jon Salz670ce062014-05-16 15:53:50 +080049from cros.factory.test import phase
jcliangcd688182012-08-20 21:01:26 +080050from cros.factory.test import state
Jon Salz51528e12012-07-02 18:54:45 +080051from cros.factory.test import shopfloor
Jon Salz83591782012-06-26 11:09:58 +080052from cros.factory.test import utils
Jon Salz128b0932013-07-03 16:55:26 +080053from cros.factory.test.test_lists import test_lists
Ricky Liang6fe218c2013-12-27 15:17:17 +080054from cros.factory.test.e2e_test.common import (
55 AutomationMode, AutomationModePrompt, ParseAutomationMode)
Jon Salz83591782012-06-26 11:09:58 +080056from cros.factory.test.event import Event
57from cros.factory.test.event import EventClient
58from cros.factory.test.event import EventServer
Hung-Te Lin91492a12014-11-25 18:56:30 +080059from cros.factory.test.event_log_watcher import EventLogWatcher
jcliangcd688182012-08-20 21:01:26 +080060from cros.factory.test.factory import TestState
Jon Salzd7550792013-07-12 05:49:27 +080061from cros.factory.test.utils import Enum
Dean Liao592e4d52013-01-10 20:06:39 +080062from cros.factory.tools.key_filter import KeyFilter
Jon Salz2af235d2013-06-24 14:47:21 +080063from cros.factory.utils import file_utils
Joel Kitchingb85ed7f2014-10-08 18:24:39 +080064from cros.factory.utils import net_utils
Jon Salz78c32392012-07-25 14:18:29 +080065from cros.factory.utils.process_utils import Spawn
Hung-Te Linf2f78f72012-02-08 19:27:11 +080066
67
Hung-Te Linf2f78f72012-02-08 19:27:11 +080068HWID_CFG_PATH = '/usr/local/share/chromeos-hwid/cfg'
Ricky Liang45c73e72015-01-15 15:00:30 +080069CACHES_DIR = os.path.join(factory.get_state_root(), 'caches')
Hung-Te Linf2f78f72012-02-08 19:27:11 +080070
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +080071CLEANUP_LOGS_PAUSED = '/var/lib/cleanup_logs_paused'
72
Jon Salz5c344f62012-07-13 14:31:16 +080073# Value for tests_after_shutdown that forces auto-run (e.g., after
74# a factory update, when the available set of tests might change).
75FORCE_AUTO_RUN = 'force_auto_run'
76
Justin Chuang83813982013-05-13 01:26:32 +080077# Sync disks when battery level is higher than this value.
78# Otherwise, power loss during disk sync operation may incur even worse outcome.
79MIN_BATTERY_LEVEL_FOR_DISK_SYNC = 1.0
80
Ricky Liang45c73e72015-01-15 15:00:30 +080081MAX_CRASH_FILE_SIZE = 64 * 1024
Jon Salze12c2b32013-06-25 16:24:34 +080082
Jon Salzd7550792013-07-12 05:49:27 +080083Status = Enum(['UNINITIALIZED', 'INITIALIZING', 'RUNNING',
84 'TERMINATING', 'TERMINATED'])
85
Ricky Liang45c73e72015-01-15 15:00:30 +080086
Hung-Te Linf2f78f72012-02-08 19:27:11 +080087def get_hwid_cfg():
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +080088 """Returns the HWID config tag, or an empty string if none can be found."""
Jon Salz0697cbf2012-07-04 15:14:04 +080089 if 'CROS_HWID' in os.environ:
90 return os.environ['CROS_HWID']
91 if os.path.exists(HWID_CFG_PATH):
Ricky Liang45c73e72015-01-15 15:00:30 +080092 with open(HWID_CFG_PATH, 'r') as hwid_cfg_handle:
Jon Salz0697cbf2012-07-04 15:14:04 +080093 return hwid_cfg_handle.read().strip()
94 return ''
Hung-Te Linf2f78f72012-02-08 19:27:11 +080095
96
Jon Salz73e0fd02012-04-04 11:46:38 +080097_inited_logging = False
Hung-Te Linf2f78f72012-02-08 19:27:11 +080098
Ricky Liang45c73e72015-01-15 15:00:30 +080099
Peter Ammon1e1ec572014-06-26 17:56:32 -0700100class Goofy(GoofyBase):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800101 """The main factory flow.
Jon Salz0697cbf2012-07-04 15:14:04 +0800102
103 Note that all methods in this class must be invoked from the main
104 (event) thread. Other threads, such as callbacks and TestInvocation
105 methods, should instead post events on the run queue.
106
107 TODO: Unit tests. (chrome-os-partner:7409)
108
109 Properties:
110 uuid: A unique UUID for this invocation of Goofy.
111 state_instance: An instance of FactoryState.
112 state_server: The FactoryState XML/RPC server.
113 state_server_thread: A thread running state_server.
114 event_server: The EventServer socket server.
115 event_server_thread: A thread running event_server.
116 event_client: A client to the event server.
117 connection_manager: The connection_manager object.
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800118 system_log_manager: The SystemLogManager object.
119 core_dump_manager: The CoreDumpManager object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800120 ui_process: The factory ui process object.
Jon Salz0697cbf2012-07-04 15:14:04 +0800121 invocations: A map from FactoryTest objects to the corresponding
122 TestInvocations objects representing active tests.
123 tests_to_run: A deque of tests that should be run when the current
124 test(s) complete.
125 options: Command-line options.
126 args: Command-line args.
127 test_list: The test list.
Jon Salz128b0932013-07-03 16:55:26 +0800128 test_lists: All new-style test lists.
Ricky Liang4bff3e32014-02-20 18:46:11 +0800129 run_id: The identifier for latest test run.
130 scheduled_run_tests: The list of tests scheduled for latest test run.
Jon Salz0697cbf2012-07-04 15:14:04 +0800131 event_handlers: Map of Event.Type to the method used to handle that
132 event. If the method has an 'event' argument, the event is passed
133 to the handler.
Jon Salz3c493bb2013-02-07 17:24:58 +0800134 last_log_disk_space_message: The last message we logged about disk space
135 (to avoid duplication).
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800136 last_kick_sync_time: The last time to kick system_log_manager to sync
137 because of core dump files (to avoid kicking too soon then abort the
138 sync.)
Jon Salz416f9cc2013-05-10 18:32:50 +0800139 hooks: A Hooks object containing hooks for various Goofy actions.
Jon Salzd7550792013-07-12 05:49:27 +0800140 status: The current Goofy status (a member of the Status enum).
Peter Ammon948b7172014-07-15 12:43:06 -0700141 link_manager: Instance of PresenterLinkManager for communicating
142 with GoofyPresenter
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800143 """
Ricky Liang45c73e72015-01-15 15:00:30 +0800144
Jon Salz0697cbf2012-07-04 15:14:04 +0800145 def __init__(self):
Peter Ammon1e1ec572014-06-26 17:56:32 -0700146 super(Goofy, self).__init__()
Jon Salz0697cbf2012-07-04 15:14:04 +0800147 self.uuid = str(uuid.uuid4())
148 self.state_instance = None
149 self.state_server = None
150 self.state_server_thread = None
Jon Salz16d10542012-07-23 12:18:45 +0800151 self.goofy_rpc = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800152 self.event_server = None
153 self.event_server_thread = None
154 self.event_client = None
155 self.connection_manager = None
Vic Yang4953fc12012-07-26 16:19:53 +0800156 self.charge_manager = None
Dean Liao88b93192014-10-23 19:37:41 +0800157 self._can_charge = True
Jon Salz8fa8e832012-07-13 19:04:09 +0800158 self.time_sanitizer = None
159 self.time_synced = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800160 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800161 self.system_log_manager = None
Cheng-Yi Chiang835f2682013-05-06 22:15:48 +0800162 self.core_dump_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800163 self.event_log = None
Vic Yange2c76a82014-10-30 12:48:19 -0700164 self.autotest_prespawner = None
165 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800166 self.ui_process = None
Vic Yanga3cecf82014-12-26 00:44:21 -0800167 self._ui_initialized = False
Jon Salzc79a9982012-08-30 04:42:01 +0800168 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800169 self.invocations = {}
170 self.tests_to_run = deque()
171 self.visible_test = None
172 self.chrome = None
Jon Salz416f9cc2013-05-10 18:32:50 +0800173 self.hooks = None
Vic Yangd8990da2013-06-27 16:57:43 +0800174 self.cpu_usage_watcher = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800175
176 self.options = None
177 self.args = None
178 self.test_list = None
Jon Salz128b0932013-07-03 16:55:26 +0800179 self.test_lists = None
Ricky Liang4bff3e32014-02-20 18:46:11 +0800180 self.run_id = None
181 self.scheduled_run_tests = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800182 self.env = None
Jon Salzb22d1172012-08-06 10:38:57 +0800183 self.last_idle = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800184 self.last_shutdown_time = None
cychiang21886742012-07-05 15:16:32 +0800185 self.last_update_check = None
Jon Salz8fa8e832012-07-13 19:04:09 +0800186 self.last_sync_time = None
Jon Salzb92c5112012-09-21 15:40:11 +0800187 self.last_log_disk_space_time = None
Jon Salz3c493bb2013-02-07 17:24:58 +0800188 self.last_log_disk_space_message = None
Justin Chuang83813982013-05-13 01:26:32 +0800189 self.last_check_battery_time = None
190 self.last_check_battery_message = None
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +0800191 self.last_kick_sync_time = None
Vic Yang311ddb82012-09-26 12:08:28 +0800192 self.exclusive_items = set()
Jon Salz0f996602012-10-03 15:26:48 +0800193 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800194 self.key_filter = None
Jon Salzce6a7f82013-06-10 18:22:54 +0800195 self.cpufreq_manager = None
Jon Salzd7550792013-07-12 05:49:27 +0800196 self.status = Status.UNINITIALIZED
Ricky Liang36512a32014-07-25 11:47:04 +0800197 self.ready_for_ui_connection = False
Peter Ammon1e1ec572014-06-26 17:56:32 -0700198 self.link_manager = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800199
Jon Salz85a39882012-07-05 16:45:04 +0800200 def test_or_root(event, parent_or_group=True):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800201 """Returns the test affected by a particular event.
Jon Salz85a39882012-07-05 16:45:04 +0800202
203 Args:
204 event: The event containing an optional 'path' attribute.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800205 parent_or_group: If True, returns the top-level parent for a test (the
Jon Salz85a39882012-07-05 16:45:04 +0800206 root node of the tests that need to be run together if the given test
207 path is to be run).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800208 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800209 try:
210 path = event.path
211 except AttributeError:
212 path = None
213
214 if path:
Jon Salz85a39882012-07-05 16:45:04 +0800215 test = self.test_list.lookup_path(path)
216 if parent_or_group:
217 test = test.get_top_level_parent_or_group()
218 return test
Jon Salz0697cbf2012-07-04 15:14:04 +0800219 else:
220 return self.test_list
221
222 self.event_handlers = {
Ricky Liang45c73e72015-01-15 15:00:30 +0800223 Event.Type.SWITCH_TEST: self.handle_switch_test,
224 Event.Type.SHOW_NEXT_ACTIVE_TEST:
225 lambda event: self.show_next_active_test(),
226 Event.Type.RESTART_TESTS:
227 lambda event: self.restart_tests(root=test_or_root(event)),
228 Event.Type.AUTO_RUN:
229 lambda event: self.auto_run(root=test_or_root(event)),
230 Event.Type.RE_RUN_FAILED:
231 lambda event: self.re_run_failed(root=test_or_root(event)),
232 Event.Type.RUN_TESTS_WITH_STATUS:
233 lambda event: self.run_tests_with_status(
234 event.status,
235 root=test_or_root(event)),
236 Event.Type.REVIEW:
237 lambda event: self.show_review_information(),
238 Event.Type.UPDATE_SYSTEM_INFO:
239 lambda event: self.update_system_info(),
240 Event.Type.STOP:
241 lambda event: self.stop(root=test_or_root(event, False),
242 fail=getattr(event, 'fail', False),
243 reason=getattr(event, 'reason', None)),
244 Event.Type.SET_VISIBLE_TEST:
245 lambda event: self.set_visible_test(
246 self.test_list.lookup_path(event.path)),
247 Event.Type.CLEAR_STATE:
248 lambda event: self.clear_state(
249 self.test_list.lookup_path(event.path)),
Jon Salz0697cbf2012-07-04 15:14:04 +0800250 }
251
Jon Salz0697cbf2012-07-04 15:14:04 +0800252 self.web_socket_manager = None
253
254 def destroy(self):
Ricky Liang74237a02014-09-18 15:11:23 +0800255 """Performs any shutdown tasks. Overrides base class method."""
Jon Salzd7550792013-07-12 05:49:27 +0800256 self.status = Status.TERMINATING
Jon Salz0697cbf2012-07-04 15:14:04 +0800257 if self.chrome:
258 self.chrome.kill()
259 self.chrome = None
Jon Salzc79a9982012-08-30 04:42:01 +0800260 if self.dummy_shopfloor:
261 self.dummy_shopfloor.kill()
262 self.dummy_shopfloor = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800263 if self.ui_process:
264 utils.kill_process_tree(self.ui_process, 'ui')
265 self.ui_process = None
266 if self.web_socket_manager:
267 logging.info('Stopping web sockets')
268 self.web_socket_manager.close()
269 self.web_socket_manager = None
270 if self.state_server_thread:
271 logging.info('Stopping state server')
272 self.state_server.shutdown()
273 self.state_server_thread.join()
274 self.state_server.server_close()
275 self.state_server_thread = None
276 if self.state_instance:
277 self.state_instance.close()
278 if self.event_server_thread:
279 logging.info('Stopping event server')
280 self.event_server.shutdown() # pylint: disable=E1101
281 self.event_server_thread.join()
282 self.event_server.server_close()
283 self.event_server_thread = None
284 if self.log_watcher:
285 if self.log_watcher.IsThreadStarted():
286 self.log_watcher.StopWatchThread()
287 self.log_watcher = None
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800288 if self.system_log_manager:
289 if self.system_log_manager.IsThreadRunning():
Cheng-Yi Chianga0f6eff2014-01-09 18:27:22 +0800290 self.system_log_manager.Stop()
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +0800291 self.system_log_manager = None
Vic Yange2c76a82014-10-30 12:48:19 -0700292 if self.autotest_prespawner:
293 logging.info('Stopping autotest prespawner')
294 self.autotest_prespawner.stop()
295 self.autotest_prespawner = None
296 if self.pytest_prespawner:
297 logging.info('Stopping pytest prespawner')
298 self.pytest_prespawner.stop()
299 self.pytest_prespawner = None
Jon Salz0697cbf2012-07-04 15:14:04 +0800300 if self.event_client:
301 logging.info('Closing event client')
302 self.event_client.close()
303 self.event_client = None
Jon Salzddf0d052013-06-18 12:52:44 +0800304 if self.cpufreq_manager:
305 self.cpufreq_manager.Stop()
Jon Salz0697cbf2012-07-04 15:14:04 +0800306 if self.event_log:
307 self.event_log.Close()
308 self.event_log = None
Dean Liao592e4d52013-01-10 20:06:39 +0800309 if self.key_filter:
310 self.key_filter.Stop()
Vic Yangd8990da2013-06-27 16:57:43 +0800311 if self.cpu_usage_watcher:
312 self.cpu_usage_watcher.terminate()
Peter Ammon1e1ec572014-06-26 17:56:32 -0700313 if self.link_manager:
314 self.link_manager.Stop()
315 self.link_manager = None
Dean Liao592e4d52013-01-10 20:06:39 +0800316
Peter Ammon1e1ec572014-06-26 17:56:32 -0700317 super(Goofy, self).destroy()
Jon Salz0697cbf2012-07-04 15:14:04 +0800318 logging.info('Done destroying Goofy')
Jon Salzd7550792013-07-12 05:49:27 +0800319 self.status = Status.TERMINATED
Jon Salz0697cbf2012-07-04 15:14:04 +0800320
321 def start_state_server(self):
Jon Salz2af235d2013-06-24 14:47:21 +0800322 # Before starting state server, remount stateful partitions with
323 # no commit flag. The default commit time (commit=600) makes corruption
324 # too likely.
Hung-Te Lind59dbfa2014-08-27 12:27:53 +0800325 utils.ResetCommitTime()
Jon Salz2af235d2013-06-24 14:47:21 +0800326
Jon Salz0697cbf2012-07-04 15:14:04 +0800327 self.state_instance, self.state_server = (
Ricky Liang45c73e72015-01-15 15:00:30 +0800328 state.create_server(bind_address='0.0.0.0'))
Jon Salz16d10542012-07-23 12:18:45 +0800329 self.goofy_rpc = GoofyRPC(self)
330 self.goofy_rpc.RegisterMethods(self.state_instance)
Jon Salz0697cbf2012-07-04 15:14:04 +0800331 logging.info('Starting state server')
332 self.state_server_thread = threading.Thread(
Ricky Liang45c73e72015-01-15 15:00:30 +0800333 target=self.state_server.serve_forever,
334 name='StateServer')
Jon Salz0697cbf2012-07-04 15:14:04 +0800335 self.state_server_thread.start()
336
337 def start_event_server(self):
338 self.event_server = EventServer()
339 logging.info('Starting factory event server')
340 self.event_server_thread = threading.Thread(
Ricky Liang45c73e72015-01-15 15:00:30 +0800341 target=self.event_server.serve_forever,
342 name='EventServer') # pylint: disable=E1101
Jon Salz0697cbf2012-07-04 15:14:04 +0800343 self.event_server_thread.start()
344
345 self.event_client = EventClient(
Ricky Liang45c73e72015-01-15 15:00:30 +0800346 callback=self.handle_event, event_loop=self.run_queue)
Jon Salz0697cbf2012-07-04 15:14:04 +0800347
348 self.web_socket_manager = WebSocketManager(self.uuid)
Ricky Liang45c73e72015-01-15 15:00:30 +0800349 self.state_server.add_handler('/event',
350 self.web_socket_manager.handle_web_socket)
Jon Salz0697cbf2012-07-04 15:14:04 +0800351
352 def start_ui(self):
353 ui_proc_args = [
Ricky Liang45c73e72015-01-15 15:00:30 +0800354 os.path.join(factory.FACTORY_PACKAGE_PATH, 'test', 'ui.py'),
355 self.options.test_list
356 ]
Jon Salz0697cbf2012-07-04 15:14:04 +0800357 if self.options.verbose:
358 ui_proc_args.append('-v')
359 logging.info('Starting ui %s', ui_proc_args)
Jon Salz78c32392012-07-25 14:18:29 +0800360 self.ui_process = Spawn(ui_proc_args)
Jon Salz0697cbf2012-07-04 15:14:04 +0800361 logging.info('Waiting for UI to come up...')
362 self.event_client.wait(
Ricky Liang45c73e72015-01-15 15:00:30 +0800363 lambda event: event.type == Event.Type.UI_READY)
Jon Salz0697cbf2012-07-04 15:14:04 +0800364 logging.info('UI has started')
365
366 def set_visible_test(self, test):
367 if self.visible_test == test:
368 return
Jon Salz2f2d42c2012-07-30 12:30:34 +0800369 if test and not test.has_ui:
370 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800371
372 if test:
373 test.update_state(visible=True)
374 if self.visible_test:
375 self.visible_test.update_state(visible=False)
376 self.visible_test = test
377
Ricky Liang48e47f92014-02-26 19:31:51 +0800378 def log_startup_messages(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800379 """Logs the tail of var/log/messages and mosys and EC console logs."""
Jon Salzd4306c82012-11-30 15:16:36 +0800380 # TODO(jsalz): This is mostly a copy-and-paste of code in init_states,
381 # for factory-3004.B only. Consolidate and merge back to ToT.
382 if utils.in_chroot():
383 return
384
385 try:
Ricky Liang45c73e72015-01-15 15:00:30 +0800386 var_log_messages = utils.var_log_messages_before_reboot()
Jon Salzd4306c82012-11-30 15:16:36 +0800387 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +0800388 'Tail of /var/log/messages before last reboot:\n'
389 '%s', ('\n'.join(
390 ' ' + x for x in var_log_messages)))
Jon Salzd4306c82012-11-30 15:16:36 +0800391 except: # pylint: disable=W0702
392 logging.exception('Unable to grok /var/log/messages')
393
394 try:
Ricky Liang117484a2014-04-14 11:14:41 +0800395 mosys_log = Spawn(
Jon Salzd4306c82012-11-30 15:16:36 +0800396 ['mosys', 'eventlog', 'list'],
397 read_stdout=True, log_stderr_on_error=True).stdout_data
398 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
399 except: # pylint: disable=W0702
400 logging.exception('Unable to read mosys eventlog')
401
Dean Liao88b93192014-10-23 19:37:41 +0800402 self.log_ec_console()
403 self.log_ec_panic_info()
404
405 @staticmethod
406 def log_ec_console():
407 """Logs EC console log into logging.info.
408
409 It logs an error message in logging.exception if an exception is raised
410 when getting EC console log.
411 For unsupported device, it logs unsupport message in logging.info
412
413 Returns:
414 EC console log string.
415 """
Jon Salzd4306c82012-11-30 15:16:36 +0800416 try:
Vic Yang8341dde2013-01-29 16:48:52 +0800417 board = system.GetBoard()
418 ec_console_log = board.GetECConsoleLog()
Jon Salzd4306c82012-11-30 15:16:36 +0800419 logging.info('EC console log after reboot:\n%s\n', ec_console_log)
Dean Liao88b93192014-10-23 19:37:41 +0800420 return ec_console_log
421 except NotImplementedError:
422 logging.info('EC console log not supported')
Jon Salzd4306c82012-11-30 15:16:36 +0800423 except: # pylint: disable=W0702
424 logging.exception('Error retrieving EC console log')
425
Dean Liao88b93192014-10-23 19:37:41 +0800426 @staticmethod
427 def log_ec_panic_info():
428 """Logs EC panic info into logging.info.
429
430 It logs an error message in logging.exception if an exception is raised
431 when getting EC panic info.
432 For unsupported device, it logs unsupport message in logging.info
433
434 Returns:
435 EC panic info string.
436 """
Vic Yang079f9872013-07-01 11:32:00 +0800437 try:
438 board = system.GetBoard()
439 ec_panic_info = board.GetECPanicInfo()
440 logging.info('EC panic info after reboot:\n%s\n', ec_panic_info)
Dean Liao88b93192014-10-23 19:37:41 +0800441 return ec_panic_info
442 except NotImplementedError:
443 logging.info('EC panic info is not supported')
Vic Yang079f9872013-07-01 11:32:00 +0800444 except: # pylint: disable=W0702
445 logging.exception('Error retrieving EC panic info')
446
Ricky Liang48e47f92014-02-26 19:31:51 +0800447 def shutdown(self, operation):
448 """Starts shutdown procedure.
449
450 Args:
Vic (Chun-Ju) Yang05b0d952014-04-28 17:39:09 +0800451 operation: The shutdown operation (reboot, full_reboot, or halt).
Ricky Liang48e47f92014-02-26 19:31:51 +0800452 """
453 active_tests = []
454 for test in self.test_list.walk():
455 if not test.is_leaf():
456 continue
457
458 test_state = test.get_state()
459 if test_state.status == TestState.ACTIVE:
460 active_tests.append(test)
461
Ricky Liang48e47f92014-02-26 19:31:51 +0800462 if not (len(active_tests) == 1 and
463 isinstance(active_tests[0], factory.ShutdownStep)):
464 logging.error(
465 'Calling Goofy shutdown outside of the shutdown factory test')
466 return
467
468 logging.info('Start Goofy shutdown (%s)', operation)
469 # Save pending test list in the state server
470 self.state_instance.set_shared_data(
471 'tests_after_shutdown',
472 [t.path for t in self.tests_to_run])
473 # Save shutdown time
474 self.state_instance.set_shared_data('shutdown_time', time.time())
475
476 with self.env.lock:
477 self.event_log.Log('shutdown', operation=operation)
478 shutdown_result = self.env.shutdown(operation)
479 if shutdown_result:
480 # That's all, folks!
Peter Ammon1e1ec572014-06-26 17:56:32 -0700481 self.run_enqueue(None)
Ricky Liang48e47f92014-02-26 19:31:51 +0800482 else:
483 # Just pass (e.g., in the chroot).
484 self.state_instance.set_shared_data('tests_after_shutdown', None)
485 # Send event with no fields to indicate that there is no
486 # longer a pending shutdown.
487 self.event_client.post_event(Event(Event.Type.PENDING_SHUTDOWN))
488
489 def handle_shutdown_complete(self, test):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800490 """Handles the case where a shutdown was detected during a shutdown step.
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800491
Ricky Liang6fe218c2013-12-27 15:17:17 +0800492 Args:
493 test: The ShutdownStep.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800494 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800495 test_state = test.update_state(increment_shutdown_count=1)
496 logging.info('Detected shutdown (%d of %d)',
Ricky Liang48e47f92014-02-26 19:31:51 +0800497 test_state.shutdown_count, test.iterations)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800498
Ricky Liang48e47f92014-02-26 19:31:51 +0800499 # Insert current shutdown test at the front of the list of tests to run
500 # after shutdown. This is to continue on post-shutdown verification in the
501 # shutdown step.
502 tests_after_shutdown = self.state_instance.get_shared_data(
503 'tests_after_shutdown', optional=True)
504 if not tests_after_shutdown:
505 self.state_instance.set_shared_data('tests_after_shutdown', [test.path])
506 elif isinstance(tests_after_shutdown, list):
507 self.state_instance.set_shared_data(
508 'tests_after_shutdown', [test.path] + tests_after_shutdown)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800509
Ricky Liang48e47f92014-02-26 19:31:51 +0800510 # Set 'post_shutdown' to inform shutdown test that a shutdown just occurred.
Ricky Liangb7eb8772014-09-15 18:05:22 +0800511 self.state_instance.set_shared_data(
512 state.POST_SHUTDOWN_TAG % test.path,
513 self.state_instance.get_test_state(test.path).invocation)
Jon Salz258a40c2012-04-19 12:34:01 +0800514
Jon Salz0697cbf2012-07-04 15:14:04 +0800515 def init_states(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800516 """Initializes all states on startup."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800517 for test in self.test_list.get_all_tests():
518 # Make sure the state server knows about all the tests,
519 # defaulting to an untested state.
520 test.update_state(update_parent=False, visible=False)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800521
Jon Salz0697cbf2012-07-04 15:14:04 +0800522 var_log_messages = None
Vic Yanga9c32212012-08-16 20:07:54 +0800523 mosys_log = None
Vic Yange4c275d2012-08-28 01:50:20 +0800524 ec_console_log = None
Vic Yang079f9872013-07-01 11:32:00 +0800525 ec_panic_info = None
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800526
Jon Salz0697cbf2012-07-04 15:14:04 +0800527 # Any 'active' tests should be marked as failed now.
528 for test in self.test_list.walk():
Jon Salza6711d72012-07-18 14:33:03 +0800529 if not test.is_leaf():
530 # Don't bother with parents; they will be updated when their
531 # children are updated.
532 continue
533
Jon Salz0697cbf2012-07-04 15:14:04 +0800534 test_state = test.get_state()
535 if test_state.status != TestState.ACTIVE:
536 continue
537 if isinstance(test, factory.ShutdownStep):
538 # Shutdown while the test was active - that's good.
Ricky Liang48e47f92014-02-26 19:31:51 +0800539 self.handle_shutdown_complete(test)
Jon Salz0697cbf2012-07-04 15:14:04 +0800540 else:
541 # Unexpected shutdown. Grab /var/log/messages for context.
542 if var_log_messages is None:
543 try:
544 var_log_messages = (
Ricky Liang45c73e72015-01-15 15:00:30 +0800545 utils.var_log_messages_before_reboot())
Jon Salz0697cbf2012-07-04 15:14:04 +0800546 # Write it to the log, to make it easier to
547 # correlate with /var/log/messages.
548 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +0800549 'Unexpected shutdown. '
550 'Tail of /var/log/messages before last reboot:\n'
551 '%s', ('\n'.join(
552 ' ' + x for x in var_log_messages)))
Jon Salz0697cbf2012-07-04 15:14:04 +0800553 except: # pylint: disable=W0702
554 logging.exception('Unable to grok /var/log/messages')
555 var_log_messages = []
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800556
Jon Salz008f4ea2012-08-28 05:39:45 +0800557 if mosys_log is None and not utils.in_chroot():
558 try:
Ricky Liang117484a2014-04-14 11:14:41 +0800559 mosys_log = Spawn(
Jon Salz008f4ea2012-08-28 05:39:45 +0800560 ['mosys', 'eventlog', 'list'],
561 read_stdout=True, log_stderr_on_error=True).stdout_data
562 # Write it to the log also.
563 logging.info('System eventlog from mosys:\n%s\n', mosys_log)
564 except: # pylint: disable=W0702
565 logging.exception('Unable to read mosys eventlog')
Vic Yanga9c32212012-08-16 20:07:54 +0800566
Vic Yange4c275d2012-08-28 01:50:20 +0800567 if ec_console_log is None:
Dean Liao88b93192014-10-23 19:37:41 +0800568 ec_console_log = self.log_ec_console()
Vic Yange4c275d2012-08-28 01:50:20 +0800569
Vic Yang079f9872013-07-01 11:32:00 +0800570 if ec_panic_info is None:
Dean Liao88b93192014-10-23 19:37:41 +0800571 ec_panic_info = self.log_ec_panic_info()
Vic Yang079f9872013-07-01 11:32:00 +0800572
Jon Salz0697cbf2012-07-04 15:14:04 +0800573 error_msg = 'Unexpected shutdown while test was running'
574 self.event_log.Log('end_test',
Ricky Liang45c73e72015-01-15 15:00:30 +0800575 path=test.path,
576 status=TestState.FAILED,
577 invocation=test.get_state().invocation,
578 error_msg=error_msg,
579 var_log_messages='\n'.join(var_log_messages),
580 mosys_log=mosys_log)
Jon Salz0697cbf2012-07-04 15:14:04 +0800581 test.update_state(
Ricky Liang45c73e72015-01-15 15:00:30 +0800582 status=TestState.FAILED,
583 error_msg=error_msg)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800584
Jon Salz50efe942012-07-26 11:54:10 +0800585 if not test.never_fails:
586 # For "never_fails" tests (such as "Start"), don't cancel
587 # pending tests, since reboot is expected.
588 factory.console.info('Unexpected shutdown while test %s '
589 'running; cancelling any pending tests',
590 test.path)
591 self.state_instance.set_shared_data('tests_after_shutdown', [])
Jon Salz69806bb2012-07-20 18:05:02 +0800592
Jon Salz008f4ea2012-08-28 05:39:45 +0800593 self.update_skipped_tests()
594
595 def update_skipped_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800596 """Updates skipped states based on run_if."""
Jon Salz885dcac2013-07-23 16:39:50 +0800597 env = TestArgEnv()
Ricky Liang45c73e72015-01-15 15:00:30 +0800598
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800599 def _evaluate_skip_from_run_if(test):
600 """Returns the run_if evaluation of the test.
601
602 Args:
603 test: A FactoryTest object.
604
605 Returns:
606 The run_if evaluation result. Returns False if the test has no
607 run_if argument.
608 """
609 value = None
610 if test.run_if_expr:
611 try:
612 value = test.run_if_expr(env)
613 except: # pylint: disable=W0702
614 logging.exception('Unable to evaluate run_if expression for %s',
615 test.path)
616 # But keep going; we have no choice. This will end up
617 # always activating the test.
618 elif test.run_if_table_name:
619 try:
620 aux = shopfloor.get_selected_aux_data(test.run_if_table_name)
621 value = aux.get(test.run_if_col)
622 except ValueError:
623 # Not available; assume it shouldn't be skipped
624 pass
625
626 if value is None:
627 skip = False
628 else:
629 skip = (not value) ^ t.run_if_not
630 return skip
631
632 # Gets all run_if evaluation, and stores results in skip_map.
633 skip_map = dict()
Jon Salz008f4ea2012-08-28 05:39:45 +0800634 for t in self.test_list.walk():
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800635 skip_map[t.path] = _evaluate_skip_from_run_if(t)
Jon Salz885dcac2013-07-23 16:39:50 +0800636
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800637 # Propagates the skip value from root of tree and updates skip_map.
638 def _update_skip_map_from_node(test, skip_from_parent):
639 """Updates skip_map from a given node.
Jon Salz885dcac2013-07-23 16:39:50 +0800640
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800641 Given a FactoryTest node and the skip value from parent, updates the
642 skip value of current node in the skip_map if skip value from parent is
643 True. If this node has children, recursively propagate this value to all
644 its children, that is, all its subtests.
645 Note that this function only updates value in skip_map, not the actual
646 test_list tree.
Jon Salz008f4ea2012-08-28 05:39:45 +0800647
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800648 Args:
649 test: The given FactoryTest object. It is a node in the test_list tree.
650 skip_from_parent: The skip value which propagates from the parent of
651 input node.
652 """
653 skip_this_tree = skip_from_parent or skip_map[test.path]
654 if skip_this_tree:
655 logging.info('Skip from node %r', test.path)
656 skip_map[test.path] = True
657 if test.is_leaf():
658 return
659 # Propagates skip value to its subtests
660 for subtest in test.subtests:
661 _update_skip_map_from_node(subtest, skip_this_tree)
662
663 _update_skip_map_from_node(self.test_list, False)
664
665 # Updates the skip value from skip_map to test_list tree. Also, updates test
666 # status if needed.
667 for t in self.test_list.walk():
668 skip = skip_map[t.path]
669 test_state = t.get_state()
670 if ((not skip) and
671 (test_state.status == TestState.PASSED) and
672 (test_state.error_msg == TestState.SKIPPED_MSG)):
673 # It was marked as skipped before, but now we need to run it.
674 # Mark as untested.
675 t.update_state(skip=skip, status=TestState.UNTESTED, error_msg='')
676 else:
677 t.update_state(skip=skip)
Jon Salz008f4ea2012-08-28 05:39:45 +0800678
Jon Salz0697cbf2012-07-04 15:14:04 +0800679 def show_next_active_test(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800680 """Rotates to the next visible active test."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800681 self.reap_completed_tests()
682 active_tests = [
Ricky Liang45c73e72015-01-15 15:00:30 +0800683 t for t in self.test_list.walk()
684 if t.is_leaf() and t.get_state().status == TestState.ACTIVE]
Jon Salz0697cbf2012-07-04 15:14:04 +0800685 if not active_tests:
686 return
Jon Salz4f6c7172012-06-11 20:45:36 +0800687
Jon Salz0697cbf2012-07-04 15:14:04 +0800688 try:
689 next_test = active_tests[
Ricky Liang45c73e72015-01-15 15:00:30 +0800690 (active_tests.index(self.visible_test) + 1) % len(active_tests)]
Jon Salz0697cbf2012-07-04 15:14:04 +0800691 except ValueError: # visible_test not present in active_tests
692 next_test = active_tests[0]
Jon Salz4f6c7172012-06-11 20:45:36 +0800693
Jon Salz0697cbf2012-07-04 15:14:04 +0800694 self.set_visible_test(next_test)
Jon Salz4f6c7172012-06-11 20:45:36 +0800695
Jon Salz0697cbf2012-07-04 15:14:04 +0800696 def handle_event(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800697 """Handles an event from the event server."""
Jon Salz0697cbf2012-07-04 15:14:04 +0800698 handler = self.event_handlers.get(event.type)
699 if handler:
700 handler(event)
701 else:
702 # We don't register handlers for all event types - just ignore
703 # this event.
704 logging.debug('Unbound event type %s', event.type)
Jon Salz4f6c7172012-06-11 20:45:36 +0800705
Vic Yangaabf9fd2013-04-09 18:56:13 +0800706 def check_critical_factory_note(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800707 """Returns True if the last factory note is critical."""
Vic Yangaabf9fd2013-04-09 18:56:13 +0800708 notes = self.state_instance.get_shared_data('factory_note', True)
709 return notes and notes[-1]['level'] == 'CRITICAL'
710
Jon Salz0697cbf2012-07-04 15:14:04 +0800711 def run_next_test(self):
henryhsu4cc6b022014-04-22 17:12:42 +0800712 """Runs the next eligible test (or tests) in self.tests_to_run.
713
714 We have three kinds of the next eligible test:
715 1. normal
716 2. backgroundable
717 3. force_background
718
719 And we have four situations of the ongoing invocations:
720 a. only a running normal test
721 b. all running tests are backgroundable
722 c. all running tests are force_background
723 d. all running tests are any combination of backgroundable and
724 force_background
725
726 When a test would like to be run, it must follow the rules:
727 [1] cannot run with [abd]
728 [2] cannot run with [a]
729 All the other combinations are allowed
730 """
Jon Salz0697cbf2012-07-04 15:14:04 +0800731 self.reap_completed_tests()
Vic Yangaabf9fd2013-04-09 18:56:13 +0800732 if self.tests_to_run and self.check_critical_factory_note():
733 self.tests_to_run.clear()
734 return
Jon Salz0697cbf2012-07-04 15:14:04 +0800735 while self.tests_to_run:
Ricky Liang6fe218c2013-12-27 15:17:17 +0800736 logging.debug('Tests to run: %s', [x.path for x in self.tests_to_run])
Jon Salz94eb56f2012-06-12 18:01:12 +0800737
Jon Salz0697cbf2012-07-04 15:14:04 +0800738 test = self.tests_to_run[0]
Jon Salz94eb56f2012-06-12 18:01:12 +0800739
Jon Salz0697cbf2012-07-04 15:14:04 +0800740 if test in self.invocations:
741 logging.info('Next test %s is already running', test.path)
742 self.tests_to_run.popleft()
743 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800744
Jon Salza1412922012-07-23 16:04:17 +0800745 for requirement in test.require_run:
746 for i in requirement.test.walk():
747 if i.get_state().status == TestState.ACTIVE:
Jon Salz304a75d2012-07-06 11:14:15 +0800748 logging.info('Waiting for active test %s to complete '
Jon Salza1412922012-07-23 16:04:17 +0800749 'before running %s', i.path, test.path)
Jon Salz304a75d2012-07-06 11:14:15 +0800750 return
751
henryhsu4cc6b022014-04-22 17:12:42 +0800752 def is_normal_test(test):
753 return not (test.backgroundable or test.force_background)
754
755 # [1] cannot run with [abd].
756 if self.invocations and is_normal_test(test) and any(
757 [not x.force_background for x in self.invocations]):
758 logging.info('Waiting for non-force_background tests to '
759 'complete before running %s', test.path)
760 return
761
762 # [2] cannot run with [a].
763 if self.invocations and test.backgroundable and any(
764 [is_normal_test(x) for x in self.invocations]):
765 logging.info('Waiting for normal tests to '
766 'complete before running %s', test.path)
Jon Salz0697cbf2012-07-04 15:14:04 +0800767 return
Jon Salz94eb56f2012-06-12 18:01:12 +0800768
Jon Salz3e6f5202012-10-15 15:08:29 +0800769 if test.get_state().skip:
770 factory.console.info('Skipping test %s', test.path)
771 test.update_state(status=TestState.PASSED,
772 error_msg=TestState.SKIPPED_MSG)
773 self.tests_to_run.popleft()
774 continue
775
Jon Salz0697cbf2012-07-04 15:14:04 +0800776 self.tests_to_run.popleft()
Jon Salz94eb56f2012-06-12 18:01:12 +0800777
Jon Salz304a75d2012-07-06 11:14:15 +0800778 untested = set()
Jon Salza1412922012-07-23 16:04:17 +0800779 for requirement in test.require_run:
780 for i in requirement.test.walk():
781 if i == test:
Jon Salz304a75d2012-07-06 11:14:15 +0800782 # We've hit this test itself; stop checking
783 break
Jon Salza1412922012-07-23 16:04:17 +0800784 if ((i.get_state().status == TestState.UNTESTED) or
785 (requirement.passed and i.get_state().status !=
786 TestState.PASSED)):
Jon Salz304a75d2012-07-06 11:14:15 +0800787 # Found an untested test; move on to the next
788 # element in require_run.
Jon Salza1412922012-07-23 16:04:17 +0800789 untested.add(i)
Jon Salz304a75d2012-07-06 11:14:15 +0800790 break
791
792 if untested:
793 untested_paths = ', '.join(sorted([x.path for x in untested]))
794 if self.state_instance.get_shared_data('engineering_mode',
795 optional=True):
796 # In engineering mode, we'll let it go.
797 factory.console.warn('In engineering mode; running '
798 '%s even though required tests '
799 '[%s] have not completed',
800 test.path, untested_paths)
801 else:
802 # Not in engineering mode; mark it failed.
803 error_msg = ('Required tests [%s] have not been run yet'
804 % untested_paths)
805 factory.console.error('Not running %s: %s',
806 test.path, error_msg)
807 test.update_state(status=TestState.FAILED,
808 error_msg=error_msg)
809 continue
810
Ricky Liang48e47f92014-02-26 19:31:51 +0800811 if (isinstance(test, factory.ShutdownStep) and
Ricky Liangb7eb8772014-09-15 18:05:22 +0800812 self.state_instance.get_shared_data(
813 state.POST_SHUTDOWN_TAG % test.path, optional=True)):
Ricky Liang48e47f92014-02-26 19:31:51 +0800814 # Invoking post shutdown method of shutdown test. We should retain the
815 # iterations_left and retries_left of the original test state.
816 test_state = self.state_instance.get_test_state(test.path)
817 self._run_test(test, test_state.iterations_left,
818 test_state.retries_left)
819 else:
820 # Starts a new test run; reset iterations and retries.
821 self._run_test(test, test.iterations, test.retries)
Jon Salz1acc8742012-07-17 17:45:55 +0800822
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +0800823 def _run_test(self, test, iterations_left=None, retries_left=None):
Vic Yanga3cecf82014-12-26 00:44:21 -0800824 if not self._ui_initialized and not test.is_no_host():
825 self.init_ui()
Vic Yang08505c72015-01-06 17:01:53 -0800826 invoc = TestInvocation(
827 self, test, on_completion=self.run_next_test,
828 on_test_failure=system.GetBoard().OnTestFailure)
Jon Salz1acc8742012-07-17 17:45:55 +0800829 new_state = test.update_state(
Ricky Liang48e47f92014-02-26 19:31:51 +0800830 status=TestState.ACTIVE, increment_count=1, error_msg='',
831 invocation=invoc.uuid, iterations_left=iterations_left,
832 retries_left=retries_left,
833 visible=(self.visible_test == test))
Jon Salz1acc8742012-07-17 17:45:55 +0800834 invoc.count = new_state.count
835
836 self.invocations[test] = invoc
837 if self.visible_test is None and test.has_ui:
838 self.set_visible_test(test)
Vic Yang311ddb82012-09-26 12:08:28 +0800839 self.check_exclusive()
Jon Salz1acc8742012-07-17 17:45:55 +0800840 invoc.start()
Jon Salz5f2a0672012-05-22 17:14:06 +0800841
Vic Yang311ddb82012-09-26 12:08:28 +0800842 def check_exclusive(self):
Jon Salzce6a7f82013-06-10 18:22:54 +0800843 # alias since this is really long
844 EXCL_OPT = factory.FactoryTest.EXCLUSIVE_OPTIONS
845
Vic Yang311ddb82012-09-26 12:08:28 +0800846 current_exclusive_items = set([
Jon Salzce6a7f82013-06-10 18:22:54 +0800847 item for item in EXCL_OPT
Vic Yang311ddb82012-09-26 12:08:28 +0800848 if any([test.is_exclusive(item) for test in self.invocations])])
849
850 new_exclusive_items = current_exclusive_items - self.exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800851 if EXCL_OPT.NETWORKING in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800852 logging.info('Disabling network')
853 self.connection_manager.DisableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800854 if EXCL_OPT.CHARGER in new_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800855 logging.info('Stop controlling charger')
856
857 new_non_exclusive_items = self.exclusive_items - current_exclusive_items
Jon Salzce6a7f82013-06-10 18:22:54 +0800858 if EXCL_OPT.NETWORKING in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800859 logging.info('Re-enabling network')
860 self.connection_manager.EnableNetworking()
Jon Salzce6a7f82013-06-10 18:22:54 +0800861 if EXCL_OPT.CHARGER in new_non_exclusive_items:
Vic Yang311ddb82012-09-26 12:08:28 +0800862 logging.info('Start controlling charger')
863
Jon Salzce6a7f82013-06-10 18:22:54 +0800864 if self.cpufreq_manager:
865 enabled = EXCL_OPT.CPUFREQ not in current_exclusive_items
866 try:
867 self.cpufreq_manager.SetEnabled(enabled)
868 except: # pylint: disable=W0702
869 logging.exception('Unable to %s cpufreq services',
870 'enable' if enabled else 'disable')
871
Ricky Liang0f9978e2015-01-30 08:19:17 +0000872 # Only adjust charge state if not excluded
873 if (EXCL_OPT.CHARGER not in current_exclusive_items and
874 not utils.in_chroot()):
875 if self.charge_manager:
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +0800876 self.charge_manager.AdjustChargeState()
877 else:
Dean Liao88b93192014-10-23 19:37:41 +0800878 self.charge()
Vic Yang311ddb82012-09-26 12:08:28 +0800879
880 self.exclusive_items = current_exclusive_items
Jon Salz5da61e62012-05-31 13:06:22 +0800881
Dean Liao88b93192014-10-23 19:37:41 +0800882 def charge(self):
883 """Charges the board.
884
885 It won't try again if last time SetChargeState raised an exception.
886 """
887 if not self._can_charge:
888 return
889
890 try:
891 system.GetBoard().SetChargeState(Board.ChargeState.CHARGE)
892 except NotImplementedError:
893 logging.info('Charging is not supported')
894 self._can_charge = False
895 except BoardException:
896 logging.exception('Unable to set charge state on this board')
897 self._can_charge = False
898
cychiang21886742012-07-05 15:16:32 +0800899 def check_for_updates(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800900 """Schedules an asynchronous check for updates if necessary."""
cychiang21886742012-07-05 15:16:32 +0800901 if not self.test_list.options.update_period_secs:
902 # Not enabled.
903 return
904
905 now = time.time()
906 if self.last_update_check and (
907 now - self.last_update_check <
908 self.test_list.options.update_period_secs):
909 # Not yet time for another check.
910 return
911
912 self.last_update_check = now
913
914 def handle_check_for_update(reached_shopfloor, md5sum, needs_update):
915 if reached_shopfloor:
916 new_update_md5sum = md5sum if needs_update else None
917 if system.SystemInfo.update_md5sum != new_update_md5sum:
918 logging.info('Received new update MD5SUM: %s', new_update_md5sum)
919 system.SystemInfo.update_md5sum = new_update_md5sum
Peter Ammon1e1ec572014-06-26 17:56:32 -0700920 self.run_enqueue(self.update_system_info)
cychiang21886742012-07-05 15:16:32 +0800921
922 updater.CheckForUpdateAsync(
Ricky Liang45c73e72015-01-15 15:00:30 +0800923 handle_check_for_update,
924 self.test_list.options.shopfloor_timeout_secs)
cychiang21886742012-07-05 15:16:32 +0800925
Jon Salza6711d72012-07-18 14:33:03 +0800926 def cancel_pending_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800927 """Cancels any tests in the run queue."""
Jon Salza6711d72012-07-18 14:33:03 +0800928 self.run_tests([])
929
Ricky Liang4bff3e32014-02-20 18:46:11 +0800930 def restore_active_run_state(self):
931 """Restores active run id and the list of scheduled tests."""
932 self.run_id = self.state_instance.get_shared_data('run_id', optional=True)
933 self.scheduled_run_tests = self.state_instance.get_shared_data(
934 'scheduled_run_tests', optional=True)
935
936 def set_active_run_state(self):
937 """Sets active run id and the list of scheduled tests."""
938 self.run_id = str(uuid.uuid4())
939 self.scheduled_run_tests = [test.path for test in self.tests_to_run]
940 self.state_instance.set_shared_data('run_id', self.run_id)
941 self.state_instance.set_shared_data('scheduled_run_tests',
942 self.scheduled_run_tests)
943
Jon Salz0697cbf2012-07-04 15:14:04 +0800944 def run_tests(self, subtrees, untested_only=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800945 """Runs tests under subtree.
Jon Salz258a40c2012-04-19 12:34:01 +0800946
Jon Salz0697cbf2012-07-04 15:14:04 +0800947 The tests are run in order unless one fails (then stops).
948 Backgroundable tests are run simultaneously; when a foreground test is
949 encountered, we wait for all active tests to finish before continuing.
Jon Salzb1b39092012-05-03 02:05:09 +0800950
Ricky Liang6fe218c2013-12-27 15:17:17 +0800951 Args:
952 subtrees: Node or nodes containing tests to run (may either be
953 a single test or a list). Duplicates will be ignored.
954 untested_only: True to run untested tests only.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800955 """
Vic Yang08505c72015-01-06 17:01:53 -0800956 system.GetBoard().OnTestStart()
957
Jon Salz0697cbf2012-07-04 15:14:04 +0800958 if type(subtrees) != list:
959 subtrees = [subtrees]
Jon Salz258a40c2012-04-19 12:34:01 +0800960
Jon Salz0697cbf2012-07-04 15:14:04 +0800961 # Nodes we've seen so far, to avoid duplicates.
962 seen = set()
Jon Salz94eb56f2012-06-12 18:01:12 +0800963
Jon Salz0697cbf2012-07-04 15:14:04 +0800964 self.tests_to_run = deque()
965 for subtree in subtrees:
966 for test in subtree.walk():
967 if test in seen:
968 continue
969 seen.add(test)
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800970
Jon Salz0697cbf2012-07-04 15:14:04 +0800971 if not test.is_leaf():
972 continue
Ricky Liang45c73e72015-01-15 15:00:30 +0800973 if untested_only and test.get_state().status != TestState.UNTESTED:
Jon Salz0697cbf2012-07-04 15:14:04 +0800974 continue
975 self.tests_to_run.append(test)
Ricky Liang4bff3e32014-02-20 18:46:11 +0800976 if subtrees:
977 self.set_active_run_state()
Jon Salz0697cbf2012-07-04 15:14:04 +0800978 self.run_next_test()
Hung-Te Linf2f78f72012-02-08 19:27:11 +0800979
Jon Salz0697cbf2012-07-04 15:14:04 +0800980 def reap_completed_tests(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800981 """Removes completed tests from the set of active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +0800982
983 Also updates the visible test if it was reaped.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +0800984 """
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800985 test_completed = False
Jon Salz0697cbf2012-07-04 15:14:04 +0800986 for t, v in dict(self.invocations).iteritems():
987 if v.is_completed():
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +0800988 test_completed = True
Jon Salz1acc8742012-07-17 17:45:55 +0800989 new_state = t.update_state(**v.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +0800990 del self.invocations[t]
991
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800992 # Stop on failure if flag is true.
993 if (self.test_list.options.stop_on_failure and
994 new_state.status == TestState.FAILED):
995 # Clean all the tests to cause goofy to stop.
996 self.tests_to_run = []
Ricky Liang45c73e72015-01-15 15:00:30 +0800997 factory.console.info('Stop on failure triggered. Empty the queue.')
Chun-Ta Lin54e17e42012-09-06 22:05:13 +0800998
Jon Salz1acc8742012-07-17 17:45:55 +0800999 if new_state.iterations_left and new_state.status == TestState.PASSED:
1000 # Play it again, Sam!
1001 self._run_test(t)
Cheng-Yi Chiangce05c002013-04-04 02:13:17 +08001002 # new_state.retries_left is obtained after update.
1003 # For retries_left == 0, test can still be run for the last time.
1004 elif (new_state.retries_left >= 0 and
1005 new_state.status == TestState.FAILED):
1006 # Still have to retry, Sam!
1007 self._run_test(t)
Jon Salz1acc8742012-07-17 17:45:55 +08001008
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +08001009 if test_completed:
Vic Yangf01c59f2013-04-19 17:37:56 +08001010 self.log_watcher.KickWatchThread()
Cheng-Yi Chiang5ac22ca2013-04-12 17:45:26 +08001011
Jon Salz0697cbf2012-07-04 15:14:04 +08001012 if (self.visible_test is None or
Jon Salz85a39882012-07-05 16:45:04 +08001013 self.visible_test not in self.invocations):
Jon Salz0697cbf2012-07-04 15:14:04 +08001014 self.set_visible_test(None)
1015 # Make the first running test, if any, the visible test
1016 for t in self.test_list.walk():
1017 if t in self.invocations:
1018 self.set_visible_test(t)
1019 break
1020
Jon Salz6dc031d2013-06-19 13:06:23 +08001021 def kill_active_tests(self, abort, root=None, reason=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001022 """Kills and waits for all active tests.
Jon Salz0697cbf2012-07-04 15:14:04 +08001023
Jon Salz85a39882012-07-05 16:45:04 +08001024 Args:
1025 abort: True to change state of killed tests to FAILED, False for
Jon Salz0697cbf2012-07-04 15:14:04 +08001026 UNTESTED.
Jon Salz85a39882012-07-05 16:45:04 +08001027 root: If set, only kills tests with root as an ancestor.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001028 reason: If set, the abort reason.
1029 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001030 self.reap_completed_tests()
1031 for test, invoc in self.invocations.items():
Jon Salz85a39882012-07-05 16:45:04 +08001032 if root and not test.has_ancestor(root):
1033 continue
1034
Ricky Liang45c73e72015-01-15 15:00:30 +08001035 factory.console.info('Killing active test %s...', test.path)
Jon Salz6dc031d2013-06-19 13:06:23 +08001036 invoc.abort_and_join(reason)
Ricky Liang45c73e72015-01-15 15:00:30 +08001037 factory.console.info('Killed %s', test.path)
Jon Salz1acc8742012-07-17 17:45:55 +08001038 test.update_state(**invoc.update_state_on_completion)
Jon Salz0697cbf2012-07-04 15:14:04 +08001039 del self.invocations[test]
Jon Salz1acc8742012-07-17 17:45:55 +08001040
Jon Salz0697cbf2012-07-04 15:14:04 +08001041 if not abort:
1042 test.update_state(status=TestState.UNTESTED)
1043 self.reap_completed_tests()
1044
Jon Salz6dc031d2013-06-19 13:06:23 +08001045 def stop(self, root=None, fail=False, reason=None):
1046 self.kill_active_tests(fail, root, reason)
Jon Salz85a39882012-07-05 16:45:04 +08001047 # Remove any tests in the run queue under the root.
1048 self.tests_to_run = deque([x for x in self.tests_to_run
1049 if root and not x.has_ancestor(root)])
1050 self.run_next_test()
Jon Salz0697cbf2012-07-04 15:14:04 +08001051
Jon Salz4712ac72013-02-07 17:12:05 +08001052 def clear_state(self, root=None):
Jon Salzd7550792013-07-12 05:49:27 +08001053 if root is None:
1054 root = self.test_list
Jon Salz6dc031d2013-06-19 13:06:23 +08001055 self.stop(root, reason='Clearing test state')
Jon Salz4712ac72013-02-07 17:12:05 +08001056 for f in root.walk():
1057 if f.is_leaf():
1058 f.update_state(status=TestState.UNTESTED)
1059
Jon Salz6dc031d2013-06-19 13:06:23 +08001060 def abort_active_tests(self, reason=None):
1061 self.kill_active_tests(True, reason=reason)
Jon Salz0697cbf2012-07-04 15:14:04 +08001062
1063 def main(self):
Jon Salzeff94182013-06-19 15:06:28 +08001064 syslog.openlog('goofy')
1065
Jon Salz0697cbf2012-07-04 15:14:04 +08001066 try:
Jon Salzd7550792013-07-12 05:49:27 +08001067 self.status = Status.INITIALIZING
Jon Salz0697cbf2012-07-04 15:14:04 +08001068 self.init()
1069 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +08001070 success=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001071 except:
1072 if self.event_log:
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001073 try:
Jon Salz0697cbf2012-07-04 15:14:04 +08001074 self.event_log.Log('goofy_init',
Ricky Liang45c73e72015-01-15 15:00:30 +08001075 success=False,
1076 trace=traceback.format_exc())
Jon Salz0697cbf2012-07-04 15:14:04 +08001077 except: # pylint: disable=W0702
1078 pass
1079 raise
1080
Jon Salzd7550792013-07-12 05:49:27 +08001081 self.status = Status.RUNNING
Jon Salzeff94182013-06-19 15:06:28 +08001082 syslog.syslog('Goofy (factory test harness) starting')
Jon Salz0697cbf2012-07-04 15:14:04 +08001083 self.run()
1084
1085 def update_system_info(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001086 """Updates system info."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001087 system_info = system.SystemInfo()
1088 self.state_instance.set_shared_data('system_info', system_info.__dict__)
1089 self.event_client.post_event(Event(Event.Type.SYSTEM_INFO,
Ricky Liang45c73e72015-01-15 15:00:30 +08001090 system_info=system_info.__dict__))
Jon Salz0697cbf2012-07-04 15:14:04 +08001091 logging.info('System info: %r', system_info.__dict__)
1092
Jon Salzeb42f0d2012-07-27 19:14:04 +08001093 def update_factory(self, auto_run_on_restart=False, post_update_hook=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001094 """Commences updating factory software.
Jon Salzeb42f0d2012-07-27 19:14:04 +08001095
1096 Args:
1097 auto_run_on_restart: Auto-run when the machine comes back up.
1098 post_update_hook: Code to call after update but immediately before
1099 restart.
1100
1101 Returns:
1102 Never if the update was successful (we just reboot).
1103 False if the update was unnecessary (no update available).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001104 """
Jon Salz6dc031d2013-06-19 13:06:23 +08001105 self.kill_active_tests(False, reason='Factory software update')
Jon Salza6711d72012-07-18 14:33:03 +08001106 self.cancel_pending_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08001107
Jon Salz5c344f62012-07-13 14:31:16 +08001108 def pre_update_hook():
1109 if auto_run_on_restart:
1110 self.state_instance.set_shared_data('tests_after_shutdown',
1111 FORCE_AUTO_RUN)
1112 self.state_instance.close()
1113
Jon Salzeb42f0d2012-07-27 19:14:04 +08001114 if updater.TryUpdate(pre_update_hook=pre_update_hook):
1115 if post_update_hook:
1116 post_update_hook()
1117 self.env.shutdown('reboot')
Jon Salz0697cbf2012-07-04 15:14:04 +08001118
Ricky Liang8fecf412014-05-22 10:56:14 +08001119 def handle_sigint(self, dummy_signum, dummy_frame): # pylint: disable=W0613
Jon Salz77c151e2012-08-28 07:20:37 +08001120 logging.error('Received SIGINT')
Peter Ammon1e1ec572014-06-26 17:56:32 -07001121 self.run_enqueue(None)
Jon Salz77c151e2012-08-28 07:20:37 +08001122 raise KeyboardInterrupt()
1123
Ricky Liang8fecf412014-05-22 10:56:14 +08001124 def handle_sigterm(self, dummy_signum, dummy_frame): # pylint: disable=W0613
1125 logging.error('Received SIGTERM')
Hung-Te Lin94ca4742014-07-09 20:13:50 +08001126 self.env.terminate()
1127 self.run_queue.put(None)
Ricky Liang8fecf412014-05-22 10:56:14 +08001128 raise RuntimeError('Received SIGTERM')
1129
Jon Salze12c2b32013-06-25 16:24:34 +08001130 def find_kcrashes(self):
1131 """Finds kcrash files, logs them, and marks them as seen."""
1132 seen_crashes = set(
1133 self.state_instance.get_shared_data('seen_crashes', optional=True)
1134 or [])
1135
1136 for path in glob.glob('/var/spool/crash/*'):
1137 if not os.path.isfile(path):
1138 continue
1139 if path in seen_crashes:
1140 continue
1141 try:
1142 stat = os.stat(path)
1143 mtime = utils.TimeString(stat.st_mtime)
1144 logging.info(
1145 'Found new crash file %s (%d bytes at %s)',
1146 path, stat.st_size, mtime)
1147 extra_log_args = {}
1148
1149 try:
1150 _, ext = os.path.splitext(path)
1151 if ext in ['.kcrash', '.meta']:
1152 ext = ext.replace('.', '')
1153 with open(path) as f:
1154 data = f.read(MAX_CRASH_FILE_SIZE)
1155 tell = f.tell()
1156 logging.info(
1157 'Contents of %s%s:%s',
1158 path,
1159 ('' if tell == stat.st_size
1160 else '(truncated to %d bytes)' % MAX_CRASH_FILE_SIZE),
1161 ('\n' + data).replace('\n', '\n ' + ext + '> '))
1162 extra_log_args['data'] = data
1163
1164 # Copy to /var/factory/kcrash for posterity
1165 kcrash_dir = factory.get_factory_root('kcrash')
1166 utils.TryMakeDirs(kcrash_dir)
1167 shutil.copy(path, kcrash_dir)
1168 logging.info('Copied to %s',
1169 os.path.join(kcrash_dir, os.path.basename(path)))
1170 finally:
1171 # Even if something goes wrong with the above, still try to
1172 # log to event log
1173 self.event_log.Log('crash_file',
1174 path=path, size=stat.st_size, mtime=mtime,
1175 **extra_log_args)
1176 except: # pylint: disable=W0702
1177 logging.exception('Unable to handle crash files %s', path)
1178 seen_crashes.add(path)
1179
1180 self.state_instance.set_shared_data('seen_crashes', list(seen_crashes))
1181
Jon Salz128b0932013-07-03 16:55:26 +08001182 def GetTestList(self, test_list_id):
1183 """Returns the test list with the given ID.
1184
1185 Raises:
1186 TestListError: The test list ID is not valid.
1187 """
1188 try:
1189 return self.test_lists[test_list_id]
1190 except KeyError:
1191 raise test_lists.TestListError(
1192 '%r is not a valid test list ID (available IDs are [%s])' % (
1193 test_list_id, ', '.join(sorted(self.test_lists.keys()))))
1194
1195 def InitTestLists(self):
1196 """Reads in all test lists and sets the active test list."""
Ricky Liang27051552014-05-04 14:22:26 +08001197 self.test_lists = test_lists.BuildAllTestLists(
1198 force_generic=(self.options.automation_mode is not None))
Jon Salzd7550792013-07-12 05:49:27 +08001199 logging.info('Loaded test lists: [%s]',
1200 test_lists.DescribeTestLists(self.test_lists))
Jon Salz128b0932013-07-03 16:55:26 +08001201
1202 if not self.options.test_list:
1203 self.options.test_list = test_lists.GetActiveTestListId()
1204
1205 if os.sep in self.options.test_list:
1206 # It's a path pointing to an old-style test list; use it.
1207 self.test_list = factory.read_test_list(self.options.test_list)
1208 else:
1209 self.test_list = self.GetTestList(self.options.test_list)
1210
1211 logging.info('Active test list: %s', self.test_list.test_list_id)
1212
1213 if isinstance(self.test_list, test_lists.OldStyleTestList):
1214 # Actually load it in. (See OldStyleTestList for an explanation
1215 # of why this is necessary.)
1216 self.test_list = self.test_list.Load()
1217
1218 self.test_list.state_instance = self.state_instance
1219
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001220 def init_hooks(self):
1221 """Initializes hooks.
1222
1223 Must run after self.test_list ready.
1224 """
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001225 module, cls = self.test_list.options.hooks_class.rsplit('.', 1)
1226 self.hooks = getattr(__import__(module, fromlist=[cls]), cls)()
1227 assert isinstance(self.hooks, factory.Hooks), (
Ricky Liang45c73e72015-01-15 15:00:30 +08001228 'hooks should be of type Hooks but is %r' % type(self.hooks))
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001229 self.hooks.test_list = self.test_list
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001230 self.hooks.OnCreatedTestList()
Shuo-Peng Liao52b90da2013-06-30 17:00:06 +08001231
Vic Yanga3cecf82014-12-26 00:44:21 -08001232 def init_ui(self):
1233 """Initialize UI."""
1234 self._ui_initialized = True
1235 if self.options.ui == 'chrome':
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001236 if self.options.monolithic:
Hung-Te Lin7bd55312014-12-30 16:43:36 +08001237 self.env.launch_chrome()
1238 else:
1239 # The presenter is responsible for launching Chrome. Let's just
1240 # wait here.
1241 self.env.controller_ready_for_ui()
Vic Yanga3cecf82014-12-26 00:44:21 -08001242 logging.info('Waiting for a web socket connection')
1243 self.web_socket_manager.wait()
1244
1245 # Wait for the test widget size to be set; this is done in
1246 # an asynchronous RPC so there is a small chance that the
1247 # web socket might be opened first.
1248 for _ in range(100): # 10 s
1249 try:
1250 if self.state_instance.get_shared_data('test_widget_size'):
1251 break
1252 except KeyError:
1253 pass # Retry
1254 time.sleep(0.1) # 100 ms
1255 else:
1256 logging.warn('Never received test_widget_size from UI')
1257
Hung-Te Lin7bd55312014-12-30 16:43:36 +08001258 logging.info('Waiting for a web socket connection')
1259 self.web_socket_manager.wait()
1260
Jon Salz0697cbf2012-07-04 15:14:04 +08001261 def init(self, args=None, env=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001262 """Initializes Goofy.
Jon Salz0697cbf2012-07-04 15:14:04 +08001263
1264 Args:
1265 args: A list of command-line arguments. Uses sys.argv if
1266 args is None.
1267 env: An Environment instance to use (or None to choose
1268 FakeChrootEnvironment or DUTEnvironment as appropriate).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001269 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001270 parser = OptionParser()
1271 parser.add_option('-v', '--verbose', dest='verbose',
Jon Salz8fa8e832012-07-13 19:04:09 +08001272 action='store_true',
1273 help='Enable debug logging')
Jon Salz0697cbf2012-07-04 15:14:04 +08001274 parser.add_option('--print_test_list', dest='print_test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001275 metavar='FILE',
1276 help='Read and print test list FILE, and exit')
Jon Salz0697cbf2012-07-04 15:14:04 +08001277 parser.add_option('--restart', dest='restart',
Jon Salz8fa8e832012-07-13 19:04:09 +08001278 action='store_true',
1279 help='Clear all test state')
Jon Salz0697cbf2012-07-04 15:14:04 +08001280 parser.add_option('--ui', dest='ui', type='choice',
Jon Salz7b5482e2014-08-04 17:48:41 +08001281 choices=['none', 'chrome'],
Jon Salz2f881df2013-02-01 17:00:35 +08001282 default='chrome',
Jon Salz8fa8e832012-07-13 19:04:09 +08001283 help='UI to use')
Jon Salz0697cbf2012-07-04 15:14:04 +08001284 parser.add_option('--ui_scale_factor', dest='ui_scale_factor',
Jon Salz8fa8e832012-07-13 19:04:09 +08001285 type='int', default=1,
1286 help=('Factor by which to scale UI '
1287 '(Chrome UI only)'))
Jon Salz0697cbf2012-07-04 15:14:04 +08001288 parser.add_option('--test_list', dest='test_list',
Jon Salz8fa8e832012-07-13 19:04:09 +08001289 metavar='FILE',
1290 help='Use FILE as test list')
Jon Salzc79a9982012-08-30 04:42:01 +08001291 parser.add_option('--dummy_shopfloor', action='store_true',
1292 help='Use a dummy shopfloor server')
Hung-Te Lincc41d2a2014-10-29 13:35:20 +08001293 parser.add_option('--dummy_connection_manager', action='store_true',
1294 help='Use a dummy connection manager')
Ricky Liang6fe218c2013-12-27 15:17:17 +08001295 parser.add_option('--automation-mode',
1296 choices=[m.lower() for m in AutomationMode],
Ricky Liang45c73e72015-01-15 15:00:30 +08001297 default='none', help='Factory test automation mode.')
Ricky Liang117484a2014-04-14 11:14:41 +08001298 parser.add_option('--no-auto-run-on-start', dest='auto_run_on_start',
1299 action='store_false', default=True,
1300 help=('do not automatically run the test list on goofy '
1301 'start; this is only valid when factory test '
1302 'automation is enabled'))
Chun-Ta Lina8dd3172014-11-26 16:15:13 +08001303 parser.add_option('--handshake_timeout', dest='handshake_timeout',
1304 type='float', default=0.3,
1305 help=('RPC timeout when doing handshake between device '
1306 'and presenter.'))
Vic Yang7d693c42014-09-14 09:52:39 +08001307 parser.add_option('--standalone', dest='standalone',
1308 action='store_true', default=False,
1309 help=('Assume the presenter is running on the same '
1310 'machines.'))
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001311 parser.add_option('--monolithic', dest='monolithic',
1312 action='store_true', default=False,
1313 help='Run in monolithic mode (without presenter)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001314 (self.options, self.args) = parser.parse_args(args)
1315
Hung-Te Lina846f602014-07-04 20:32:22 +08001316 signal.signal(signal.SIGINT, self.handle_sigint)
1317 # TODO(hungte) SIGTERM does not work properly without Telemetry and should
1318 # be fixed.
Hung-Te Lina846f602014-07-04 20:32:22 +08001319
Jon Salz46b89562012-07-05 11:49:22 +08001320 # Make sure factory directories exist.
1321 factory.get_log_root()
1322 factory.get_state_root()
1323 factory.get_test_data_root()
1324
Jon Salz0697cbf2012-07-04 15:14:04 +08001325 global _inited_logging # pylint: disable=W0603
1326 if not _inited_logging:
1327 factory.init_logging('goofy', verbose=self.options.verbose)
1328 _inited_logging = True
Jon Salz8fa8e832012-07-13 19:04:09 +08001329
Jon Salz0f996602012-10-03 15:26:48 +08001330 if self.options.print_test_list:
Joel Kitchingb85ed7f2014-10-08 18:24:39 +08001331 print(factory.read_test_list(
1332 self.options.print_test_list).__repr__(recursive=True))
Jon Salz0f996602012-10-03 15:26:48 +08001333 sys.exit(0)
1334
Jon Salzee85d522012-07-17 14:34:46 +08001335 event_log.IncrementBootSequence()
Jon Salzd15bbcf2013-05-21 17:33:57 +08001336 # Don't defer logging the initial event, so we can make sure
1337 # that device_id, reimage_id, etc. are all set up.
1338 self.event_log = EventLog('goofy', defer=False)
Jon Salz0697cbf2012-07-04 15:14:04 +08001339
Jon Salz0697cbf2012-07-04 15:14:04 +08001340 if env:
1341 self.env = env
1342 elif factory.in_chroot():
1343 self.env = test_environment.FakeChrootEnvironment()
1344 logging.warn(
Ricky Liang45c73e72015-01-15 15:00:30 +08001345 'Using chroot environment: will not actually run autotests')
Hung-Te Lina846f602014-07-04 20:32:22 +08001346 elif self.options.ui == 'chrome':
Ricky Liang09d66d82014-09-25 11:20:54 +08001347 self.env = test_environment.DUTEnvironment()
Jon Salz0697cbf2012-07-04 15:14:04 +08001348 self.env.goofy = self
Vic Yanga4931152014-08-11 16:36:24 -07001349 # web_socket_manager will be initialized later
1350 # pylint: disable=W0108
1351 self.env.has_sockets = lambda: self.web_socket_manager.has_sockets()
Jon Salz0697cbf2012-07-04 15:14:04 +08001352
1353 if self.options.restart:
1354 state.clear_state()
1355
Jon Salz0697cbf2012-07-04 15:14:04 +08001356 if self.options.ui_scale_factor != 1 and utils.in_qemu():
1357 logging.warn(
Ricky Liang45c73e72015-01-15 15:00:30 +08001358 'In QEMU; ignoring ui_scale_factor argument')
Jon Salz0697cbf2012-07-04 15:14:04 +08001359 self.options.ui_scale_factor = 1
1360
1361 logging.info('Started')
1362
Hung-Te Lin8f6a3782015-01-06 22:58:32 +08001363 if not self.options.monolithic:
Hung-Te Lin7bd55312014-12-30 16:43:36 +08001364 self.link_manager = PresenterLinkManager(
1365 check_interval=1,
1366 handshake_timeout=self.options.handshake_timeout,
1367 standalone=self.options.standalone)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001368
Jon Salz0697cbf2012-07-04 15:14:04 +08001369 self.start_state_server()
1370 self.state_instance.set_shared_data('hwid_cfg', get_hwid_cfg())
1371 self.state_instance.set_shared_data('ui_scale_factor',
Ricky Liang09216dc2013-02-22 17:26:45 +08001372 self.options.ui_scale_factor)
Jon Salz0697cbf2012-07-04 15:14:04 +08001373 self.last_shutdown_time = (
Ricky Liang45c73e72015-01-15 15:00:30 +08001374 self.state_instance.get_shared_data('shutdown_time', optional=True))
Jon Salz0697cbf2012-07-04 15:14:04 +08001375 self.state_instance.del_shared_data('shutdown_time', optional=True)
Jon Salzb19ea072013-02-07 16:35:00 +08001376 self.state_instance.del_shared_data('startup_error', optional=True)
Jon Salz0697cbf2012-07-04 15:14:04 +08001377
Ricky Liang6fe218c2013-12-27 15:17:17 +08001378 self.options.automation_mode = ParseAutomationMode(
1379 self.options.automation_mode)
1380 self.state_instance.set_shared_data('automation_mode',
1381 self.options.automation_mode)
1382 self.state_instance.set_shared_data(
1383 'automation_mode_prompt',
1384 AutomationModePrompt[self.options.automation_mode])
1385
Jon Salz128b0932013-07-03 16:55:26 +08001386 try:
1387 self.InitTestLists()
1388 except: # pylint: disable=W0702
1389 logging.exception('Unable to initialize test lists')
1390 self.state_instance.set_shared_data(
1391 'startup_error',
1392 'Unable to initialize test lists\n%s' % (
1393 traceback.format_exc()))
Jon Salzb19ea072013-02-07 16:35:00 +08001394 if self.options.ui == 'chrome':
1395 # Create an empty test list with default options so that the rest of
1396 # startup can proceed.
1397 self.test_list = factory.FactoryTestList(
1398 [], self.state_instance, factory.Options())
1399 else:
1400 # Bail with an error; no point in starting up.
1401 sys.exit('No valid test list; exiting.')
1402
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001403 self.init_hooks()
1404
Jon Salz822838b2013-03-25 17:32:33 +08001405 if self.test_list.options.clear_state_on_start:
1406 self.state_instance.clear_test_state()
1407
Jon Salz670ce062014-05-16 15:53:50 +08001408 # If the phase is invalid, this will raise a ValueError.
1409 phase.SetPersistentPhase(self.test_list.options.phase)
1410
Dean Liao85ca86f2014-11-03 12:28:08 +08001411 # For netboot firmware, mainfw_type should be 'netboot'.
1412 if (system.SystemInfo().mainfw_type != 'nonchrome' and
1413 system.SystemInfo().firmware_version is None):
Ricky Liang45c73e72015-01-15 15:00:30 +08001414 self.state_instance.set_shared_data(
1415 'startup_error',
Vic Yang9bd4f772013-06-04 17:34:00 +08001416 'Netboot firmware detected\n'
1417 'Connect Ethernet and reboot to re-image.\n'
1418 u'侦测到网路开机固件\n'
1419 u'请连接乙太网并重启')
1420
Jon Salz0697cbf2012-07-04 15:14:04 +08001421 if not self.state_instance.has_shared_data('ui_lang'):
1422 self.state_instance.set_shared_data('ui_lang',
Ricky Liang45c73e72015-01-15 15:00:30 +08001423 self.test_list.options.ui_lang)
Jon Salz0697cbf2012-07-04 15:14:04 +08001424 self.state_instance.set_shared_data(
Ricky Liang45c73e72015-01-15 15:00:30 +08001425 'test_list_options',
1426 self.test_list.options.__dict__)
Jon Salz0697cbf2012-07-04 15:14:04 +08001427 self.state_instance.test_list = self.test_list
1428
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001429 self.check_log_rotation()
Jon Salz83ef34b2012-11-01 19:46:35 +08001430
Jon Salz23926422012-09-01 03:38:13 +08001431 if self.options.dummy_shopfloor:
Ricky Liang45c73e72015-01-15 15:00:30 +08001432 os.environ[shopfloor.SHOPFLOOR_SERVER_ENV_VAR_NAME] = (
1433 'http://%s:%d/' %
Joel Kitchingb85ed7f2014-10-08 18:24:39 +08001434 (net_utils.LOCALHOST, shopfloor.DEFAULT_SERVER_PORT))
Jon Salz23926422012-09-01 03:38:13 +08001435 self.dummy_shopfloor = Spawn(
1436 [os.path.join(factory.FACTORY_PATH, 'bin', 'shopfloor_server'),
1437 '--dummy'])
1438 elif self.test_list.options.shopfloor_server_url:
1439 shopfloor.set_server_url(self.test_list.options.shopfloor_server_url)
Jon Salz2bf2f6b2013-03-28 18:49:26 +08001440 shopfloor.set_enabled(True)
Jon Salz23926422012-09-01 03:38:13 +08001441
Jon Salz0f996602012-10-03 15:26:48 +08001442 if self.test_list.options.time_sanitizer and not utils.in_chroot():
Jon Salz8fa8e832012-07-13 19:04:09 +08001443 self.time_sanitizer = time_sanitizer.TimeSanitizer(
Ricky Liang45c73e72015-01-15 15:00:30 +08001444 base_time=time_sanitizer.GetBaseTimeFromFile(
1445 # lsb-factory is written by the factory install shim during
1446 # installation, so it should have a good time obtained from
1447 # the mini-Omaha server. If it's not available, we'll use
1448 # /etc/lsb-factory (which will be much older, but reasonably
1449 # sane) and rely on a shopfloor sync to set a more accurate
1450 # time.
1451 '/usr/local/etc/lsb-factory',
1452 '/etc/lsb-release'))
Jon Salz8fa8e832012-07-13 19:04:09 +08001453 self.time_sanitizer.RunOnce()
1454
Vic Yangd8990da2013-06-27 16:57:43 +08001455 if self.test_list.options.check_cpu_usage_period_secs:
Ricky Liang45c73e72015-01-15 15:00:30 +08001456 self.cpu_usage_watcher = Spawn(
1457 ['py/tools/cpu_usage_monitor.py', '-p',
1458 str(self.test_list.options.check_cpu_usage_period_secs)],
Vic Yangd8990da2013-06-27 16:57:43 +08001459 cwd=factory.FACTORY_PATH)
1460
Jon Salz0697cbf2012-07-04 15:14:04 +08001461 self.init_states()
1462 self.start_event_server()
Hung-Te Lincc41d2a2014-10-29 13:35:20 +08001463
1464 if self.options.dummy_connection_manager:
1465 # Override network manager creation to dummy implmenetation.
1466 logging.info('Using dummy network manager (--dummy_connection_manager).')
1467 self.connection_manager = connection_manager.DummyConnectionManager()
1468 else:
1469 self.connection_manager = self.env.create_connection_manager(
1470 self.test_list.options.wlans,
1471 self.test_list.options.scan_wifi_period_secs)
1472
Jon Salz0697cbf2012-07-04 15:14:04 +08001473 # Note that we create a log watcher even if
1474 # sync_event_log_period_secs isn't set (no background
1475 # syncing), since we may use it to flush event logs as well.
1476 self.log_watcher = EventLogWatcher(
Ricky Liang45c73e72015-01-15 15:00:30 +08001477 self.test_list.options.sync_event_log_period_secs,
1478 event_log_db_file=None,
1479 handle_event_logs_callback=self.handle_event_logs)
Jon Salz0697cbf2012-07-04 15:14:04 +08001480 if self.test_list.options.sync_event_log_period_secs:
1481 self.log_watcher.StartWatchThread()
1482
Cheng-Yi Chianga0f6eff2014-01-09 18:27:22 +08001483 # Creates a system log manager to scan logs periocially.
1484 # A scan includes clearing logs and optionally syncing logs if
1485 # enable_syng_log is True. We kick it to sync logs.
1486 self.system_log_manager = SystemLogManager(
Ricky Liang45c73e72015-01-15 15:00:30 +08001487 sync_log_paths=self.test_list.options.sync_log_paths,
1488 sync_log_period_secs=self.test_list.options.sync_log_period_secs,
1489 scan_log_period_secs=self.test_list.options.scan_log_period_secs,
1490 clear_log_paths=self.test_list.options.clear_log_paths,
1491 clear_log_excluded_paths=self.test_list.options.clear_log_excluded_paths)
Cheng-Yi Chianga0f6eff2014-01-09 18:27:22 +08001492 self.system_log_manager.Start()
Cheng-Yi Chiang344b10f2013-05-03 16:44:03 +08001493
Jon Salz0697cbf2012-07-04 15:14:04 +08001494 self.update_system_info()
1495
Vic Yang4953fc12012-07-26 16:19:53 +08001496 assert ((self.test_list.options.min_charge_pct is None) ==
1497 (self.test_list.options.max_charge_pct is None))
Vic Yange83d9a12013-04-19 20:00:20 +08001498 if utils.in_chroot():
1499 logging.info('In chroot, ignoring charge manager and charge state')
Ricky Liangc392a1c2014-06-20 18:24:59 +08001500 elif (self.test_list.options.enable_charge_manager and
1501 self.test_list.options.min_charge_pct is not None):
Vic Yang4953fc12012-07-26 16:19:53 +08001502 self.charge_manager = ChargeManager(self.test_list.options.min_charge_pct,
1503 self.test_list.options.max_charge_pct)
Jon Salzad7353b2012-10-15 16:22:46 +08001504 system.SystemStatus.charge_manager = self.charge_manager
Cheng-Yi Chiangd8186952013-04-04 23:41:14 +08001505 else:
1506 # Goofy should set charger state to charge if charge_manager is disabled.
Dean Liao88b93192014-10-23 19:37:41 +08001507 self.charge()
Vic Yang4953fc12012-07-26 16:19:53 +08001508
Vic Yang6cee2472014-10-22 17:18:52 -07001509 if CoreDumpManager.CoreDumpEnabled():
1510 self.core_dump_manager = CoreDumpManager(
1511 self.test_list.options.core_dump_watchlist)
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001512
Jon Salz0697cbf2012-07-04 15:14:04 +08001513 os.environ['CROS_FACTORY'] = '1'
1514 os.environ['CROS_DISABLE_SITE_SYSINFO'] = '1'
1515
Shuo-Peng Liao1ff502e2013-06-30 18:37:02 +08001516 if not utils.in_chroot() and self.test_list.options.use_cpufreq_manager:
Ricky Liangecddbd42014-07-24 11:32:10 +08001517 logging.info('Enabling CPU frequency manager')
Jon Salzddf0d052013-06-18 12:52:44 +08001518 self.cpufreq_manager = CpufreqManager(event_log=self.event_log)
Jon Salzce6a7f82013-06-10 18:22:54 +08001519
Justin Chuang31b02432013-06-27 15:16:51 +08001520 # Startup hooks may want to skip some tests.
1521 self.update_skipped_tests()
Jon Salz416f9cc2013-05-10 18:32:50 +08001522
Jon Salze12c2b32013-06-25 16:24:34 +08001523 self.find_kcrashes()
1524
Shuo-Peng Liao268b40b2013-07-01 15:58:59 +08001525 # Should not move earlier.
1526 self.hooks.OnStartup()
1527
Ricky Liang36512a32014-07-25 11:47:04 +08001528 # Only after this point the Goofy backend is ready for UI connection.
1529 self.ready_for_ui_connection = True
1530
Ricky Liang650f6bf2012-09-28 13:22:54 +08001531 # Create download path for autotest beforehand or autotests run at
1532 # the same time might fail due to race condition.
1533 if not factory.in_chroot():
1534 utils.TryMakeDirs(os.path.join('/usr/local/autotest', 'tests',
1535 'download'))
1536
Jon Salz0697cbf2012-07-04 15:14:04 +08001537 def state_change_callback(test, test_state):
1538 self.event_client.post_event(
Ricky Liang4bff3e32014-02-20 18:46:11 +08001539 Event(Event.Type.STATE_CHANGE, path=test.path, state=test_state))
Jon Salz0697cbf2012-07-04 15:14:04 +08001540 self.test_list.state_change_callback = state_change_callback
Jon Salz73e0fd02012-04-04 11:46:38 +08001541
Vic Yange2c76a82014-10-30 12:48:19 -07001542 self.autotest_prespawner = prespawner.AutotestPrespawner()
1543 self.autotest_prespawner.start()
1544
1545 self.pytest_prespawner = prespawner.PytestPrespawner()
1546 self.pytest_prespawner.start()
Jon Salza6711d72012-07-18 14:33:03 +08001547
Ricky Liang48e47f92014-02-26 19:31:51 +08001548 tests_after_shutdown = self.state_instance.get_shared_data(
1549 'tests_after_shutdown', optional=True)
Jon Salz57717ca2012-04-04 16:47:25 +08001550
Jon Salz5c344f62012-07-13 14:31:16 +08001551 force_auto_run = (tests_after_shutdown == FORCE_AUTO_RUN)
1552 if not force_auto_run and tests_after_shutdown is not None:
Ricky Liang48e47f92014-02-26 19:31:51 +08001553 logging.info('Resuming tests after shutdown: %s', tests_after_shutdown)
Jon Salz0697cbf2012-07-04 15:14:04 +08001554 self.tests_to_run.extend(
Ricky Liang4bff3e32014-02-20 18:46:11 +08001555 self.test_list.lookup_path(t) for t in tests_after_shutdown)
Peter Ammon1e1ec572014-06-26 17:56:32 -07001556 self.run_enqueue(self.run_next_test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001557 else:
Jon Salz5c344f62012-07-13 14:31:16 +08001558 if force_auto_run or self.test_list.options.auto_run_on_start:
Ricky Liang117484a2014-04-14 11:14:41 +08001559 # If automation mode is enabled, allow suppress auto_run_on_start.
1560 if (self.options.automation_mode == 'NONE' or
1561 self.options.auto_run_on_start):
Peter Ammon1e1ec572014-06-26 17:56:32 -07001562 self.run_enqueue(
Ricky Liang117484a2014-04-14 11:14:41 +08001563 lambda: self.run_tests(self.test_list, untested_only=True))
Jon Salz5c344f62012-07-13 14:31:16 +08001564 self.state_instance.set_shared_data('tests_after_shutdown', None)
Ricky Liang4bff3e32014-02-20 18:46:11 +08001565 self.restore_active_run_state()
Hung-Te Linf2f78f72012-02-08 19:27:11 +08001566
Vic Yang08505c72015-01-06 17:01:53 -08001567 system.GetBoard().OnTestStart()
1568
Dean Liao592e4d52013-01-10 20:06:39 +08001569 self.may_disable_cros_shortcut_keys()
1570
1571 def may_disable_cros_shortcut_keys(self):
1572 test_options = self.test_list.options
1573 if test_options.disable_cros_shortcut_keys:
1574 logging.info('Filter ChromeOS shortcut keys.')
1575 self.key_filter = KeyFilter(
1576 unmap_caps_lock=test_options.disable_caps_lock,
1577 caps_lock_keycode=test_options.caps_lock_keycode)
1578 self.key_filter.Start()
1579
Jon Salz0e6532d2012-10-25 16:30:11 +08001580 def _should_sync_time(self, foreground=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001581 """Returns True if we should attempt syncing time with shopfloor.
Jon Salz0e6532d2012-10-25 16:30:11 +08001582
1583 Args:
1584 foreground: If True, synchronizes even if background syncing
1585 is disabled (e.g., in explicit sync requests from the
1586 SyncShopfloor test).
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001587 """
Jon Salz0e6532d2012-10-25 16:30:11 +08001588 return ((foreground or
1589 self.test_list.options.sync_time_period_secs) and
Jon Salz54882d02012-08-31 01:57:54 +08001590 self.time_sanitizer and
1591 (not self.time_synced) and
1592 (not factory.in_chroot()))
1593
Jon Salz0e6532d2012-10-25 16:30:11 +08001594 def sync_time_with_shopfloor_server(self, foreground=False):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001595 """Syncs time with shopfloor server, if not yet synced.
Jon Salz54882d02012-08-31 01:57:54 +08001596
Jon Salz0e6532d2012-10-25 16:30:11 +08001597 Args:
1598 foreground: If True, synchronizes even if background syncing
1599 is disabled (e.g., in explicit sync requests from the
1600 SyncShopfloor test).
1601
Jon Salz54882d02012-08-31 01:57:54 +08001602 Returns:
1603 False if no time sanitizer is available, or True if this sync (or a
1604 previous sync) succeeded.
1605
1606 Raises:
1607 Exception if unable to contact the shopfloor server.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001608 """
Jon Salz0e6532d2012-10-25 16:30:11 +08001609 if self._should_sync_time(foreground):
Jon Salz54882d02012-08-31 01:57:54 +08001610 self.time_sanitizer.SyncWithShopfloor()
1611 self.time_synced = True
1612 return self.time_synced
1613
Jon Salzb92c5112012-09-21 15:40:11 +08001614 def log_disk_space_stats(self):
Jon Salz18e0e022013-06-11 17:13:39 +08001615 if (utils.in_chroot() or
1616 not self.test_list.options.log_disk_space_period_secs):
Jon Salzb92c5112012-09-21 15:40:11 +08001617 return
1618
1619 now = time.time()
1620 if (self.last_log_disk_space_time and
1621 now - self.last_log_disk_space_time <
1622 self.test_list.options.log_disk_space_period_secs):
1623 return
1624 self.last_log_disk_space_time = now
1625
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001626 # Upload event if stateful partition usage is above threshold.
1627 # Stateful partition is mounted on /usr/local, while
1628 # encrypted stateful partition is mounted on /var.
1629 # If there are too much logs in the factory process,
1630 # these two partitions might get full.
Jon Salzb92c5112012-09-21 15:40:11 +08001631 try:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001632 vfs_infos = disk_space.GetAllVFSInfo()
1633 stateful_info, encrypted_info = None, None
1634 for vfs_info in vfs_infos.values():
1635 if '/usr/local' in vfs_info.mount_points:
1636 stateful_info = vfs_info
1637 if '/var' in vfs_info.mount_points:
1638 encrypted_info = vfs_info
1639
1640 stateful = disk_space.GetPartitionUsage(stateful_info)
1641 encrypted = disk_space.GetPartitionUsage(encrypted_info)
1642
Ricky Liang45c73e72015-01-15 15:00:30 +08001643 above_threshold = (
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001644 self.test_list.options.stateful_usage_threshold and
1645 max(stateful.bytes_used_pct,
1646 stateful.inodes_used_pct,
1647 encrypted.bytes_used_pct,
1648 encrypted.inodes_used_pct) >
Ricky Liang45c73e72015-01-15 15:00:30 +08001649 self.test_list.options.stateful_usage_threshold)
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001650
1651 if above_threshold:
1652 self.event_log.Log('stateful_partition_usage',
Ricky Liang45c73e72015-01-15 15:00:30 +08001653 partitions={
1654 'stateful': {
1655 'bytes_used_pct': FloatDigit(stateful.bytes_used_pct, 2),
1656 'inodes_used_pct': FloatDigit(stateful.inodes_used_pct, 2)},
1657 'encrypted_stateful': {
1658 'bytes_used_pct': FloatDigit(encrypted.bytes_used_pct, 2),
1659 'inodes_used_pct': FloatDigit(encrypted.inodes_used_pct, 2)}
1660 })
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001661 self.log_watcher.ScanEventLogs()
Cheng-Yi Chiang00798e72013-06-20 18:16:39 +08001662 if (not utils.in_chroot() and
1663 self.test_list.options.stateful_usage_above_threshold_action):
1664 Spawn(self.test_list.options.stateful_usage_above_threshold_action,
1665 call=True)
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001666
1667 message = disk_space.FormatSpaceUsedAll(vfs_infos)
Jon Salz3c493bb2013-02-07 17:24:58 +08001668 if message != self.last_log_disk_space_message:
Cheng-Yi Chiangd0406522013-04-01 15:40:18 +08001669 if above_threshold:
1670 logging.warning(message)
1671 else:
1672 logging.info(message)
Jon Salz3c493bb2013-02-07 17:24:58 +08001673 self.last_log_disk_space_message = message
Jon Salzb92c5112012-09-21 15:40:11 +08001674 except: # pylint: disable=W0702
1675 logging.exception('Unable to get disk space used')
1676
Justin Chuang83813982013-05-13 01:26:32 +08001677 def check_battery(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001678 """Checks the current battery status.
Justin Chuang83813982013-05-13 01:26:32 +08001679
1680 Logs current battery charging level and status to log. If the battery level
1681 is lower below warning_low_battery_pct, send warning event to shopfloor.
1682 If the battery level is lower below critical_low_battery_pct, flush disks.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001683 """
Justin Chuang83813982013-05-13 01:26:32 +08001684 if not self.test_list.options.check_battery_period_secs:
1685 return
1686
1687 now = time.time()
1688 if (self.last_check_battery_time and
1689 now - self.last_check_battery_time <
1690 self.test_list.options.check_battery_period_secs):
1691 return
1692 self.last_check_battery_time = now
1693
1694 message = ''
1695 log_level = logging.INFO
1696 try:
1697 power = system.GetBoard().power
1698 if not power.CheckBatteryPresent():
1699 message = 'Battery is not present'
1700 else:
1701 ac_present = power.CheckACPresent()
1702 charge_pct = power.GetChargePct(get_float=True)
1703 message = ('Current battery level %.1f%%, AC charger is %s' %
1704 (charge_pct, 'connected' if ac_present else 'disconnected'))
1705
1706 if charge_pct > self.test_list.options.critical_low_battery_pct:
1707 critical_low_battery = False
1708 else:
1709 critical_low_battery = True
1710 # Only sync disks when battery level is still above minimum
1711 # value. This can be used for offline analysis when shopfloor cannot
1712 # be connected.
1713 if charge_pct > MIN_BATTERY_LEVEL_FOR_DISK_SYNC:
1714 logging.warning('disk syncing for critical low battery situation')
1715 os.system('sync; sync; sync')
1716 else:
1717 logging.warning('disk syncing is cancelled '
1718 'because battery level is lower than %.1f',
1719 MIN_BATTERY_LEVEL_FOR_DISK_SYNC)
1720
1721 # Notify shopfloor server
1722 if (critical_low_battery or
1723 (not ac_present and
1724 charge_pct <= self.test_list.options.warning_low_battery_pct)):
1725 log_level = logging.WARNING
1726
1727 self.event_log.Log('low_battery',
1728 battery_level=charge_pct,
1729 charger_connected=ac_present,
1730 critical=critical_low_battery)
1731 self.log_watcher.KickWatchThread()
Cheng-Yi Chianga0f6eff2014-01-09 18:27:22 +08001732 if self.test_list.options.enable_sync_log:
1733 self.system_log_manager.KickToSync()
Ricky Liang45c73e72015-01-15 15:00:30 +08001734 except: # pylint: disable=W0702
Justin Chuang83813982013-05-13 01:26:32 +08001735 logging.exception('Unable to check battery or notify shopfloor')
1736 finally:
1737 if message != self.last_check_battery_message:
1738 logging.log(log_level, message)
1739 self.last_check_battery_message = message
1740
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001741 def check_core_dump(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001742 """Checks if there is any core dumped file.
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001743
1744 Removes unwanted core dump files immediately.
1745 Syncs those files matching watch list to server with a delay between
1746 each sync. After the files have been synced to server, deletes the files.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001747 """
Vic Yang6cee2472014-10-22 17:18:52 -07001748 if not self.core_dump_manager:
1749 return
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001750 core_dump_files = self.core_dump_manager.ScanFiles()
1751 if core_dump_files:
1752 now = time.time()
1753 if (self.last_kick_sync_time and now - self.last_kick_sync_time <
1754 self.test_list.options.kick_sync_min_interval_secs):
1755 return
1756 self.last_kick_sync_time = now
1757
1758 # Sends event to server
1759 self.event_log.Log('core_dumped', files=core_dump_files)
1760 self.log_watcher.KickWatchThread()
1761
1762 # Syncs files to server
Cheng-Yi Chianga0f6eff2014-01-09 18:27:22 +08001763 if self.test_list.options.enable_sync_log:
1764 self.system_log_manager.KickToSync(
Cheng-Yi Chiangd3516a32013-07-17 15:30:47 +08001765 core_dump_files, self.core_dump_manager.ClearFiles)
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001766
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001767 def check_log_rotation(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001768 """Checks log rotation file presence/absence according to test_list option.
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001769
1770 Touch /var/lib/cleanup_logs_paused if test_list.options.disable_log_rotation
1771 is True, delete it otherwise. This must be done in idle loop because
1772 autotest client will touch /var/lib/cleanup_logs_paused each time it runs
1773 an autotest.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001774 """
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001775 if utils.in_chroot():
1776 return
1777 try:
1778 if self.test_list.options.disable_log_rotation:
1779 open(CLEANUP_LOGS_PAUSED, 'w').close()
1780 else:
1781 file_utils.TryUnlink(CLEANUP_LOGS_PAUSED)
1782 except: # pylint: disable=W0702
1783 # Oh well. Logs an error (but no trace)
1784 logging.info(
1785 'Unable to %s %s: %s',
1786 'touch' if self.test_list.options.disable_log_rotation else 'delete',
1787 CLEANUP_LOGS_PAUSED, utils.FormatExceptionOnly())
1788
Jon Salz8fa8e832012-07-13 19:04:09 +08001789 def sync_time_in_background(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001790 """Writes out current time and tries to sync with shopfloor server."""
Jon Salzb22d1172012-08-06 10:38:57 +08001791 if not self.time_sanitizer:
1792 return
1793
1794 # Write out the current time.
1795 self.time_sanitizer.SaveTime()
1796
Jon Salz54882d02012-08-31 01:57:54 +08001797 if not self._should_sync_time():
Jon Salz8fa8e832012-07-13 19:04:09 +08001798 return
1799
1800 now = time.time()
1801 if self.last_sync_time and (
1802 now - self.last_sync_time <
1803 self.test_list.options.sync_time_period_secs):
1804 # Not yet time for another check.
1805 return
1806 self.last_sync_time = now
1807
1808 def target():
1809 try:
Jon Salz54882d02012-08-31 01:57:54 +08001810 self.sync_time_with_shopfloor_server()
Jon Salz8fa8e832012-07-13 19:04:09 +08001811 except: # pylint: disable=W0702
1812 # Oh well. Log an error (but no trace)
1813 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +08001814 'Unable to get time from shopfloor server: %s',
1815 utils.FormatExceptionOnly())
Jon Salz8fa8e832012-07-13 19:04:09 +08001816
1817 thread = threading.Thread(target=target)
1818 thread.daemon = True
1819 thread.start()
1820
Peter Ammon1e1ec572014-06-26 17:56:32 -07001821 def perform_periodic_tasks(self):
1822 """Override of base method to perform periodic work.
Vic Yang4953fc12012-07-26 16:19:53 +08001823
Peter Ammon1e1ec572014-06-26 17:56:32 -07001824 This method must not raise exceptions.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001825 """
Peter Ammon1e1ec572014-06-26 17:56:32 -07001826 super(Goofy, self).perform_periodic_tasks()
Jon Salzb22d1172012-08-06 10:38:57 +08001827
Vic Yang311ddb82012-09-26 12:08:28 +08001828 self.check_exclusive()
cychiang21886742012-07-05 15:16:32 +08001829 self.check_for_updates()
Jon Salz8fa8e832012-07-13 19:04:09 +08001830 self.sync_time_in_background()
Jon Salzb92c5112012-09-21 15:40:11 +08001831 self.log_disk_space_stats()
Justin Chuang83813982013-05-13 01:26:32 +08001832 self.check_battery()
Cheng-Yi Chiangcdfa4182013-05-05 03:20:19 +08001833 self.check_core_dump()
Cheng-Yi Chiang39d32ad2013-07-23 15:02:38 +08001834 self.check_log_rotation()
Jon Salz57717ca2012-04-04 16:47:25 +08001835
Jon Salzd15bbcf2013-05-21 17:33:57 +08001836 def handle_event_logs(self, chunks):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001837 """Callback for event watcher.
Jon Salz258a40c2012-04-19 12:34:01 +08001838
Jon Salz0697cbf2012-07-04 15:14:04 +08001839 Attempts to upload the event logs to the shopfloor server.
Vic Yang93027612013-05-06 02:42:49 +08001840
1841 Args:
Jon Salzd15bbcf2013-05-21 17:33:57 +08001842 chunks: A list of Chunk objects.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001843 """
Vic Yang93027612013-05-06 02:42:49 +08001844 first_exception = None
1845 exception_count = 0
1846
Jon Salzd15bbcf2013-05-21 17:33:57 +08001847 for chunk in chunks:
Vic Yang93027612013-05-06 02:42:49 +08001848 try:
Jon Salzcddb6402013-05-23 12:56:42 +08001849 description = 'event logs (%s)' % str(chunk)
Vic Yang93027612013-05-06 02:42:49 +08001850 start_time = time.time()
1851 shopfloor_client = shopfloor.get_instance(
Ricky Liang45c73e72015-01-15 15:00:30 +08001852 detect=True,
1853 timeout=self.test_list.options.shopfloor_timeout_secs)
1854 shopfloor_client.UploadEvent(chunk.log_name + '.' +
Jon Salzd15bbcf2013-05-21 17:33:57 +08001855 event_log.GetReimageId(),
1856 Binary(chunk.chunk))
Vic Yang93027612013-05-06 02:42:49 +08001857 logging.info(
Ricky Liang45c73e72015-01-15 15:00:30 +08001858 'Successfully synced %s in %.03f s',
1859 description, time.time() - start_time)
1860 except: # pylint: disable=W0702
Jon Salzd15bbcf2013-05-21 17:33:57 +08001861 first_exception = (first_exception or (chunk.log_name + ': ' +
Vic Yang93027612013-05-06 02:42:49 +08001862 utils.FormatExceptionOnly()))
1863 exception_count += 1
1864
1865 if exception_count:
1866 if exception_count == 1:
1867 msg = 'Log upload failed: %s' % first_exception
1868 else:
1869 msg = '%d log upload failed; first is: %s' % (
1870 exception_count, first_exception)
1871 raise Exception(msg)
1872
Ricky Liang45c73e72015-01-15 15:00:30 +08001873 def run_tests_with_status(self, statuses_to_run, starting_at=None, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001874 """Runs all top-level tests with a particular status.
Jon Salz0405ab52012-03-16 15:26:52 +08001875
Jon Salz0697cbf2012-07-04 15:14:04 +08001876 All active tests, plus any tests to re-run, are reset.
Jon Salz57717ca2012-04-04 16:47:25 +08001877
Jon Salz0697cbf2012-07-04 15:14:04 +08001878 Args:
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001879 statuses_to_run: The particular status that caller wants to run.
Jon Salz0697cbf2012-07-04 15:14:04 +08001880 starting_at: If provided, only auto-runs tests beginning with
1881 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001882 root: The root of tests to run. If not provided, it will be
1883 the root of all tests.
1884 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001885 root = root or self.test_list
Jon Salz57717ca2012-04-04 16:47:25 +08001886
Jon Salz0697cbf2012-07-04 15:14:04 +08001887 if starting_at:
1888 # Make sure they passed a test, not a string.
1889 assert isinstance(starting_at, factory.FactoryTest)
Jon Salz0405ab52012-03-16 15:26:52 +08001890
Jon Salz0697cbf2012-07-04 15:14:04 +08001891 tests_to_reset = []
1892 tests_to_run = []
Jon Salz0405ab52012-03-16 15:26:52 +08001893
Jon Salz0697cbf2012-07-04 15:14:04 +08001894 found_starting_at = False
Jon Salz0405ab52012-03-16 15:26:52 +08001895
Jon Salz0697cbf2012-07-04 15:14:04 +08001896 for test in root.get_top_level_tests():
1897 if starting_at:
1898 if test == starting_at:
1899 # We've found starting_at; do auto-run on all
1900 # subsequent tests.
1901 found_starting_at = True
1902 if not found_starting_at:
1903 # Don't start this guy yet
1904 continue
Jon Salz0405ab52012-03-16 15:26:52 +08001905
Jon Salz0697cbf2012-07-04 15:14:04 +08001906 status = test.get_state().status
1907 if status == TestState.ACTIVE or status in statuses_to_run:
1908 # Reset the test (later; we will need to abort
1909 # all active tests first).
1910 tests_to_reset.append(test)
1911 if status in statuses_to_run:
1912 tests_to_run.append(test)
Jon Salz0405ab52012-03-16 15:26:52 +08001913
Jon Salz6dc031d2013-06-19 13:06:23 +08001914 self.abort_active_tests('Operator requested run/re-run of certain tests')
Jon Salz258a40c2012-04-19 12:34:01 +08001915
Jon Salz0697cbf2012-07-04 15:14:04 +08001916 # Reset all statuses of the tests to run (in case any tests were active;
1917 # we want them to be run again).
1918 for test_to_reset in tests_to_reset:
1919 for test in test_to_reset.walk():
1920 test.update_state(status=TestState.UNTESTED)
Jon Salz57717ca2012-04-04 16:47:25 +08001921
Jon Salz0697cbf2012-07-04 15:14:04 +08001922 self.run_tests(tests_to_run, untested_only=True)
Jon Salz0405ab52012-03-16 15:26:52 +08001923
Jon Salz0697cbf2012-07-04 15:14:04 +08001924 def restart_tests(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001925 """Restarts all tests."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001926 root = root or self.test_list
Jon Salz0405ab52012-03-16 15:26:52 +08001927
Jon Salz6dc031d2013-06-19 13:06:23 +08001928 self.abort_active_tests('Operator requested restart of certain tests')
Jon Salz0697cbf2012-07-04 15:14:04 +08001929 for test in root.walk():
Ricky Liangfea4ac92014-08-21 11:55:59 +08001930 test.update_state(status=TestState.UNTESTED)
Jon Salz0697cbf2012-07-04 15:14:04 +08001931 self.run_tests(root)
Hung-Te Lin96632362012-03-20 21:14:18 +08001932
Jon Salz0697cbf2012-07-04 15:14:04 +08001933 def auto_run(self, starting_at=None, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001934 """"Auto-runs" tests that have not been run yet.
Hung-Te Lin96632362012-03-20 21:14:18 +08001935
Jon Salz0697cbf2012-07-04 15:14:04 +08001936 Args:
1937 starting_at: If provide, only auto-runs tests beginning with
1938 this test.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001939 root: If provided, the root of tests to run. If not provided, the root
1940 will be test_list (root of all tests).
1941 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001942 root = root or self.test_list
1943 self.run_tests_with_status([TestState.UNTESTED, TestState.ACTIVE],
Ricky Liang45c73e72015-01-15 15:00:30 +08001944 starting_at=starting_at,
1945 root=root)
Jon Salz968e90b2012-03-18 16:12:43 +08001946
Jon Salz0697cbf2012-07-04 15:14:04 +08001947 def re_run_failed(self, root=None):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001948 """Re-runs failed tests."""
Jon Salz0697cbf2012-07-04 15:14:04 +08001949 root = root or self.test_list
1950 self.run_tests_with_status([TestState.FAILED], root=root)
Jon Salz57717ca2012-04-04 16:47:25 +08001951
Jon Salz0697cbf2012-07-04 15:14:04 +08001952 def show_review_information(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001953 """Event handler for showing review information screen.
Jon Salz57717ca2012-04-04 16:47:25 +08001954
Peter Ammon1e1ec572014-06-26 17:56:32 -07001955 The information screen is rendered by main UI program (ui.py), so in
Jon Salz0697cbf2012-07-04 15:14:04 +08001956 goofy we only need to kill all active tests, set them as untested, and
1957 clear remaining tests.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001958 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001959 self.kill_active_tests(False)
Jon Salza6711d72012-07-18 14:33:03 +08001960 self.cancel_pending_tests()
Jon Salz57717ca2012-04-04 16:47:25 +08001961
Jon Salz0697cbf2012-07-04 15:14:04 +08001962 def handle_switch_test(self, event):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001963 """Switches to a particular test.
Jon Salz0405ab52012-03-16 15:26:52 +08001964
Ricky Liang6fe218c2013-12-27 15:17:17 +08001965 Args:
1966 event: The SWITCH_TEST event.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001967 """
Jon Salz0697cbf2012-07-04 15:14:04 +08001968 test = self.test_list.lookup_path(event.path)
1969 if not test:
1970 logging.error('Unknown test %r', event.key)
1971 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001972
Jon Salz0697cbf2012-07-04 15:14:04 +08001973 invoc = self.invocations.get(test)
1974 if invoc and test.backgroundable:
1975 # Already running: just bring to the front if it
1976 # has a UI.
1977 logging.info('Setting visible test to %s', test.path)
Jon Salz36fbbb52012-07-05 13:45:06 +08001978 self.set_visible_test(test)
Jon Salz0697cbf2012-07-04 15:14:04 +08001979 return
Jon Salz73e0fd02012-04-04 11:46:38 +08001980
Jon Salz6dc031d2013-06-19 13:06:23 +08001981 self.abort_active_tests('Operator requested abort (switch_test)')
Jon Salz0697cbf2012-07-04 15:14:04 +08001982 for t in test.walk():
1983 t.update_state(status=TestState.UNTESTED)
Jon Salz73e0fd02012-04-04 11:46:38 +08001984
Jon Salz0697cbf2012-07-04 15:14:04 +08001985 if self.test_list.options.auto_run_on_keypress:
1986 self.auto_run(starting_at=test)
1987 else:
1988 self.run_tests(test)
Jon Salz73e0fd02012-04-04 11:46:38 +08001989
Jon Salz0697cbf2012-07-04 15:14:04 +08001990 def wait(self):
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001991 """Waits for all pending invocations.
Jon Salz0697cbf2012-07-04 15:14:04 +08001992
1993 Useful for testing.
Cheng-Yi Chiang1e3e2692013-12-24 18:02:36 +08001994 """
Jon Salz1acc8742012-07-17 17:45:55 +08001995 while self.invocations:
1996 for k, v in self.invocations.iteritems():
1997 logging.info('Waiting for %s to complete...', k)
1998 v.thread.join()
1999 self.reap_completed_tests()
Jon Salz0697cbf2012-07-04 15:14:04 +08002000
Hung-Te Linf2f78f72012-02-08 19:27:11 +08002001if __name__ == '__main__':
Peter Ammona3d298c2014-09-23 10:11:02 -07002002 Goofy.run_main_and_exit()