1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright 2016 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Li# repohooks/pre-upload.py currently does not run pylint. But for developers who 7*9c5db199SXin Li# want to check their code manually we disable several harmless pylint warnings 8*9c5db199SXin Li# which just distract from more serious remaining issues. 9*9c5db199SXin Li# 10*9c5db199SXin Li# The instance variables _host and _install_paths are not defined in __init__(). 11*9c5db199SXin Li# pylint: disable=attribute-defined-outside-init 12*9c5db199SXin Li# 13*9c5db199SXin Li# Many short variable names don't follow the naming convention. 14*9c5db199SXin Li# pylint: disable=invalid-name 15*9c5db199SXin Li# 16*9c5db199SXin Li# _parse_result() and _dir_size() don't access self and could be functions. 17*9c5db199SXin Li# pylint: disable=no-self-use 18*9c5db199SXin Li 19*9c5db199SXin Lifrom collections import namedtuple 20*9c5db199SXin Liimport errno 21*9c5db199SXin Liimport glob 22*9c5db199SXin Liimport hashlib 23*9c5db199SXin Liimport logging 24*9c5db199SXin Liimport os 25*9c5db199SXin Liimport pipes 26*9c5db199SXin Liimport re 27*9c5db199SXin Liimport shutil 28*9c5db199SXin Liimport stat 29*9c5db199SXin Liimport subprocess 30*9c5db199SXin Liimport tempfile 31*9c5db199SXin Liimport time 32*9c5db199SXin Liimport six.moves.urllib_parse as urlparse 33*9c5db199SXin Li 34*9c5db199SXin Lifrom autotest_lib.client.bin import utils as client_utils 35*9c5db199SXin Lifrom autotest_lib.client.common_lib import error 36*9c5db199SXin Lifrom autotest_lib.server import test 37*9c5db199SXin Lifrom autotest_lib.server import utils 38*9c5db199SXin Lifrom autotest_lib.server.cros.tradefed import adb as adb_utils 39*9c5db199SXin Lifrom autotest_lib.server.cros.tradefed import cts_expected_failure_parser 40*9c5db199SXin Lifrom autotest_lib.server.cros.tradefed import tradefed_chromelogin as login 41*9c5db199SXin Lifrom autotest_lib.server.cros.tradefed import tradefed_constants as constants 42*9c5db199SXin Lifrom autotest_lib.server.cros.tradefed import tradefed_utils 43*9c5db199SXin Lifrom autotest_lib.server.cros.tradefed import tradefed_prerequisite 44*9c5db199SXin Lifrom autotest_lib.server.autotest import OFFLOAD_ENVVAR 45*9c5db199SXin Li 46*9c5db199SXin Li# TODO(kinaba): Move to tradefed_utils together with the setup/cleanup methods. 47*9c5db199SXin LiMediaAsset = namedtuple('MediaAssetInfo', ['uri', 'localpath']) 48*9c5db199SXin Li 49*9c5db199SXin Li 50*9c5db199SXin Liclass TradefedTest(test.test): 51*9c5db199SXin Li """Base class to prepare DUT to run tests via tradefed.""" 52*9c5db199SXin Li version = 1 53*9c5db199SXin Li 54*9c5db199SXin Li # Default and upperbounds of max_retry, based on board and revision 55*9c5db199SXin Li # after branching (that is, 'y' of R74-12345.y.z). 56*9c5db199SXin Li # 57*9c5db199SXin Li # By default, 0<=y<1 does 5 retries and 1<=y does 10. The |max_retry| 58*9c5db199SXin Li # parameter in control files can override the count, within the 59*9c5db199SXin Li # _BRANCH_MAX_RETRY limit below. 60*9c5db199SXin Li _BRANCH_DEFAULT_RETRY = [(0, 5), (1, 10)] # dev=5, beta=stable=10 61*9c5db199SXin Li _BRANCH_MAX_RETRY = [(0, 12), (1, 30), # dev=12, beta=30, stable=99 62*9c5db199SXin Li (constants.APPROXIMATE_STABLE_BRANCH_NUMBER, 99)] 63*9c5db199SXin Li # TODO(kinaba): betty-arcnext 64*9c5db199SXin Li _BOARD_MAX_RETRY = {'betty': 0} 65*9c5db199SXin Li 66*9c5db199SXin Li _SHARD_CMD = None 67*9c5db199SXin Li _board_arch = None 68*9c5db199SXin Li _board_name = None 69*9c5db199SXin Li _model_name = None 70*9c5db199SXin Li _release_branch_number = None # The 'y' of OS version Rxx-xxxxx.y.z 71*9c5db199SXin Li _android_version = None 72*9c5db199SXin Li _first_api_level = None 73*9c5db199SXin Li _num_media_bundles = 0 74*9c5db199SXin Li _abilist = [] 75*9c5db199SXin Li 76*9c5db199SXin Li # A job will be aborted after 16h. Subtract 30m for setup/teardown. 77*9c5db199SXin Li _MAX_LAB_JOB_LENGTH_IN_SEC = 16 * 60 * 60 - 30 * 60 78*9c5db199SXin Li _job_deadline = None 79*9c5db199SXin Li 80*9c5db199SXin Li # Currently this is only used for dependency injection for testing. 81*9c5db199SXin Li def __init__(self, *args, **kwargs): 82*9c5db199SXin Li super().__init__(*args) 83*9c5db199SXin Li self._adb = kwargs.get('adb', adb_utils.Adb()) 84*9c5db199SXin Li 85*9c5db199SXin Li def _log_java_version(self): 86*9c5db199SXin Li """Log java version to debug failures due to version mismatch.""" 87*9c5db199SXin Li utils.run( 88*9c5db199SXin Li 'java', 89*9c5db199SXin Li args=('-version',), 90*9c5db199SXin Li ignore_status=False, 91*9c5db199SXin Li verbose=True, 92*9c5db199SXin Li stdout_tee=utils.TEE_TO_LOGS, 93*9c5db199SXin Li stderr_tee=utils.TEE_TO_LOGS) 94*9c5db199SXin Li 95*9c5db199SXin Li def initialize(self, 96*9c5db199SXin Li bundle=None, 97*9c5db199SXin Li uri=None, 98*9c5db199SXin Li host=None, 99*9c5db199SXin Li hosts=None, 100*9c5db199SXin Li max_retry=None, 101*9c5db199SXin Li load_waivers=True, 102*9c5db199SXin Li retry_manual_tests=False, 103*9c5db199SXin Li warn_on_test_retry=True, 104*9c5db199SXin Li hard_reboot_on_failure=False, 105*9c5db199SXin Li use_jdk9=False, 106*9c5db199SXin Li use_old_adb=False): 107*9c5db199SXin Li """Sets up the tools and binary bundles for the test.""" 108*9c5db199SXin Li if utils.is_in_container() and not client_utils.is_moblab(): 109*9c5db199SXin Li self._job_deadline = time.time() + self._MAX_LAB_JOB_LENGTH_IN_SEC 110*9c5db199SXin Li 111*9c5db199SXin Li self._install_paths = [] 112*9c5db199SXin Li # TODO(pwang): Remove host if we enable multiple hosts everywhere. 113*9c5db199SXin Li self._hosts = [host] if host else hosts 114*9c5db199SXin Li for host in self._hosts: 115*9c5db199SXin Li logging.info('Hostname: %s', host.host_port) 116*9c5db199SXin Li self._verify_hosts() 117*9c5db199SXin Li 118*9c5db199SXin Li self._max_retry = self._get_max_retry(max_retry) 119*9c5db199SXin Li self._warn_on_test_retry = warn_on_test_retry 120*9c5db199SXin Li # Tests in the lab run within individual lxc container instances. 121*9c5db199SXin Li if utils.is_in_container(): 122*9c5db199SXin Li cache_root = constants.TRADEFED_CACHE_CONTAINER 123*9c5db199SXin Li else: 124*9c5db199SXin Li cache_root = constants.TRADEFED_CACHE_LOCAL 125*9c5db199SXin Li 126*9c5db199SXin Li # The content of the cache survives across jobs. 127*9c5db199SXin Li self._safe_makedirs(cache_root) 128*9c5db199SXin Li self._tradefed_cache = os.path.join(cache_root, 'cache') 129*9c5db199SXin Li self._tradefed_cache_lock = os.path.join(cache_root, 'lock') 130*9c5db199SXin Li self._tradefed_cache_dirty = os.path.join(cache_root, 'dirty') 131*9c5db199SXin Li # The content of the install location does not survive across jobs and 132*9c5db199SXin Li # is isolated (by using a unique path)_against other autotest instances. 133*9c5db199SXin Li # This is not needed for the lab, but if somebody wants to run multiple 134*9c5db199SXin Li # TradedefTest instance. 135*9c5db199SXin Li self._tradefed_install = tempfile.mkdtemp( 136*9c5db199SXin Li prefix=constants.TRADEFED_PREFIX) 137*9c5db199SXin Li # Under lxc the cache is shared between multiple autotest/tradefed 138*9c5db199SXin Li # instances. We need to synchronize access to it. All binaries are 139*9c5db199SXin Li # installed through the (shared) cache into the local (unshared) 140*9c5db199SXin Li # lxc/autotest instance storage. 141*9c5db199SXin Li # If clearing the cache it must happen before all downloads. 142*9c5db199SXin Li self._clean_download_cache_if_needed() 143*9c5db199SXin Li # Set permissions (rwxr-xr-x) to the executable binaries. 144*9c5db199SXin Li permission = ( 145*9c5db199SXin Li stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH 146*9c5db199SXin Li | stat.S_IXOTH) 147*9c5db199SXin Li 148*9c5db199SXin Li adb_dir = constants.ADB_DIR_OLD if use_old_adb else constants.ADB_DIR 149*9c5db199SXin Li self._install_files(adb_dir, constants.ADB_FILES, permission) 150*9c5db199SXin Li self._install_files(constants.SDK_TOOLS_DIR, 151*9c5db199SXin Li constants.SDK_TOOLS_FILES, permission) 152*9c5db199SXin Li 153*9c5db199SXin Li # If use_jdk9 is set true, use jdk9 than default jdk8. 154*9c5db199SXin Li if use_jdk9: 155*9c5db199SXin Li if utils.is_in_container() and not client_utils.is_moblab(): 156*9c5db199SXin Li logging.info('Lab: switching to JDK9') 157*9c5db199SXin Li try: 158*9c5db199SXin Li os.environ['JAVA_HOME'] = '/usr/lib/jvm/jdk-9.0.4' 159*9c5db199SXin Li os.environ['PATH'] = os.environ['JAVA_HOME']\ 160*9c5db199SXin Li + '/bin:' + os.environ['PATH'] 161*9c5db199SXin Li logging.info( 162*9c5db199SXin Li subprocess.check_output(['java', '-version'], 163*9c5db199SXin Li stderr=subprocess.STDOUT)) 164*9c5db199SXin Li except OSError: 165*9c5db199SXin Li logging.error('Can\'t change current PATH directory') 166*9c5db199SXin Li else: 167*9c5db199SXin Li logging.info('Non-lab environment: should be using JDK9+') 168*9c5db199SXin Li 169*9c5db199SXin Li # TODO(kinaba): Remove the hack and fully enable the feature. 170*9c5db199SXin Li # For release branches (Rx-yyyyy.3.0 or above), always use the 171*9c5db199SXin Li # official build instead of the release build. See b/210369548 172*9c5db199SXin Li if uri == 'DEV' and self._get_release_branch_number() >= 3: 173*9c5db199SXin Li uri = 'LATEST' 174*9c5db199SXin Li # Install the tradefed bundle. 175*9c5db199SXin Li bundle_install_path = self._install_bundle( 176*9c5db199SXin Li self._get_bundle_url(uri, bundle)) 177*9c5db199SXin Li self._repository = os.path.join(bundle_install_path, 178*9c5db199SXin Li self._get_tradefed_base_dir()) 179*9c5db199SXin Li 180*9c5db199SXin Li # Load expected test failures to exclude them from re-runs. 181*9c5db199SXin Li self._waivers = set() 182*9c5db199SXin Li if load_waivers: 183*9c5db199SXin Li self._waivers.update( 184*9c5db199SXin Li self._get_expected_failures('expectations', bundle)) 185*9c5db199SXin Li if not retry_manual_tests: 186*9c5db199SXin Li self._waivers.update( 187*9c5db199SXin Li self._get_expected_failures('manual_tests', bundle)) 188*9c5db199SXin Li 189*9c5db199SXin Li # Load modules with no tests. 190*9c5db199SXin Li self._notest_modules = self._get_expected_failures('notest_modules', 191*9c5db199SXin Li bundle) 192*9c5db199SXin Li self._hard_reboot_on_failure = hard_reboot_on_failure 193*9c5db199SXin Li 194*9c5db199SXin Li def _output_perf(self): 195*9c5db199SXin Li """Output performance values.""" 196*9c5db199SXin Li base = self._default_tradefed_base_dir() 197*9c5db199SXin Li path = tradefed_utils.get_test_result_xml_path(base) 198*9c5db199SXin Li if path: 199*9c5db199SXin Li for metric in tradefed_utils.get_perf_metrics_from_test_result_xml( 200*9c5db199SXin Li path, self.resultsdir): 201*9c5db199SXin Li self.output_perf_value(**metric) 202*9c5db199SXin Li 203*9c5db199SXin Li def _prepare_synchronous_offloads(self): 204*9c5db199SXin Li """ 205*9c5db199SXin Li Copy files needed for APFE to synchronous offload dir, with some 206*9c5db199SXin Li structure to make the post-job postprocessing simpler. 207*9c5db199SXin Li """ 208*9c5db199SXin Li testname = os.path.basename(self.outputdir) 209*9c5db199SXin Li # This is yyyy.mm.dd_hh.mm.ss (start time) 210*9c5db199SXin Li timestamp_pattern = ("[0-9][0-9][0-9][0-9].[0-9][0-9].[0-9][0-9]" + 211*9c5db199SXin Li "_[0-9][0-9].[0-9][0-9].[0-9][0-9]") 212*9c5db199SXin Li time_glob = os.path.join( 213*9c5db199SXin Li self._default_tradefed_base_dir(), timestamp_pattern 214*9c5db199SXin Li ) 215*9c5db199SXin Li for dirpath in glob.glob(time_glob): 216*9c5db199SXin Li timestamp = os.path.basename(dirpath) 217*9c5db199SXin Li locs = [os.path.join(dirpath, f) for f in ["test_result.xml", 218*9c5db199SXin Li "testResult.xml"]] 219*9c5db199SXin Li for f in locs: 220*9c5db199SXin Li if os.path.exists(f): 221*9c5db199SXin Li subdirs = self._subdirs(f, testname, timestamp) 222*9c5db199SXin Li self._copy_to_offload_dir(f, subdirs) 223*9c5db199SXin Li for z in glob.glob(time_glob+".zip"): 224*9c5db199SXin Li self._copy_to_offload_dir(z, self._subdirs(z, testname)) 225*9c5db199SXin Li 226*9c5db199SXin Li def _copy_to_offload_dir(self, src_path, subdirs, recursive=True): 227*9c5db199SXin Li target = os.path.join(os.getenv(OFFLOAD_ENVVAR), *subdirs) 228*9c5db199SXin Li self._safe_makedirs(target) 229*9c5db199SXin Li if not recursive or os.path.isfile(src_path): 230*9c5db199SXin Li return shutil.copy2(src_path, str(target)) 231*9c5db199SXin Li return shutil.copytree(src_path, str(target)) 232*9c5db199SXin Li 233*9c5db199SXin Li def _subdirs(self, path, testname, timestamp=""): 234*9c5db199SXin Li # CTS results from bvt-arc suites need to be sent to the 235*9c5db199SXin Li # specially-designated bucket for early EDI entries in APFE, 236*9c5db199SXin Li # but only there. 237*9c5db199SXin Li dest = "BVT" if 'bvt-arc' in path else "CTS" 238*9c5db199SXin Li return ["APFE", dest, testname, timestamp] 239*9c5db199SXin Li 240*9c5db199SXin Li def cleanup(self): 241*9c5db199SXin Li """Cleans up any dirtied state.""" 242*9c5db199SXin Li 243*9c5db199SXin Li # We also run a postprocess result and performance data 244*9c5db199SXin Li # offloading here so that WARN and FAIL runs also run the 245*9c5db199SXin Li # steps. postprocess() method only runs for PASSing jobs. 246*9c5db199SXin Li self._prepare_synchronous_offloads() 247*9c5db199SXin Li self._output_perf() 248*9c5db199SXin Li 249*9c5db199SXin Li try: 250*9c5db199SXin Li # Clean up test data that may not be deletable on previous 251*9c5db199SXin Li # ChromeOS versions. See b/170276268. 252*9c5db199SXin Li self._run_commands([ 253*9c5db199SXin Li 'cryptohome --action=remove --force [email protected]' 254*9c5db199SXin Li ], 255*9c5db199SXin Li ignore_status=True) 256*9c5db199SXin Li except: 257*9c5db199SXin Li logging.error('Failed to clean up the test account.') 258*9c5db199SXin Li 259*9c5db199SXin Li self._kill_adb_server() 260*9c5db199SXin Li 261*9c5db199SXin Li if hasattr(self, '_tradefed_install'): 262*9c5db199SXin Li logging.info('Cleaning up %s.', self._tradefed_install) 263*9c5db199SXin Li try: 264*9c5db199SXin Li shutil.rmtree(self._tradefed_install) 265*9c5db199SXin Li except IOError: 266*9c5db199SXin Li pass 267*9c5db199SXin Li 268*9c5db199SXin Li def _kill_adb_server(self): 269*9c5db199SXin Li # Kill any lingering adb servers. 270*9c5db199SXin Li try: 271*9c5db199SXin Li self._adb.run(None, 272*9c5db199SXin Li verbose=True, 273*9c5db199SXin Li args=('kill-server', ), 274*9c5db199SXin Li timeout=constants.ADB_KILL_SERVER_TIMEOUT_SECONDS) 275*9c5db199SXin Li except error.CmdTimeoutError as e: 276*9c5db199SXin Li logging.warn(e) 277*9c5db199SXin Li # `adb kill-server` sometimes hangs up. Kill it more brutally. 278*9c5db199SXin Li try: 279*9c5db199SXin Li client_utils.system( 280*9c5db199SXin Li 'killall adb', 281*9c5db199SXin Li ignore_status=True, 282*9c5db199SXin Li timeout=constants.ADB_KILL_SERVER_TIMEOUT_SECONDS) 283*9c5db199SXin Li except error.CmdTimeoutError as e: 284*9c5db199SXin Li # The timeout is ignored, since the only known failure pattern 285*9c5db199SXin Li # b/142828365 is due to a zombie process that does not prevent 286*9c5db199SXin Li # starting a new server with a new adb key. 287*9c5db199SXin Li logging.warn(e) 288*9c5db199SXin Li except (error.CmdError, AttributeError): 289*9c5db199SXin Li pass 290*9c5db199SXin Li 291*9c5db199SXin Li def _verify_hosts(self): 292*9c5db199SXin Li """Verify all hosts' ChromeOS consistency.""" 293*9c5db199SXin Li # Check release builder path. E.g. cave-release/R66-10435.0.0 294*9c5db199SXin Li release_builder_path = set(host.get_release_builder_path() 295*9c5db199SXin Li for host in self._hosts) 296*9c5db199SXin Li if len(release_builder_path) > 1: 297*9c5db199SXin Li raise error.TestFail('Hosts\' CHROMEOS_RELEASE_BUILDER_PATH is ' 298*9c5db199SXin Li 'different: %s', release_builder_path) 299*9c5db199SXin Li 300*9c5db199SXin Li # Check ChromeOS ARC VERSION. E.g. 301*9c5db199SXin Li arc_version = set(host.get_arc_version() for host in self._hosts) 302*9c5db199SXin Li if len(arc_version) > 1: 303*9c5db199SXin Li raise error.TestFail('Hosts\' CHROMEOS_ARC_VERSION is different: ' 304*9c5db199SXin Li '%s', arc_version) 305*9c5db199SXin Li 306*9c5db199SXin Li # Check ChromeOS model for unibuild. 307*9c5db199SXin Li # TODO(pwang): Adding a check if we found how to detect host's model. 308*9c5db199SXin Li 309*9c5db199SXin Li def _verify_arc_hosts(self): 310*9c5db199SXin Li """Verify all hosts' Android configuration consistency. 311*9c5db199SXin Li 312*9c5db199SXin Li This method should only be called after all hosts' Android has been 313*9c5db199SXin Li successfully booted up.""" 314*9c5db199SXin Li # Check all hosts have same Android fingerprint. 315*9c5db199SXin Li fingerprint = set( 316*9c5db199SXin Li self._adb.run(host, 317*9c5db199SXin Li args=('shell', 'getprop', 318*9c5db199SXin Li 'ro.build.fingerprint')).stdout 319*9c5db199SXin Li for host in self._hosts) 320*9c5db199SXin Li if len(fingerprint) > 1: 321*9c5db199SXin Li raise error.TestFail('Hosts\' supported fingerprint is different: ' 322*9c5db199SXin Li '%s', fingerprint) 323*9c5db199SXin Li 324*9c5db199SXin Li def _calculate_test_count_factor(self, bundle): 325*9c5db199SXin Li """ Calculate the multiplicative factor for the test case number. 326*9c5db199SXin Li 327*9c5db199SXin Li The value equals to the times each test case is run, which is determined 328*9c5db199SXin Li by the intersection of the supported ABIs of the CTS/GTS bundle and that 329*9c5db199SXin Li of the tested device.""" 330*9c5db199SXin Li # This is only a conservative approximation. Some suites only run the 331*9c5db199SXin Li # primary ABI, so to be fully precise, those have to be counted as 1. 332*9c5db199SXin Li arm_abis = set(('armeabi-v7a', 'arm64-v8a')) 333*9c5db199SXin Li x86_abis = set(('x86', 'x86_64')) 334*9c5db199SXin Li if bundle and bundle.startswith('arm'): 335*9c5db199SXin Li tradefed_abis = arm_abis 336*9c5db199SXin Li elif bundle and bundle.startswith('x86'): 337*9c5db199SXin Li tradefed_abis = x86_abis 338*9c5db199SXin Li else: 339*9c5db199SXin Li tradefed_abis = arm_abis | x86_abis 340*9c5db199SXin Li self._test_count_factor = len(set(self._get_abilist()) & tradefed_abis) 341*9c5db199SXin Li # Avoid setting timeout=0 (None) in any cases. 342*9c5db199SXin Li self._timeout_factor = max(1, self._test_count_factor) 343*9c5db199SXin Li 344*9c5db199SXin Li def _try_adb_connect(self, host): 345*9c5db199SXin Li """Attempts to connect to adb on the DUT. 346*9c5db199SXin Li 347*9c5db199SXin Li @param host: DUT that need to be connected. 348*9c5db199SXin Li @return boolean indicating if adb connected successfully. 349*9c5db199SXin Li """ 350*9c5db199SXin Li # Add ADB_TRACE=all for debugging adb connection failures. 351*9c5db199SXin Li env = os.environ.copy() 352*9c5db199SXin Li env['ADB_TRACE'] = 'all' 353*9c5db199SXin Li try: 354*9c5db199SXin Li # This may fail return failure due to a race condition in adb 355*9c5db199SXin Li # connect (b/29370989). If adb is already connected, this command 356*9c5db199SXin Li # will immediately return success. 357*9c5db199SXin Li host_port = adb_utils.get_adb_target(host) 358*9c5db199SXin Li result = self._adb.run( 359*9c5db199SXin Li host, 360*9c5db199SXin Li args=('connect', host_port), 361*9c5db199SXin Li verbose=True, 362*9c5db199SXin Li env=env, 363*9c5db199SXin Li ignore_status=True, 364*9c5db199SXin Li timeout=constants.ADB_CONNECT_TIMEOUT_SECONDS) 365*9c5db199SXin Li if result.exit_status != 0: 366*9c5db199SXin Li return False 367*9c5db199SXin Li 368*9c5db199SXin Li result = self._adb.run( 369*9c5db199SXin Li host, 370*9c5db199SXin Li args=('devices', ), 371*9c5db199SXin Li env=env, 372*9c5db199SXin Li timeout=constants.ADB_CONNECT_TIMEOUT_SECONDS) 373*9c5db199SXin Li if not re.search(r'{}\s+(device|unauthorized)'.format( 374*9c5db199SXin Li re.escape(host_port)), result.stdout): 375*9c5db199SXin Li logging.info('No result found in with pattern: %s', 376*9c5db199SXin Li r'{}\s+(device|unauthorized)'.format( 377*9c5db199SXin Li re.escape(host_port))) 378*9c5db199SXin Li return False 379*9c5db199SXin Li 380*9c5db199SXin Li # Actually test the connection with an adb command as there can be 381*9c5db199SXin Li # a race between detecting the connected device and actually being 382*9c5db199SXin Li # able to run a command with authenticated adb. 383*9c5db199SXin Li result = self._adb.run( 384*9c5db199SXin Li host, 385*9c5db199SXin Li args=('shell', 'exit'), 386*9c5db199SXin Li env=env, 387*9c5db199SXin Li ignore_status=True, 388*9c5db199SXin Li timeout=constants.ADB_CONNECT_TIMEOUT_SECONDS) 389*9c5db199SXin Li return result.exit_status == 0 390*9c5db199SXin Li except error.CmdTimeoutError as e: 391*9c5db199SXin Li logging.warning(e) 392*9c5db199SXin Li return False 393*9c5db199SXin Li 394*9c5db199SXin Li def _android_shell(self, host, command): 395*9c5db199SXin Li """Run a command remotely on the device in an android shell 396*9c5db199SXin Li 397*9c5db199SXin Li This function is strictly for internal use only, as commands do not run 398*9c5db199SXin Li in a fully consistent Android environment. Prefer adb shell instead. 399*9c5db199SXin Li """ 400*9c5db199SXin Li host.run('android-sh -c ' + pipes.quote(command)) 401*9c5db199SXin Li 402*9c5db199SXin Li def _connect_adb(self, host): 403*9c5db199SXin Li """Sets up ADB connection to the ARC container. 404*9c5db199SXin Li 405*9c5db199SXin Li @param host: DUT that should be connected to. 406*9c5db199SXin Li """ 407*9c5db199SXin Li logging.info('Setting up adb connection.') 408*9c5db199SXin Li 409*9c5db199SXin Li # adbd may take some time to come up. Repeatedly try to connect to adb. 410*9c5db199SXin Li utils.poll_for_condition( 411*9c5db199SXin Li lambda: self._try_adb_connect(host), 412*9c5db199SXin Li timeout=constants.ADB_READY_TIMEOUT_SECONDS, 413*9c5db199SXin Li sleep_interval=constants.ADB_POLLING_INTERVAL_SECONDS) 414*9c5db199SXin Li 415*9c5db199SXin Li logging.info('Successfully setup adb connection.') 416*9c5db199SXin Li 417*9c5db199SXin Li def _wait_for_arc_boot(self, host): 418*9c5db199SXin Li """Wait until ARC is fully booted. 419*9c5db199SXin Li 420*9c5db199SXin Li Tests for the presence of the intent helper app to determine whether ARC 421*9c5db199SXin Li has finished booting. 422*9c5db199SXin Li @param host: DUT that need to be connected to. 423*9c5db199SXin Li """ 424*9c5db199SXin Li 425*9c5db199SXin Li def _intent_helper_running(): 426*9c5db199SXin Li result = self._adb.run(host, 427*9c5db199SXin Li args=('shell', 'pgrep', '-f', 428*9c5db199SXin Li 'org.chromium.arc.intent_helper'), 429*9c5db199SXin Li ignore_status=True) 430*9c5db199SXin Li return bool(result.stdout) 431*9c5db199SXin Li 432*9c5db199SXin Li utils.poll_for_condition( 433*9c5db199SXin Li _intent_helper_running, 434*9c5db199SXin Li exception=error.TestFail( 435*9c5db199SXin Li 'Error: Timed out waiting for intent helper.'), 436*9c5db199SXin Li timeout=constants.ARC_READY_TIMEOUT_SECONDS, 437*9c5db199SXin Li sleep_interval=constants.ARC_POLLING_INTERVAL_SECONDS) 438*9c5db199SXin Li 439*9c5db199SXin Li def _disable_adb_install_dialog(self, host): 440*9c5db199SXin Li """Disables a dialog shown on adb install execution. 441*9c5db199SXin Li 442*9c5db199SXin Li By default, on adb install execution, "Allow Google to regularly check 443*9c5db199SXin Li device activity ... " dialog is shown. It requires manual user action 444*9c5db199SXin Li so that tests are blocked at the point. 445*9c5db199SXin Li This method disables it. 446*9c5db199SXin Li """ 447*9c5db199SXin Li logging.info('Disabling the adb install dialog.') 448*9c5db199SXin Li result = self._adb.run(host, 449*9c5db199SXin Li verbose=True, 450*9c5db199SXin Li args=('shell', 'settings', 'put', 'global', 451*9c5db199SXin Li 'verifier_verify_adb_installs', '0')) 452*9c5db199SXin Li logging.info('Disable adb dialog: %s', result.stdout) 453*9c5db199SXin Li 454*9c5db199SXin Li # Android "RescueParty" feature can reset the above settings when the 455*9c5db199SXin Li # device crashes often. Disable the rescue during testing. 456*9c5db199SXin Li # Keeping only for P and below since R has SELinux restrictions. 457*9c5db199SXin Li if self._get_android_version() < 29: 458*9c5db199SXin Li self._android_shell(host, 'setprop persist.sys.disable_rescue true') 459*9c5db199SXin Li 460*9c5db199SXin Li def _ready_arc(self): 461*9c5db199SXin Li """Ready ARC and adb in parallel for running tests via tradefed.""" 462*9c5db199SXin Li key_path = os.path.join(self.tmpdir, 'test_key') 463*9c5db199SXin Li with open(key_path, 'w') as f: 464*9c5db199SXin Li f.write(constants.PRIVATE_KEY) 465*9c5db199SXin Li os.environ['ADB_VENDOR_KEYS'] = key_path 466*9c5db199SXin Li 467*9c5db199SXin Li for _ in range(2): 468*9c5db199SXin Li try: 469*9c5db199SXin Li # Kill existing adb server to ensure that the env var is picked 470*9c5db199SXin Li # up, and reset any previous bad state. 471*9c5db199SXin Li self._kill_adb_server() 472*9c5db199SXin Li 473*9c5db199SXin Li # TODO(pwang): connect_adb takes 10+ seconds on a single DUT. 474*9c5db199SXin Li # Parallelize it if it becomes a bottleneck. 475*9c5db199SXin Li for host in self._hosts: 476*9c5db199SXin Li self._connect_adb(host) 477*9c5db199SXin Li self._disable_adb_install_dialog(host) 478*9c5db199SXin Li self._wait_for_arc_boot(host) 479*9c5db199SXin Li self._verify_arc_hosts() 480*9c5db199SXin Li return 481*9c5db199SXin Li except (utils.TimeoutError, error.CmdTimeoutError): 482*9c5db199SXin Li logging.error('Failed to set up adb connection. Retrying...') 483*9c5db199SXin Li raise error.TestFail('Error: Failed to set up adb connection') 484*9c5db199SXin Li 485*9c5db199SXin Li def _safe_makedirs(self, path): 486*9c5db199SXin Li """Creates a directory at |path| and its ancestors. 487*9c5db199SXin Li 488*9c5db199SXin Li Unlike os.makedirs(), ignore errors even if directories exist. 489*9c5db199SXin Li """ 490*9c5db199SXin Li try: 491*9c5db199SXin Li os.makedirs(path) 492*9c5db199SXin Li except OSError as e: 493*9c5db199SXin Li if not (e.errno == errno.EEXIST and os.path.isdir(path)): 494*9c5db199SXin Li raise 495*9c5db199SXin Li 496*9c5db199SXin Li def _unzip(self, filename): 497*9c5db199SXin Li """Unzip the file. 498*9c5db199SXin Li 499*9c5db199SXin Li The destination directory name will be the stem of filename. 500*9c5db199SXin Li E.g., _unzip('foo/bar/baz.zip') will create directory at 501*9c5db199SXin Li 'foo/bar/baz', and then will inflate zip's content under the directory. 502*9c5db199SXin Li If here is already a directory at the stem, that directory will be used. 503*9c5db199SXin Li 504*9c5db199SXin Li @param filename: Path to the zip archive. 505*9c5db199SXin Li @return Path to the inflated directory. 506*9c5db199SXin Li """ 507*9c5db199SXin Li destination = os.path.splitext(filename)[0] 508*9c5db199SXin Li if os.path.isdir(destination): 509*9c5db199SXin Li logging.info('Skipping unzip %s, reusing content of %s', filename, 510*9c5db199SXin Li destination) 511*9c5db199SXin Li return destination 512*9c5db199SXin Li tmp = tempfile.mkdtemp(dir=os.path.dirname(filename)) 513*9c5db199SXin Li logging.info('Begin unzip %s', filename) 514*9c5db199SXin Li try: 515*9c5db199SXin Li utils.run('unzip', args=('-d', tmp, filename)) 516*9c5db199SXin Li except: 517*9c5db199SXin Li logging.error('Failed unzip, cleaning up.') 518*9c5db199SXin Li # Clean up just created files. 519*9c5db199SXin Li shutil.rmtree(tmp, ignore_errors=True) 520*9c5db199SXin Li raise 521*9c5db199SXin Li logging.info('End unzip %s', filename) 522*9c5db199SXin Li try: 523*9c5db199SXin Li os.renames(tmp, destination) 524*9c5db199SXin Li except: 525*9c5db199SXin Li logging.error('Failed rename, cleaning up.') 526*9c5db199SXin Li shutil.rmtree(destination, ignore_errors=True) 527*9c5db199SXin Li shutil.rmtree(tmp, ignore_errors=True) 528*9c5db199SXin Li raise 529*9c5db199SXin Li return destination 530*9c5db199SXin Li 531*9c5db199SXin Li def _dir_size(self, directory): 532*9c5db199SXin Li """Compute recursive size in bytes of directory.""" 533*9c5db199SXin Li size = 0 534*9c5db199SXin Li for root, _, files in os.walk(directory): 535*9c5db199SXin Li for name in files: 536*9c5db199SXin Li try: 537*9c5db199SXin Li size += os.path.getsize(os.path.join(root, name)) 538*9c5db199SXin Li except OSError: 539*9c5db199SXin Li logging.error('Inaccessible path (crbug/793696): %s/%s', 540*9c5db199SXin Li root, name) 541*9c5db199SXin Li return size 542*9c5db199SXin Li 543*9c5db199SXin Li def _invalidate_download_cache(self): 544*9c5db199SXin Li """Marks the download cache for deferred deletion. 545*9c5db199SXin Li 546*9c5db199SXin Li Used to make cache file operations atomic across failures and reboots. 547*9c5db199SXin Li The caller is responsible to hold the lock to the cache. 548*9c5db199SXin Li """ 549*9c5db199SXin Li if not os.path.exists(self._tradefed_cache_dirty): 550*9c5db199SXin Li os.mkdir(self._tradefed_cache_dirty) 551*9c5db199SXin Li 552*9c5db199SXin Li def _validate_download_cache(self): 553*9c5db199SXin Li """Validates and unmarks the download cache from deletion. 554*9c5db199SXin Li 555*9c5db199SXin Li Used to make cache file operations atomic across failures and reboots. 556*9c5db199SXin Li The caller is responsible to hold the lock to the cache. 557*9c5db199SXin Li """ 558*9c5db199SXin Li shutil.rmtree(self._tradefed_cache_dirty, ignore_errors=True) 559*9c5db199SXin Li 560*9c5db199SXin Li def _clean_download_cache_if_needed(self, force=False): 561*9c5db199SXin Li """Invalidates cache to prevent it from growing too large.""" 562*9c5db199SXin Li # If the cache is large enough to hold a working set, we can simply 563*9c5db199SXin Li # delete everything without thrashing. 564*9c5db199SXin Li # TODO(ihf): Investigate strategies like LRU. 565*9c5db199SXin Li clean = force 566*9c5db199SXin Li with tradefed_utils.lock(self._tradefed_cache_lock): 567*9c5db199SXin Li size = self._dir_size(self._tradefed_cache) 568*9c5db199SXin Li if size > constants.TRADEFED_CACHE_MAX_SIZE: 569*9c5db199SXin Li logging.info( 570*9c5db199SXin Li 'Current cache size=%d got too large. Clearing %s.', size, 571*9c5db199SXin Li self._tradefed_cache) 572*9c5db199SXin Li clean = True 573*9c5db199SXin Li else: 574*9c5db199SXin Li logging.info('Current cache size=%d of %s.', size, 575*9c5db199SXin Li self._tradefed_cache) 576*9c5db199SXin Li if os.path.exists(self._tradefed_cache_dirty): 577*9c5db199SXin Li logging.info('Found dirty cache.') 578*9c5db199SXin Li clean = True 579*9c5db199SXin Li if clean: 580*9c5db199SXin Li logging.warning('Cleaning download cache.') 581*9c5db199SXin Li shutil.rmtree(self._tradefed_cache, ignore_errors=True) 582*9c5db199SXin Li self._safe_makedirs(self._tradefed_cache) 583*9c5db199SXin Li shutil.rmtree(self._tradefed_cache_dirty, ignore_errors=True) 584*9c5db199SXin Li 585*9c5db199SXin Li def _download_to_cache(self, uri): 586*9c5db199SXin Li """Downloads the uri from the storage server. 587*9c5db199SXin Li 588*9c5db199SXin Li It always checks the cache for available binaries first and skips 589*9c5db199SXin Li download if binaries are already in cache. 590*9c5db199SXin Li 591*9c5db199SXin Li The caller of this function is responsible for holding the cache lock. 592*9c5db199SXin Li 593*9c5db199SXin Li @param uri: The Google Storage, dl.google.com or local uri. 594*9c5db199SXin Li @return Path to the downloaded object, name. 595*9c5db199SXin Li """ 596*9c5db199SXin Li # We are hashing the uri instead of the binary. This is acceptable, as 597*9c5db199SXin Li # the uris are supposed to contain version information and an object is 598*9c5db199SXin Li # not supposed to be changed once created. 599*9c5db199SXin Li output_dir = os.path.join(self._tradefed_cache, 600*9c5db199SXin Li hashlib.md5(uri.encode()).hexdigest()) 601*9c5db199SXin Li # Check for existence of cache entry. We check for directory existence 602*9c5db199SXin Li # instead of file existence, so that _install_bundle can delete original 603*9c5db199SXin Li # zip files to save disk space. 604*9c5db199SXin Li if os.path.exists(output_dir): 605*9c5db199SXin Li # TODO(crbug.com/800657): Mitigation for the invalid state. Normally 606*9c5db199SXin Li # this should not happen, but when a lock is force borken due to 607*9c5db199SXin Li # high IO load, multiple processes may enter the critical section 608*9c5db199SXin Li # and leave a bad state permanently. 609*9c5db199SXin Li if os.listdir(output_dir): 610*9c5db199SXin Li logging.info('Skipping download of %s, reusing content of %s.', 611*9c5db199SXin Li uri, output_dir) 612*9c5db199SXin Li return os.path.join(output_dir, 613*9c5db199SXin Li os.path.basename(urlparse.urlparse(uri).path)) 614*9c5db199SXin Li logging.error('Empty cache entry detected %s', output_dir) 615*9c5db199SXin Li return self._download_to_dir(uri, output_dir) 616*9c5db199SXin Li 617*9c5db199SXin Li def _download_to_dir(self, uri, output_dir): 618*9c5db199SXin Li """Downloads the gs|http|https|file uri from the storage server. 619*9c5db199SXin Li 620*9c5db199SXin Li @param uri: The Google Storage, dl.google.com or local uri. 621*9c5db199SXin Li @output_dir: The directory where the downloaded file should be placed. 622*9c5db199SXin Li @return Path to the downloaded object, name. 623*9c5db199SXin Li """ 624*9c5db199SXin Li # Split uri into 3 pieces for use by gsutil and also by wget. 625*9c5db199SXin Li parsed = urlparse.urlparse(uri) 626*9c5db199SXin Li filename = os.path.basename(parsed.path) 627*9c5db199SXin Li output = os.path.join(output_dir, filename) 628*9c5db199SXin Li 629*9c5db199SXin Li self._safe_makedirs(output_dir) 630*9c5db199SXin Li if parsed.scheme not in ['gs', 'http', 'https', 'file']: 631*9c5db199SXin Li raise error.TestFail( 632*9c5db199SXin Li 'Error: Unknown download scheme %s' % parsed.scheme) 633*9c5db199SXin Li if parsed.scheme in ['http', 'https']: 634*9c5db199SXin Li logging.info('Using wget to download %s to %s.', uri, output_dir) 635*9c5db199SXin Li # We are downloading 1 file at a time, hence using -O over -P. 636*9c5db199SXin Li utils.run( 637*9c5db199SXin Li 'wget', 638*9c5db199SXin Li args=('--report-speed=bits', '-O', output, uri), 639*9c5db199SXin Li verbose=True) 640*9c5db199SXin Li return output 641*9c5db199SXin Li 642*9c5db199SXin Li if parsed.scheme in ['file']: 643*9c5db199SXin Li logging.info('Copy the local file from %s to %s.', parsed.path, 644*9c5db199SXin Li output_dir) 645*9c5db199SXin Li utils.run( 646*9c5db199SXin Li 'cp', 647*9c5db199SXin Li args=('-f', parsed.path, output), 648*9c5db199SXin Li verbose=True) 649*9c5db199SXin Li return output 650*9c5db199SXin Li 651*9c5db199SXin Li # If the machine can access to the storage server directly, 652*9c5db199SXin Li # defer to "gsutil" for downloading. 653*9c5db199SXin Li logging.info('Downloading %s directly to %s.', uri, output) 654*9c5db199SXin Li # b/17445576: gsutil rsync of individual files is not implemented. 655*9c5db199SXin Li res = utils.run('gsutil', 656*9c5db199SXin Li args=('cp', uri, output), 657*9c5db199SXin Li verbose=True, 658*9c5db199SXin Li ignore_status=True) 659*9c5db199SXin Li if not res or res.exit_status != 0: 660*9c5db199SXin Li logging.warning('Retrying download...') 661*9c5db199SXin Li utils.run('gsutil', args=('cp', uri, output), verbose=True) 662*9c5db199SXin Li return output 663*9c5db199SXin Li 664*9c5db199SXin Li def _instance_copyfile(self, cache_path): 665*9c5db199SXin Li """Makes a copy of a file from the (shared) cache to a wholy owned 666*9c5db199SXin Li local instance. Also copies one level of cache directoy (MD5 named). 667*9c5db199SXin Li """ 668*9c5db199SXin Li filename = os.path.basename(cache_path) 669*9c5db199SXin Li dirname = os.path.basename(os.path.dirname(cache_path)) 670*9c5db199SXin Li instance_dir = os.path.join(self._tradefed_install, dirname) 671*9c5db199SXin Li # Make sure destination directory is named the same. 672*9c5db199SXin Li self._safe_makedirs(instance_dir) 673*9c5db199SXin Li instance_path = os.path.join(instance_dir, filename) 674*9c5db199SXin Li shutil.copyfile(cache_path, instance_path) 675*9c5db199SXin Li return instance_path 676*9c5db199SXin Li 677*9c5db199SXin Li def _instance_copytree(self, cache_path): 678*9c5db199SXin Li """Makes a copy of a directory from the (shared and writable) cache to 679*9c5db199SXin Li a wholy owned local instance. 680*9c5db199SXin Li 681*9c5db199SXin Li TODO(ihf): Consider using cp -al to only copy links. Not sure if this 682*9c5db199SXin Li is really a benefit across the container boundary, but it is risky due 683*9c5db199SXin Li to the possibility of corrupting the original files by an lxc instance. 684*9c5db199SXin Li """ 685*9c5db199SXin Li # We keep the top 2 names from the cache_path = .../dir1/dir2. 686*9c5db199SXin Li dir2 = os.path.basename(cache_path) 687*9c5db199SXin Li dir1 = os.path.basename(os.path.dirname(cache_path)) 688*9c5db199SXin Li instance_path = os.path.join(self._tradefed_install, dir1, dir2) 689*9c5db199SXin Li # TODO(kinaba): Fix in a safer way. 690*9c5db199SXin Li # Below is a workaround to avoid copying large CTS/GTS tree in test lab. 691*9c5db199SXin Li # Contents of $cache_path/android-cts are symlinked to the destination 692*9c5db199SXin Li # rather than copied. 693*9c5db199SXin Li # 1) Why not symlink 'android-cts' itself? Because the tests will 694*9c5db199SXin Li # create results/ logs/ subplans/ subdirectory there. We do not 695*9c5db199SXin Li # want to write to the shared cache. 696*9c5db199SXin Li # 2) Why not hardlink? Cache and the local directory may be on 697*9c5db199SXin Li # different mount points, so hardlink may not work. 698*9c5db199SXin Li # 3) Why this isn't safe? Cache is cleared when it became full, even 699*9c5db199SXin Li # during the test is run on an instance. 700*9c5db199SXin Li # 4) Why this is acceptable despite the unsatefy? Cache clearance is 701*9c5db199SXin Li # a rare event (once in 6 months). Skylab drones won't usually 702*9c5db199SXin Li # live that long, and even if it did, the failure is once in 6 703*9c5db199SXin Li # months after all. 704*9c5db199SXin Li special_src = None 705*9c5db199SXin Li special_dest = None 706*9c5db199SXin Li if utils.is_in_container() and not client_utils.is_moblab(): 707*9c5db199SXin Li for xts_name in ['android-cts', 'android-gts', 'android-sts']: 708*9c5db199SXin Li xts_root = os.path.join(cache_path, xts_name) 709*9c5db199SXin Li if os.path.exists(xts_root): 710*9c5db199SXin Li special_src = xts_root 711*9c5db199SXin Li special_dest = os.path.join(instance_path, xts_name) 712*9c5db199SXin Li break 713*9c5db199SXin Li if special_src: 714*9c5db199SXin Li logging.info('SYMLINK© contents of %s to instance %s', 715*9c5db199SXin Li cache_path, instance_path) 716*9c5db199SXin Li self._safe_makedirs(special_dest) 717*9c5db199SXin Li for entry in os.listdir(special_src): 718*9c5db199SXin Li # Subdirectories are created by relative path from 719*9c5db199SXin Li # tools/cts_tradefed. So for 'tools' dir we copy. 720*9c5db199SXin Li if entry == 'tools': 721*9c5db199SXin Li shutil.copytree(os.path.join(special_src, entry), 722*9c5db199SXin Li os.path.join(special_dest, entry)) 723*9c5db199SXin Li elif entry == 'testcases': 724*9c5db199SXin Li # Directory structure in testcases/ needs to be 725*9c5db199SXin Li # instantiated, because CTS tries `find` command 726*9c5db199SXin Li # in the directory without following symlinks 727*9c5db199SXin Li for subdir, _, files in os.walk( 728*9c5db199SXin Li os.path.join(special_src, entry)): 729*9c5db199SXin Li rel = os.path.relpath(subdir, special_src) 730*9c5db199SXin Li os.mkdir(os.path.join(special_dest, rel)) 731*9c5db199SXin Li for file in files: 732*9c5db199SXin Li os.symlink(os.path.join(special_src, rel, file), 733*9c5db199SXin Li os.path.join(special_dest, rel, file)) 734*9c5db199SXin Li else: 735*9c5db199SXin Li os.symlink(os.path.join(special_src, entry), 736*9c5db199SXin Li os.path.join(special_dest, entry)) 737*9c5db199SXin Li else: 738*9c5db199SXin Li logging.info('Copying %s to instance %s', cache_path, 739*9c5db199SXin Li instance_path) 740*9c5db199SXin Li shutil.copytree(cache_path, instance_path) 741*9c5db199SXin Li return instance_path 742*9c5db199SXin Li 743*9c5db199SXin Li def _install_bundle(self, gs_uri): 744*9c5db199SXin Li """Downloads a zip file, installs it and returns the local path. 745*9c5db199SXin Li 746*9c5db199SXin Li @param gs_uri: GS bucket that contains the necessary files. 747*9c5db199SXin Li """ 748*9c5db199SXin Li if not gs_uri.endswith('.zip'): 749*9c5db199SXin Li raise error.TestFail('Error: Not a .zip file %s.', gs_uri) 750*9c5db199SXin Li # Atomic write through of file. 751*9c5db199SXin Li with tradefed_utils.lock(self._tradefed_cache_lock): 752*9c5db199SXin Li # Atomic operations. 753*9c5db199SXin Li self._invalidate_download_cache() 754*9c5db199SXin Li # Download is lazy (cache_path may not actually exist if 755*9c5db199SXin Li # cache_unzipped does). 756*9c5db199SXin Li cache_path = self._download_to_cache(gs_uri) 757*9c5db199SXin Li # Unzip is lazy as well (but cache_unzipped guaranteed to 758*9c5db199SXin Li # exist). 759*9c5db199SXin Li cache_unzipped = self._unzip(cache_path) 760*9c5db199SXin Li # To save space we delete the original zip file. This works as 761*9c5db199SXin Li # _download only checks existence of the cache directory for 762*9c5db199SXin Li # lazily skipping download, and unzip itself will bail if the 763*9c5db199SXin Li # unzipped destination exists. Hence we don't need the original 764*9c5db199SXin Li # anymore. 765*9c5db199SXin Li if os.path.exists(cache_path): 766*9c5db199SXin Li logging.info('Deleting original %s', cache_path) 767*9c5db199SXin Li os.remove(cache_path) 768*9c5db199SXin Li # Erase dirty marker from disk. 769*9c5db199SXin Li self._validate_download_cache() 770*9c5db199SXin Li # We always copy files to give tradefed a clean copy of the 771*9c5db199SXin Li # bundle. 772*9c5db199SXin Li unzipped_local = self._instance_copytree(cache_unzipped) 773*9c5db199SXin Li return unzipped_local 774*9c5db199SXin Li 775*9c5db199SXin Li def _install_files(self, gs_dir, files, permission): 776*9c5db199SXin Li """Installs binary tools.""" 777*9c5db199SXin Li for filename in files: 778*9c5db199SXin Li gs_uri = os.path.join(gs_dir, filename) 779*9c5db199SXin Li # Atomic write through of file. 780*9c5db199SXin Li with tradefed_utils.lock(self._tradefed_cache_lock): 781*9c5db199SXin Li # We don't want to leave a corrupt cache for other jobs. 782*9c5db199SXin Li self._invalidate_download_cache() 783*9c5db199SXin Li cache_path = self._download_to_cache(gs_uri) 784*9c5db199SXin Li # Mark cache as clean again. 785*9c5db199SXin Li self._validate_download_cache() 786*9c5db199SXin Li # This only affects the current job, so not part of cache 787*9c5db199SXin Li # validation. 788*9c5db199SXin Li local = self._instance_copyfile(cache_path) 789*9c5db199SXin Li os.chmod(local, permission) 790*9c5db199SXin Li # Keep track of PATH. 791*9c5db199SXin Li local_dir = os.path.dirname(local) 792*9c5db199SXin Li self._install_paths.append(local_dir) 793*9c5db199SXin Li self._adb.add_path(local_dir) 794*9c5db199SXin Li 795*9c5db199SXin Li def _prepare_media(self, media_asset): 796*9c5db199SXin Li """Downloads and offers the cached media files to tradefed.""" 797*9c5db199SXin Li if media_asset.uri: 798*9c5db199SXin Li media = self._install_bundle(media_asset.uri) 799*9c5db199SXin Li if os.path.islink(media_asset.localpath): 800*9c5db199SXin Li os.unlink(media_asset.localpath) 801*9c5db199SXin Li if os.path.isdir(media_asset.localpath): 802*9c5db199SXin Li shutil.rmtree(media_asset.localpath) 803*9c5db199SXin Li self._safe_makedirs(os.path.dirname(media_asset.localpath)) 804*9c5db199SXin Li os.symlink(media, media_asset.localpath) 805*9c5db199SXin Li 806*9c5db199SXin Li logging.info('Offered %s as a media directory in %s', 807*9c5db199SXin Li media, media_asset.localpath) 808*9c5db199SXin Li 809*9c5db199SXin Li # Records the number of existing media bundles, to check later. 810*9c5db199SXin Li if os.path.isdir(media_asset.localpath): 811*9c5db199SXin Li self._num_media_bundles = len( 812*9c5db199SXin Li os.listdir(media_asset.localpath)) 813*9c5db199SXin Li 814*9c5db199SXin Li def _cleanup_media(self, media_asset): 815*9c5db199SXin Li """Clean up the local copy of cached media files.""" 816*9c5db199SXin Li self._fail_on_unexpected_media_download(media_asset) 817*9c5db199SXin Li if os.path.islink(media_asset.localpath): 818*9c5db199SXin Li path = os.readlink(media_asset.localpath) 819*9c5db199SXin Li os.unlink(media_asset.localpath) 820*9c5db199SXin Li if os.path.isdir(path): 821*9c5db199SXin Li logging.info('Cleaning up media files in %s', path) 822*9c5db199SXin Li shutil.rmtree(path) 823*9c5db199SXin Li 824*9c5db199SXin Li def _fail_on_unexpected_media_download(self, media_asset): 825*9c5db199SXin Li if os.path.isdir(media_asset.localpath): 826*9c5db199SXin Li contents = os.listdir(media_asset.localpath) 827*9c5db199SXin Li # Ignore a table-of-contents file created by newer xTS 828*9c5db199SXin Li TOC_FILE = 'contents.toc' 829*9c5db199SXin Li if TOC_FILE in contents: 830*9c5db199SXin Li contents.remove(TOC_FILE) 831*9c5db199SXin Li if len(contents) > self._num_media_bundles: 832*9c5db199SXin Li raise error.TestFail( 833*9c5db199SXin Li 'Failed: Unexpected media bundle was added %s' % contents) 834*9c5db199SXin Li 835*9c5db199SXin Li def _should_push_mediastress_asset(self, target_module, board): 836*9c5db199SXin Li """Returns whether we should manually push mediastress assets. 837*9c5db199SXin Li 838*9c5db199SXin Li TODO(b/210801048): Remove this workaround once ARCVM storage performance 839*9c5db199SXin Li on ARM becomes good enough. 840*9c5db199SXin Li """ 841*9c5db199SXin Li return (target_module and 'CtsMediaStressTestCases' in target_module 842*9c5db199SXin Li and board in ['kukui-arc-r']) 843*9c5db199SXin Li 844*9c5db199SXin Li def _push_mediastress_asset(self, media_asset): 845*9c5db199SXin Li """Pushes mediastress assets to the DUT for the upcoming test.""" 846*9c5db199SXin Li logging.info( 847*9c5db199SXin Li 'Pushing mediastress assets in advance to workaround slow ' 848*9c5db199SXin Li 'storage on ARM boards (b/210801048)') 849*9c5db199SXin Li 850*9c5db199SXin Li media_dir = os.path.join(media_asset.localpath, 851*9c5db199SXin Li 'android-cts-media-1.5') 852*9c5db199SXin Li copy_media_sh = os.path.join(media_dir, 'copy_media.sh') 853*9c5db199SXin Li os.chmod(copy_media_sh, 0o755) 854*9c5db199SXin Li 855*9c5db199SXin Li old_cwd = os.getcwd() 856*9c5db199SXin Li os.chdir(media_dir) 857*9c5db199SXin Li try: 858*9c5db199SXin Li for host in self._hosts: 859*9c5db199SXin Li host_port = adb_utils.get_adb_target(host) 860*9c5db199SXin Li self._run( 861*9c5db199SXin Li copy_media_sh, 862*9c5db199SXin Li args=('all', '-s', host_port), 863*9c5db199SXin Li timeout=constants.ADB_PUSH_MEDIASTRESS_TIMEOUT_SECONDS, 864*9c5db199SXin Li verbose=True, 865*9c5db199SXin Li ignore_status=False, 866*9c5db199SXin Li stdout_tee=utils.TEE_TO_LOGS, 867*9c5db199SXin Li stderr_tee=utils.TEE_TO_LOGS) 868*9c5db199SXin Li finally: 869*9c5db199SXin Li os.chdir(old_cwd) 870*9c5db199SXin Li 871*9c5db199SXin Li def _fetch_helpers_from_dut(self): 872*9c5db199SXin Li """Fetches the CTS helpers from the dut and installs into the testcases 873*9c5db199SXin Li subdirectory of our local autotest copy. 874*9c5db199SXin Li """ 875*9c5db199SXin Li tf_testcases = os.path.join(self._repository, 'testcases') 876*9c5db199SXin Li 877*9c5db199SXin Li # Earlier checks enforce that each host has the same build fingerprint, 878*9c5db199SXin Li # so we can assume that the packages from the first host will work 879*9c5db199SXin Li # across the whole set. 880*9c5db199SXin Li package_list = self._adb.run( 881*9c5db199SXin Li self._hosts[0], 882*9c5db199SXin Li args=('shell', 'getprop', 883*9c5db199SXin Li constants.TRADEFED_CTS_HELPERS_PROPERTY)).stdout.strip() 884*9c5db199SXin Li for pkg in package_list.split(':'): 885*9c5db199SXin Li if not pkg: 886*9c5db199SXin Li continue 887*9c5db199SXin Li apk_name = pkg + '.apk' 888*9c5db199SXin Li logging.info('Installing CTS helper package %s to %s', apk_name, 889*9c5db199SXin Li tf_testcases) 890*9c5db199SXin Li self._hosts[0].get_file( 891*9c5db199SXin Li os.path.join(constants.BOARD_CTS_HELPERS_DIR, apk_name), 892*9c5db199SXin Li tf_testcases) 893*9c5db199SXin Li 894*9c5db199SXin Li def _run(self, *args, **kwargs): 895*9c5db199SXin Li """Executes the given command line. 896*9c5db199SXin Li 897*9c5db199SXin Li To support SDK tools, such as adb or aapt, this adds _install_paths 898*9c5db199SXin Li to the extra_paths. Before invoking this, ensure _install_files() has 899*9c5db199SXin Li been called. 900*9c5db199SXin Li """ 901*9c5db199SXin Li kwargs['extra_paths'] = ( 902*9c5db199SXin Li kwargs.get('extra_paths', []) + self._install_paths) 903*9c5db199SXin Li return utils.run(*args, **kwargs) 904*9c5db199SXin Li 905*9c5db199SXin Li def _collect_tradefed_global_log(self, result, destination): 906*9c5db199SXin Li """Collects the tradefed global log. 907*9c5db199SXin Li 908*9c5db199SXin Li @param result: The result object from utils.run. 909*9c5db199SXin Li @param destination: Autotest result directory (destination of logs). 910*9c5db199SXin Li """ 911*9c5db199SXin Li match = re.search(r'Saved log to /tmp/(tradefed_global_log_.*\.txt)', 912*9c5db199SXin Li result.stdout) 913*9c5db199SXin Li if not match: 914*9c5db199SXin Li logging.debug(result.stdout) 915*9c5db199SXin Li logging.error('no tradefed_global_log file is found') 916*9c5db199SXin Li return 917*9c5db199SXin Li 918*9c5db199SXin Li name = match.group(1) 919*9c5db199SXin Li dest = os.path.join(destination, 'logs', 'tmp') 920*9c5db199SXin Li self._safe_makedirs(dest) 921*9c5db199SXin Li shutil.copy(os.path.join('/tmp', name), os.path.join(dest, name)) 922*9c5db199SXin Li 923*9c5db199SXin Li def _get_expected_failures(self, directory, bundle_abi): 924*9c5db199SXin Li """Return a list of expected failures or no test module. 925*9c5db199SXin Li 926*9c5db199SXin Li @param directory: A directory with expected no tests or failures files. 927*9c5db199SXin Li @param bundle_abi: 'arm' or 'x86' if the test is for the particular ABI. 928*9c5db199SXin Li None otherwise (like GTS, built for multi-ABI.) 929*9c5db199SXin Li @return: A list of expected failures or no test modules for the current 930*9c5db199SXin Li testing device. 931*9c5db199SXin Li """ 932*9c5db199SXin Li # Load waivers and manual tests so TF doesn't re-run them. 933*9c5db199SXin Li expected_fail_files = [] 934*9c5db199SXin Li test_board = self._get_board_name() 935*9c5db199SXin Li test_model = self._get_model_name() 936*9c5db199SXin Li test_arch = self._get_board_arch() 937*9c5db199SXin Li sdk_ver = self._get_android_version() 938*9c5db199SXin Li first_api_level = self._get_first_api_level() 939*9c5db199SXin Li expected_fail_dir = os.path.join(self.bindir, directory) 940*9c5db199SXin Li if os.path.exists(expected_fail_dir): 941*9c5db199SXin Li expected_fail_files += glob.glob(expected_fail_dir + '/*.yaml') 942*9c5db199SXin Li 943*9c5db199SXin Li waivers = cts_expected_failure_parser.ParseKnownCTSFailures( 944*9c5db199SXin Li expected_fail_files) 945*9c5db199SXin Li return waivers.find_waivers(test_arch, test_board, test_model, 946*9c5db199SXin Li bundle_abi, sdk_ver, first_api_level, 947*9c5db199SXin Li self._hosts[0]) 948*9c5db199SXin Li 949*9c5db199SXin Li def _get_abilist(self): 950*9c5db199SXin Li """Return the abilist supported by calling adb command. 951*9c5db199SXin Li 952*9c5db199SXin Li This method should only be called after the android environment is 953*9c5db199SXin Li successfully initialized.""" 954*9c5db199SXin Li if not self._abilist: 955*9c5db199SXin Li for _ in range(3): 956*9c5db199SXin Li abilist_str = self._adb.run( 957*9c5db199SXin Li self._hosts[0], 958*9c5db199SXin Li args=('shell', 'getprop', 959*9c5db199SXin Li 'ro.product.cpu.abilist')).stdout.strip() 960*9c5db199SXin Li if abilist_str: 961*9c5db199SXin Li self._abilist = abilist_str.split(',') 962*9c5db199SXin Li break 963*9c5db199SXin Li else: 964*9c5db199SXin Li # TODO(kinaba): Sometimes getprop returns an empty string. 965*9c5db199SXin Li # Investigate why. For now we mitigate the bug by retries. 966*9c5db199SXin Li logging.error('Empty abilist.') 967*9c5db199SXin Li return self._abilist 968*9c5db199SXin Li 969*9c5db199SXin Li def _get_release_branch_number(self): 970*9c5db199SXin Li """Returns the DUT branch number (z of Rxx-yyyyy.z.w) or 0 on error.""" 971*9c5db199SXin Li if not self._release_branch_number: 972*9c5db199SXin Li ver = (self._hosts[0].get_release_version() or '').split('.') 973*9c5db199SXin Li self._release_branch_number = (int(ver[1]) if len(ver) >= 3 else 0) 974*9c5db199SXin Li return self._release_branch_number 975*9c5db199SXin Li 976*9c5db199SXin Li def _get_board_arch(self): 977*9c5db199SXin Li """Return target DUT arch name.""" 978*9c5db199SXin Li if not self._board_arch: 979*9c5db199SXin Li self._board_arch = ('arm' if self._hosts[0].get_cpu_arch() == 'arm' 980*9c5db199SXin Li else 'x86') 981*9c5db199SXin Li return self._board_arch 982*9c5db199SXin Li 983*9c5db199SXin Li def _get_board_name(self): 984*9c5db199SXin Li """Return target DUT board name.""" 985*9c5db199SXin Li if not self._board_name: 986*9c5db199SXin Li self._board_name = self._hosts[0].get_board().split(':')[1] 987*9c5db199SXin Li return self._board_name 988*9c5db199SXin Li 989*9c5db199SXin Li def _get_model_name(self): 990*9c5db199SXin Li """Return target DUT model name.""" 991*9c5db199SXin Li if not self._model_name: 992*9c5db199SXin Li self._model_name = self._hosts[0].get_model_from_cros_config() 993*9c5db199SXin Li return self._model_name 994*9c5db199SXin Li 995*9c5db199SXin Li def _get_android_version(self): 996*9c5db199SXin Li """Return target DUT Android SDK version""" 997*9c5db199SXin Li # TODO(kinaba): factor this out to server/hosts/cros_host.py 998*9c5db199SXin Li if not self._android_version: 999*9c5db199SXin Li self._android_version = self._hosts[0].run( 1000*9c5db199SXin Li 'grep ANDROID_SDK /etc/lsb-release', 1001*9c5db199SXin Li ignore_status=True).stdout.rstrip().split('=')[1] 1002*9c5db199SXin Li return int(self._android_version) 1003*9c5db199SXin Li 1004*9c5db199SXin Li def _get_first_api_level(self): 1005*9c5db199SXin Li """Return target DUT Android first API level.""" 1006*9c5db199SXin Li if not self._first_api_level: 1007*9c5db199SXin Li self._first_api_level = self._hosts[0].get_arc_first_api_level() 1008*9c5db199SXin Li return int(self._first_api_level) 1009*9c5db199SXin Li 1010*9c5db199SXin Li def _get_max_retry(self, max_retry): 1011*9c5db199SXin Li """Return the maximum number of retries. 1012*9c5db199SXin Li 1013*9c5db199SXin Li @param max_retry: max_retry specified in the control file. 1014*9c5db199SXin Li @return: number of retries for this specific host. 1015*9c5db199SXin Li """ 1016*9c5db199SXin Li if max_retry is None: 1017*9c5db199SXin Li max_retry = self._get_branch_retry(self._BRANCH_DEFAULT_RETRY) 1018*9c5db199SXin Li candidate = [max_retry] 1019*9c5db199SXin Li candidate.append(self._get_board_retry()) 1020*9c5db199SXin Li candidate.append(self._get_branch_retry(self._BRANCH_MAX_RETRY)) 1021*9c5db199SXin Li return min(x for x in candidate if x is not None) 1022*9c5db199SXin Li 1023*9c5db199SXin Li def _get_board_retry(self): 1024*9c5db199SXin Li """Return the maximum number of retries for DUT board name. 1025*9c5db199SXin Li 1026*9c5db199SXin Li @return: number of max_retry or None. 1027*9c5db199SXin Li """ 1028*9c5db199SXin Li board = self._get_board_name() 1029*9c5db199SXin Li if board in self._BOARD_MAX_RETRY: 1030*9c5db199SXin Li return self._BOARD_MAX_RETRY[board] 1031*9c5db199SXin Li logging.info('No board retry specified for board: %s', board) 1032*9c5db199SXin Li return None 1033*9c5db199SXin Li 1034*9c5db199SXin Li def _get_branch_retry(self, table): 1035*9c5db199SXin Li """Returns the retry count for DUT branch number defined in |table|.""" 1036*9c5db199SXin Li number = self._get_release_branch_number() 1037*9c5db199SXin Li for lowerbound, retry in reversed(table): 1038*9c5db199SXin Li if lowerbound <= number: 1039*9c5db199SXin Li return retry 1040*9c5db199SXin Li logging.warning('Could not establish channel. Using retry=0.') 1041*9c5db199SXin Li return 0 1042*9c5db199SXin Li 1043*9c5db199SXin Li def _is_tablet_mode_device(self): 1044*9c5db199SXin Li """Returns if running the test on a tabled mode device""" 1045*9c5db199SXin Li # TODO(kinaba): consider adding per-model check 1046*9c5db199SXin Li board = self._get_board_name() 1047*9c5db199SXin Li return any(board.startswith(b) for b in constants.TABLET_MODE_BOARDS) 1048*9c5db199SXin Li 1049*9c5db199SXin Li def _run_commands(self, commands, **kwargs): 1050*9c5db199SXin Li """Run commands on all the hosts.""" 1051*9c5db199SXin Li # We need to copy the ADB key to the device to run adb on it. 1052*9c5db199SXin Li pre_commands = [] 1053*9c5db199SXin Li if any(command.startswith('adb ') for command in commands): 1054*9c5db199SXin Li key_path = '/tmp/arc.adb_key' 1055*9c5db199SXin Li for host in self._hosts: 1056*9c5db199SXin Li host.env['ADB_VENDOR_KEYS'] = key_path 1057*9c5db199SXin Li pre_commands = [ 1058*9c5db199SXin Li 'adb kill-server', 1059*9c5db199SXin Li 'echo %s > %s' % 1060*9c5db199SXin Li (pipes.quote(constants.PRIVATE_KEY), key_path) 1061*9c5db199SXin Li ] 1062*9c5db199SXin Li 1063*9c5db199SXin Li for host in self._hosts: 1064*9c5db199SXin Li if pre_commands: 1065*9c5db199SXin Li logging.info('Running DUT adb setup') 1066*9c5db199SXin Li for command in pre_commands: 1067*9c5db199SXin Li host.run(command, ignore_status=True, verbose=False) 1068*9c5db199SXin Li for command in commands: 1069*9c5db199SXin Li logging.info('RUN: %s\n', command) 1070*9c5db199SXin Li output = host.run(command, **kwargs) 1071*9c5db199SXin Li logging.info('END: %s\n', command) 1072*9c5db199SXin Li logging.debug(output) 1073*9c5db199SXin Li 1074*9c5db199SXin Li def _override_powerd_prefs(self): 1075*9c5db199SXin Li """Overrides powerd prefs to prevent screen from turning off, complying 1076*9c5db199SXin Li with CTS requirements. 1077*9c5db199SXin Li 1078*9c5db199SXin Li This is a remote version of PowerPrefChanger which ensures overrided 1079*9c5db199SXin Li policies won't persist across reboots by bind-mounting onto the config 1080*9c5db199SXin Li directory. 1081*9c5db199SXin Li """ 1082*9c5db199SXin Li pref_dir = constants.POWERD_PREF_DIR 1083*9c5db199SXin Li temp_dir = constants.POWERD_TEMP_DIR 1084*9c5db199SXin Li commands = ( 1085*9c5db199SXin Li 'cp -r %s %s' % (pref_dir, temp_dir), 1086*9c5db199SXin Li 'echo 1 > %s/ignore_external_policy' % temp_dir, 1087*9c5db199SXin Li 'echo 0 | tee %s/{,un}plugged_{dim,off,suspend}_ms' % temp_dir, 1088*9c5db199SXin Li 'mount --bind %s %s' % (temp_dir, pref_dir), 1089*9c5db199SXin Li 'restart powerd', 1090*9c5db199SXin Li ) 1091*9c5db199SXin Li try: 1092*9c5db199SXin Li self._run_commands(commands) 1093*9c5db199SXin Li except (error.AutoservRunError, error.AutoservSSHTimeout): 1094*9c5db199SXin Li logging.warning('Failed to override powerd policy, tests depending ' 1095*9c5db199SXin Li 'on screen being always on may fail.') 1096*9c5db199SXin Li 1097*9c5db199SXin Li def _restore_powerd_prefs(self): 1098*9c5db199SXin Li """Restores powerd prefs overrided by _override_powerd_prefs().""" 1099*9c5db199SXin Li pref_dir = constants.POWERD_PREF_DIR 1100*9c5db199SXin Li temp_dir = constants.POWERD_TEMP_DIR 1101*9c5db199SXin Li commands = ( 1102*9c5db199SXin Li 'umount %s' % pref_dir, 1103*9c5db199SXin Li 'restart powerd', 1104*9c5db199SXin Li 'rm -rf %s' % temp_dir, 1105*9c5db199SXin Li ) 1106*9c5db199SXin Li try: 1107*9c5db199SXin Li self._run_commands(commands) 1108*9c5db199SXin Li except (error.AutoservRunError, error.AutoservSSHTimeout): 1109*9c5db199SXin Li logging.warning('Failed to restore powerd policy, overrided policy ' 1110*9c5db199SXin Li 'will persist until device reboot.') 1111*9c5db199SXin Li 1112*9c5db199SXin Li def _should_set_cpu_governor(self, target_module, board): 1113*9c5db199SXin Li """Returns whether we should set performance governor.""" 1114*9c5db199SXin Li # TODO(kinaba): The current restore logic only applies to Kukui 1115*9c5db199SXin Li # and Trogdor. Please update the logic when expanding the scope. 1116*9c5db199SXin Li return (target_module and "CtsDeqp" in target_module) and (board in [ 1117*9c5db199SXin Li 'kukui-arc-r', 'trogdor-arc-r' 1118*9c5db199SXin Li ]) 1119*9c5db199SXin Li 1120*9c5db199SXin Li def _set_cpu_governor(self, governor): 1121*9c5db199SXin Li """Set the specified CPU governor.""" 1122*9c5db199SXin Li self._run_commands([('for i in /sys/devices/system/cpu/cpufreq/*; do' 1123*9c5db199SXin Li ' echo %s > $i/scaling_governor; done') % governor 1124*9c5db199SXin Li ]) 1125*9c5db199SXin Li 1126*9c5db199SXin Li def _override_cpu_governor(self): 1127*9c5db199SXin Li """Override the CPU governor for performance mode.""" 1128*9c5db199SXin Li try: 1129*9c5db199SXin Li self._set_cpu_governor('performance') 1130*9c5db199SXin Li except (error.AutoservRunError, error.AutoservSSHTimeout): 1131*9c5db199SXin Li logging.warning('Failed to override CPU governor, tests depending ' 1132*9c5db199SXin Li 'on boosted performance may fail.') 1133*9c5db199SXin Li 1134*9c5db199SXin Li def _restore_cpu_governor(self): 1135*9c5db199SXin Li """Restore the CPU governor to the default value.""" 1136*9c5db199SXin Li try: 1137*9c5db199SXin Li self._set_cpu_governor('schedutil') 1138*9c5db199SXin Li except (error.AutoservRunError, error.AutoservSSHTimeout): 1139*9c5db199SXin Li logging.warning('Failed to restore CPU governor, overrided policy ' 1140*9c5db199SXin Li 'will persist until device reboot.') 1141*9c5db199SXin Li 1142*9c5db199SXin Li def _mute_device(self): 1143*9c5db199SXin Li """Mutes the device to avoid noises while running tests""" 1144*9c5db199SXin Li try: 1145*9c5db199SXin Li self._run_commands(['cras_test_client --mute 1'], 1146*9c5db199SXin Li ignore_status=True) 1147*9c5db199SXin Li except: 1148*9c5db199SXin Li logging.warning('Failed to mute the device') 1149*9c5db199SXin Li 1150*9c5db199SXin Li def _clean_crash_logs(self): 1151*9c5db199SXin Li try: 1152*9c5db199SXin Li self._run_commands(['rm -f /home/chronos/crash/*']) 1153*9c5db199SXin Li except (error.AutoservRunError, error.AutoservSSHTimeout): 1154*9c5db199SXin Li logging.warning('Failed to clean up crash logs.') 1155*9c5db199SXin Li 1156*9c5db199SXin Li def _run_and_parse_tradefed(self, command): 1157*9c5db199SXin Li """Kick off the tradefed command. 1158*9c5db199SXin Li 1159*9c5db199SXin Li @param command: Lists of command tokens. 1160*9c5db199SXin Li @raise TestFail: when a test failure is detected. 1161*9c5db199SXin Li @return: tuple of (tests, pass, fail, notexecuted) counts. 1162*9c5db199SXin Li """ 1163*9c5db199SXin Li target_argument = [] 1164*9c5db199SXin Li for host in self._hosts: 1165*9c5db199SXin Li target_argument += ['-s', adb_utils.get_adb_target(host)] 1166*9c5db199SXin Li shard_argument = [] 1167*9c5db199SXin Li if len(self._hosts) > 1: 1168*9c5db199SXin Li if self._SHARD_CMD: 1169*9c5db199SXin Li shard_argument = [self._SHARD_CMD, str(len(self._hosts))] 1170*9c5db199SXin Li else: 1171*9c5db199SXin Li logging.warning('cts-tradefed shard command isn\'t defined, ' 1172*9c5db199SXin Li 'falling back to use single device.') 1173*9c5db199SXin Li command = command + target_argument + shard_argument 1174*9c5db199SXin Li 1175*9c5db199SXin Li try: 1176*9c5db199SXin Li output = self._run_tradefed(command) 1177*9c5db199SXin Li except Exception as e: 1178*9c5db199SXin Li self._log_java_version() 1179*9c5db199SXin Li if not isinstance(e, error.CmdTimeoutError): 1180*9c5db199SXin Li # In case this happened due to file corruptions, try to 1181*9c5db199SXin Li # force to recreate the cache. 1182*9c5db199SXin Li logging.error('Failed to run tradefed! Cleaning up now.') 1183*9c5db199SXin Li self._clean_download_cache_if_needed(force=True) 1184*9c5db199SXin Li raise 1185*9c5db199SXin Li 1186*9c5db199SXin Li result_destination = self._default_tradefed_base_dir() 1187*9c5db199SXin Li # Gather the global log first. Datetime parsing below can abort the test 1188*9c5db199SXin Li # if tradefed startup had failed. Even then the global log is useful. 1189*9c5db199SXin Li self._collect_tradefed_global_log(output, result_destination) 1190*9c5db199SXin Li # Result parsing must come after all other essential operations as test 1191*9c5db199SXin Li # warnings, errors and failures can be raised here. 1192*9c5db199SXin Li base = self._default_tradefed_base_dir() 1193*9c5db199SXin Li path = tradefed_utils.get_test_result_xml_path(base) 1194*9c5db199SXin Li return tradefed_utils.parse_tradefed_testresults_xml( 1195*9c5db199SXin Li test_result_xml_path=path, 1196*9c5db199SXin Li waivers=self._waivers) 1197*9c5db199SXin Li 1198*9c5db199SXin Li def _setup_result_directories(self): 1199*9c5db199SXin Li """Sets up the results and logs directories for tradefed. 1200*9c5db199SXin Li 1201*9c5db199SXin Li Tradefed saves the logs and results at: 1202*9c5db199SXin Li self._repository/results/$datetime/ 1203*9c5db199SXin Li self._repository/results/$datetime.zip 1204*9c5db199SXin Li self._repository/logs/$datetime/ 1205*9c5db199SXin Li Because other tools rely on the currently chosen Google storage paths 1206*9c5db199SXin Li we need to keep destination_results in: 1207*9c5db199SXin Li self.resultsdir/android-cts/results/$datetime/ 1208*9c5db199SXin Li self.resultsdir/android-cts/results/$datetime.zip 1209*9c5db199SXin Li self.resultsdir/android-cts/results/logs/$datetime/ 1210*9c5db199SXin Li To bridge between them, create symlinks from the former to the latter. 1211*9c5db199SXin Li """ 1212*9c5db199SXin Li logging.info('Setting up tradefed results and logs directories.') 1213*9c5db199SXin Li 1214*9c5db199SXin Li results_destination = self._default_tradefed_base_dir() 1215*9c5db199SXin Li logs_destination = os.path.join(results_destination, 'logs') 1216*9c5db199SXin Li directory_mapping = [ 1217*9c5db199SXin Li (os.path.join(self._repository, 'results'), results_destination), 1218*9c5db199SXin Li (os.path.join(self._repository, 'logs'), logs_destination), 1219*9c5db199SXin Li ] 1220*9c5db199SXin Li 1221*9c5db199SXin Li for (tradefed_path, final_path) in directory_mapping: 1222*9c5db199SXin Li if os.path.exists(tradefed_path): 1223*9c5db199SXin Li shutil.rmtree(tradefed_path) 1224*9c5db199SXin Li self._safe_makedirs(final_path) 1225*9c5db199SXin Li os.symlink(final_path, tradefed_path) 1226*9c5db199SXin Li 1227*9c5db199SXin Li def _default_tradefed_base_dir(self): 1228*9c5db199SXin Li return os.path.join(self.resultsdir, self._get_tradefed_base_dir()) 1229*9c5db199SXin Li 1230*9c5db199SXin Li def _install_plan(self, subplan): 1231*9c5db199SXin Li """Copy test subplan to CTS-TF. 1232*9c5db199SXin Li 1233*9c5db199SXin Li @param subplan: CTS subplan to be copied into TF. 1234*9c5db199SXin Li """ 1235*9c5db199SXin Li logging.info('Install subplan: %s', subplan) 1236*9c5db199SXin Li subplans_tf_dir = os.path.join(self._repository, 'subplans') 1237*9c5db199SXin Li if not os.path.exists(subplans_tf_dir): 1238*9c5db199SXin Li os.makedirs(subplans_tf_dir) 1239*9c5db199SXin Li test_subplan_file = os.path.join(self.bindir, 'subplans', 1240*9c5db199SXin Li '%s.xml' % subplan) 1241*9c5db199SXin Li try: 1242*9c5db199SXin Li shutil.copy(test_subplan_file, subplans_tf_dir) 1243*9c5db199SXin Li except (shutil.Error, OSError, IOError) as e: 1244*9c5db199SXin Li raise error.TestFail( 1245*9c5db199SXin Li 'Error: failed to copy test subplan %s to CTS bundle. %s' % 1246*9c5db199SXin Li (test_subplan_file, e)) 1247*9c5db199SXin Li 1248*9c5db199SXin Li def _should_skip_test(self, _bundle): 1249*9c5db199SXin Li """Some tests are expected to fail and are skipped. 1250*9c5db199SXin Li 1251*9c5db199SXin Li Subclasses should override with specific details. 1252*9c5db199SXin Li """ 1253*9c5db199SXin Li return False 1254*9c5db199SXin Li 1255*9c5db199SXin Li def _should_reboot(self, steps): 1256*9c5db199SXin Li """Oracle to decide if DUT should reboot or just restart Chrome. 1257*9c5db199SXin Li 1258*9c5db199SXin Li For now we will not reboot after the first two iterations, but on all 1259*9c5db199SXin Li iterations afterward as before. In particular this means that most CTS 1260*9c5db199SXin Li tests will now not get a "clean" machine, but one on which tests ran 1261*9c5db199SXin Li before. But we will still reboot after persistent failures, hopefully 1262*9c5db199SXin Li not causing too many flakes down the line. 1263*9c5db199SXin Li """ 1264*9c5db199SXin Li if steps < 3: 1265*9c5db199SXin Li return False 1266*9c5db199SXin Li return True 1267*9c5db199SXin Li 1268*9c5db199SXin Li def _copy_extra_artifacts_dut(self, extra_artifacts, host, output_dir): 1269*9c5db199SXin Li """ Upload the custom artifacts """ 1270*9c5db199SXin Li self._safe_makedirs(output_dir) 1271*9c5db199SXin Li 1272*9c5db199SXin Li for artifact in extra_artifacts: 1273*9c5db199SXin Li logging.info('Copying extra artifacts from "%s" to "%s".', 1274*9c5db199SXin Li artifact, output_dir) 1275*9c5db199SXin Li try: 1276*9c5db199SXin Li self._adb.run(host, 1277*9c5db199SXin Li verbose=True, 1278*9c5db199SXin Li timeout=120, 1279*9c5db199SXin Li args=('pull', artifact, output_dir)) 1280*9c5db199SXin Li except: 1281*9c5db199SXin Li # Maybe ADB connection failed, or the artifacts don't exist. 1282*9c5db199SXin Li logging.exception('Copying extra artifacts failed.') 1283*9c5db199SXin Li 1284*9c5db199SXin Li def _copy_extra_artifacts_host(self, extra_artifacts, host, output_dir): 1285*9c5db199SXin Li """ Upload the custom artifacts """ 1286*9c5db199SXin Li self._safe_makedirs(output_dir) 1287*9c5db199SXin Li 1288*9c5db199SXin Li for artifact in extra_artifacts: 1289*9c5db199SXin Li logging.info('Copying extra artifacts from "%s" to "%s".', 1290*9c5db199SXin Li artifact, output_dir) 1291*9c5db199SXin Li for extracted_path in glob.glob(artifact): 1292*9c5db199SXin Li logging.info('... %s', extracted_path) 1293*9c5db199SXin Li # Move it not to collect it again in future retries. 1294*9c5db199SXin Li shutil.move(extracted_path, output_dir) 1295*9c5db199SXin Li 1296*9c5db199SXin Li def _run_tradefed_list_results(self): 1297*9c5db199SXin Li """Run the `tradefed list results` command. 1298*9c5db199SXin Li 1299*9c5db199SXin Li @return: tuple of the last (session_id, pass, fail, all_done?). 1300*9c5db199SXin Li """ 1301*9c5db199SXin Li 1302*9c5db199SXin Li # Fix b/143580192: We set the timeout to 3 min. It never takes more than 1303*9c5db199SXin Li # 10s on light disk load. 1304*9c5db199SXin Li output = self._run_tradefed_with_timeout(['list', 'results'], 180) 1305*9c5db199SXin Li 1306*9c5db199SXin Li # Parses the last session from the output that looks like: 1307*9c5db199SXin Li # 1308*9c5db199SXin Li # Session Pass Fail Modules Complete ... 1309*9c5db199SXin Li # 0 90 10 1 of 2 1310*9c5db199SXin Li # 1 199 1 2 of 2 1311*9c5db199SXin Li # ... 1312*9c5db199SXin Li lastmatch = None 1313*9c5db199SXin Li for m in re.finditer(r'^(\d+)\s+(\d+)\s+(\d+)\s+(\d+) of (\d+)', 1314*9c5db199SXin Li output.stdout, re.MULTILINE): 1315*9c5db199SXin Li session, passed, failed, done, total = map(int, 1316*9c5db199SXin Li m.group(1, 2, 3, 4, 5)) 1317*9c5db199SXin Li lastmatch = (session, passed, failed, done == total) 1318*9c5db199SXin Li return lastmatch 1319*9c5db199SXin Li 1320*9c5db199SXin Li def _get_bundle_url(self, uri, bundle): 1321*9c5db199SXin Li # TODO: Replace with NotImplementedError once all subclasses are done 1322*9c5db199SXin Li return self._get_latest_bundle_url(bundle) if uri == 'LATEST' else ( 1323*9c5db199SXin Li uri or self._get_default_bundle_url(bundle)) 1324*9c5db199SXin Li 1325*9c5db199SXin Li def _tradefed_retry_command(self, template, session_id): 1326*9c5db199SXin Li raise NotImplementedError('Subclass should override this function') 1327*9c5db199SXin Li 1328*9c5db199SXin Li def _tradefed_run_command(self, template): 1329*9c5db199SXin Li raise NotImplementedError('Subclass should override this function') 1330*9c5db199SXin Li 1331*9c5db199SXin Li def _tradefed_cmd_path(self): 1332*9c5db199SXin Li raise NotImplementedError('Subclass should override this function') 1333*9c5db199SXin Li 1334*9c5db199SXin Li def _tradefed_env(self): 1335*9c5db199SXin Li return None 1336*9c5db199SXin Li 1337*9c5db199SXin Li def _run_tradefed_with_timeout(self, command, timeout): 1338*9c5db199SXin Li tradefed = self._tradefed_cmd_path() 1339*9c5db199SXin Li with tradefed_utils.adb_keepalive( 1340*9c5db199SXin Li adb_utils.get_adb_targets(self._hosts), self._install_paths): 1341*9c5db199SXin Li logging.info('RUN(timeout=%d): %s', timeout, 1342*9c5db199SXin Li ' '.join([tradefed] + command)) 1343*9c5db199SXin Li output = self._run( 1344*9c5db199SXin Li tradefed, 1345*9c5db199SXin Li args=tuple(command), 1346*9c5db199SXin Li env=self._tradefed_env(), 1347*9c5db199SXin Li timeout=timeout, 1348*9c5db199SXin Li verbose=True, 1349*9c5db199SXin Li ignore_status=False, 1350*9c5db199SXin Li # Make sure to tee tradefed stdout/stderr to autotest logs 1351*9c5db199SXin Li # continuously during the test run. 1352*9c5db199SXin Li stdout_tee=utils.TEE_TO_LOGS, 1353*9c5db199SXin Li stderr_tee=utils.TEE_TO_LOGS) 1354*9c5db199SXin Li logging.info('END: %s\n', ' '.join([tradefed] + command)) 1355*9c5db199SXin Li return output 1356*9c5db199SXin Li 1357*9c5db199SXin Li def _run_tradefed(self, command): 1358*9c5db199SXin Li timeout = self._timeout * self._timeout_factor 1359*9c5db199SXin Li if self._job_deadline is not None: 1360*9c5db199SXin Li clipped = int(min(timeout, self._job_deadline - time.time())) 1361*9c5db199SXin Li # Even the shortest tradefed run takes 1.5 minutes. Took 2x'ed 1362*9c5db199SXin Li # value as a threshold that a meaningful test can run. 1363*9c5db199SXin Li if clipped < 3 * 60: 1364*9c5db199SXin Li raise error.TestError( 1365*9c5db199SXin Li 'Hitting job time limit: only %s seconds left' % 1366*9c5db199SXin Li clipped) 1367*9c5db199SXin Li timeout = clipped 1368*9c5db199SXin Li return self._run_tradefed_with_timeout(command, timeout) 1369*9c5db199SXin Li 1370*9c5db199SXin Li def _run_tradefed_with_retries(self, 1371*9c5db199SXin Li test_name, 1372*9c5db199SXin Li run_template, 1373*9c5db199SXin Li retry_template, 1374*9c5db199SXin Li timeout, 1375*9c5db199SXin Li media_asset=None, 1376*9c5db199SXin Li enable_default_apps=False, 1377*9c5db199SXin Li target_module=None, 1378*9c5db199SXin Li target_plan=None, 1379*9c5db199SXin Li executable_test_count=None, 1380*9c5db199SXin Li bundle=None, 1381*9c5db199SXin Li use_helpers=False, 1382*9c5db199SXin Li extra_artifacts=[], 1383*9c5db199SXin Li extra_artifacts_host=[], 1384*9c5db199SXin Li login_precondition_commands=[], 1385*9c5db199SXin Li precondition_commands=[], 1386*9c5db199SXin Li prerequisites=[]): 1387*9c5db199SXin Li """Run CTS/GTS with retry logic. 1388*9c5db199SXin Li 1389*9c5db199SXin Li We first kick off the specified module. Then rerun just the failures 1390*9c5db199SXin Li on the next MAX_RETRY iterations. 1391*9c5db199SXin Li """ 1392*9c5db199SXin Li for prereq in prerequisites: 1393*9c5db199SXin Li result = tradefed_prerequisite.check(prereq, self._hosts) 1394*9c5db199SXin Li if not result[0]: 1395*9c5db199SXin Li raise error.TestError(result[1]) 1396*9c5db199SXin Li 1397*9c5db199SXin Li # On dev and beta channels timeouts are sharp, lenient on stable. 1398*9c5db199SXin Li self._timeout = timeout 1399*9c5db199SXin Li if (self._get_release_branch_number() >= 1400*9c5db199SXin Li constants.APPROXIMATE_STABLE_BRANCH_NUMBER): 1401*9c5db199SXin Li self._timeout += 3600 1402*9c5db199SXin Li 1403*9c5db199SXin Li if self._should_skip_test(bundle): 1404*9c5db199SXin Li logging.warning('Skipped test %s', ' '.join(test_name)) 1405*9c5db199SXin Li return 1406*9c5db199SXin Li 1407*9c5db199SXin Li steps = -1 # For historic reasons the first iteration is not counted. 1408*9c5db199SXin Li self.summary = '' 1409*9c5db199SXin Li board = self._get_board_name() 1410*9c5db199SXin Li session_id = None 1411*9c5db199SXin Li 1412*9c5db199SXin Li self._setup_result_directories() 1413*9c5db199SXin Li if media_asset: 1414*9c5db199SXin Li self._prepare_media(media_asset) 1415*9c5db199SXin Li 1416*9c5db199SXin Li # This loop retries failures. For this reason please do not raise 1417*9c5db199SXin Li # TestFail in this loop if you suspect the failure might be fixed 1418*9c5db199SXin Li # in the next loop iteration. 1419*9c5db199SXin Li while steps < self._max_retry: 1420*9c5db199SXin Li steps += 1 1421*9c5db199SXin Li keep_media = media_asset and media_asset.uri and steps >= 1 1422*9c5db199SXin Li self._run_commands(login_precondition_commands, ignore_status=True) 1423*9c5db199SXin Li # TODO(kinaba): Make it a general config (per-model choice 1424*9c5db199SXin Li # of tablet,clamshell,default) if the code below works. 1425*9c5db199SXin Li if utils.is_in_container() and not client_utils.is_moblab(): 1426*9c5db199SXin Li # Force laptop mode for non TABLET_MODE_BOARDS 1427*9c5db199SXin Li if not self._is_tablet_mode_device(): 1428*9c5db199SXin Li self._run_commands( 1429*9c5db199SXin Li ['inject_powerd_input_event --code=tablet --value=0'], 1430*9c5db199SXin Li ignore_status=True) 1431*9c5db199SXin Li 1432*9c5db199SXin Li session_log_dir = os.path.join(self.resultsdir, 1433*9c5db199SXin Li 'login_session_log', 1434*9c5db199SXin Li 'step%02d' % steps) 1435*9c5db199SXin Li with login.login_chrome(hosts=self._hosts, 1436*9c5db199SXin Li board=board, 1437*9c5db199SXin Li dont_override_profile=keep_media, 1438*9c5db199SXin Li enable_default_apps=enable_default_apps, 1439*9c5db199SXin Li log_dir=session_log_dir) as current_logins: 1440*9c5db199SXin Li if self._should_reboot(steps): 1441*9c5db199SXin Li # TODO(rohitbm): Evaluate if power cycle really helps with 1442*9c5db199SXin Li # Bluetooth test failures, and then make the implementation 1443*9c5db199SXin Li # more strict by first running complete restart and reboot 1444*9c5db199SXin Li # retries and then perform power cycle. 1445*9c5db199SXin Li # 1446*9c5db199SXin Li # Currently, (steps + 1 == self._max_retry) means that 1447*9c5db199SXin Li # hard_reboot is attempted after "this" cycle failed. Then, 1448*9c5db199SXin Li # the last remaining 1 step will be run on the rebooted DUT. 1449*9c5db199SXin Li hard_reboot = (self._hard_reboot_on_failure 1450*9c5db199SXin Li and steps + 1 == self._max_retry) 1451*9c5db199SXin Li for current_login in current_logins: 1452*9c5db199SXin Li current_login.need_reboot(hard_reboot=hard_reboot) 1453*9c5db199SXin Li self._ready_arc() 1454*9c5db199SXin Li self._calculate_test_count_factor(bundle) 1455*9c5db199SXin Li 1456*9c5db199SXin Li # Check the ABI list and skip (pass) the tests if not applicable. 1457*9c5db199SXin Li # This needs to be done after _ready_arc() for reading the device's 1458*9c5db199SXin Li # ABI list from the booted ARC instance. 1459*9c5db199SXin Li if '--abi' in run_template: 1460*9c5db199SXin Li abi = run_template[run_template.index('--abi') + 1] 1461*9c5db199SXin Li abilist = self._get_abilist() 1462*9c5db199SXin Li if abilist and abi not in abilist: 1463*9c5db199SXin Li logging.info( 1464*9c5db199SXin Li 'Specified ABI %s is not in the device ABI list %s. Skipping.', 1465*9c5db199SXin Li abi, abilist) 1466*9c5db199SXin Li return 1467*9c5db199SXin Li 1468*9c5db199SXin Li # For CtsMediaStressTestCases, push media assets in advance if 1469*9c5db199SXin Li # applicable. 1470*9c5db199SXin Li if (not keep_media and media_asset 1471*9c5db199SXin Li and self._should_push_mediastress_asset( 1472*9c5db199SXin Li target_module, board)): 1473*9c5db199SXin Li self._push_mediastress_asset(media_asset) 1474*9c5db199SXin Li 1475*9c5db199SXin Li self._run_commands(precondition_commands, ignore_status=True) 1476*9c5db199SXin Li if use_helpers: 1477*9c5db199SXin Li self._fetch_helpers_from_dut() 1478*9c5db199SXin Li 1479*9c5db199SXin Li # Run tradefed. 1480*9c5db199SXin Li if session_id == None: 1481*9c5db199SXin Li if target_plan is not None: 1482*9c5db199SXin Li self._install_plan(target_plan) 1483*9c5db199SXin Li 1484*9c5db199SXin Li logging.info('Running %s:', test_name) 1485*9c5db199SXin Li command = self._tradefed_run_command(run_template) 1486*9c5db199SXin Li else: 1487*9c5db199SXin Li logging.info('Retrying failures of %s with session_id %d:', 1488*9c5db199SXin Li test_name, session_id) 1489*9c5db199SXin Li command = self._tradefed_retry_command(retry_template, 1490*9c5db199SXin Li session_id) 1491*9c5db199SXin Li 1492*9c5db199SXin Li if media_asset and media_asset.uri: 1493*9c5db199SXin Li # Clean-up crash logs from previous sessions to ensure 1494*9c5db199SXin Li # enough disk space for 16GB storage devices: b/156075084. 1495*9c5db199SXin Li if not keep_media: 1496*9c5db199SXin Li self._clean_crash_logs() 1497*9c5db199SXin Li # b/196748125. Mute before running tests to avoid noises. 1498*9c5db199SXin Li self._mute_device() 1499*9c5db199SXin Li set_performance_governor = self._should_set_cpu_governor( 1500*9c5db199SXin Li target_module, board) 1501*9c5db199SXin Li # TODO(b/182397469): speculatively disable the "screen-on" 1502*9c5db199SXin Li # handler for dEQP. Revert when the issue is resolved. 1503*9c5db199SXin Li keep_screen_on = not (target_module 1504*9c5db199SXin Li and "CtsDeqpTestCases" in target_module) 1505*9c5db199SXin Li if set_performance_governor: 1506*9c5db199SXin Li self._override_cpu_governor() 1507*9c5db199SXin Li if keep_screen_on: 1508*9c5db199SXin Li self._override_powerd_prefs() 1509*9c5db199SXin Li try: 1510*9c5db199SXin Li waived_tests = self._run_and_parse_tradefed(command) 1511*9c5db199SXin Li finally: 1512*9c5db199SXin Li if keep_screen_on: 1513*9c5db199SXin Li self._restore_powerd_prefs() 1514*9c5db199SXin Li if set_performance_governor: 1515*9c5db199SXin Li self._restore_cpu_governor() 1516*9c5db199SXin Li if media_asset: 1517*9c5db199SXin Li self._fail_on_unexpected_media_download(media_asset) 1518*9c5db199SXin Li result = self._run_tradefed_list_results() 1519*9c5db199SXin Li if not result: 1520*9c5db199SXin Li logging.error('Did not find any test results. Retry.') 1521*9c5db199SXin Li for current_login in current_logins: 1522*9c5db199SXin Li current_login.need_reboot() 1523*9c5db199SXin Li continue 1524*9c5db199SXin Li 1525*9c5db199SXin Li last_waived = len(waived_tests) 1526*9c5db199SXin Li last_session_id, last_passed, last_failed, last_all_done =\ 1527*9c5db199SXin Li result 1528*9c5db199SXin Li 1529*9c5db199SXin Li if last_failed > last_waived or not utils.is_in_container(): 1530*9c5db199SXin Li for host in self._hosts: 1531*9c5db199SXin Li dir_name = "%s-step%02d" % (host.hostname, steps) 1532*9c5db199SXin Li output_dir = os.path.join( 1533*9c5db199SXin Li self.resultsdir, 'extra_artifacts', dir_name) 1534*9c5db199SXin Li self._copy_extra_artifacts_dut( 1535*9c5db199SXin Li extra_artifacts, host, output_dir) 1536*9c5db199SXin Li self._copy_extra_artifacts_host( 1537*9c5db199SXin Li extra_artifacts_host, host, output_dir) 1538*9c5db199SXin Li 1539*9c5db199SXin Li if last_passed + last_failed > 0: 1540*9c5db199SXin Li # At least one test had run, which means the media push step 1541*9c5db199SXin Li # of tradefed didn't fail. To free up the storage earlier, 1542*9c5db199SXin Li # delete the copy on the server side. See crbug.com/970881 1543*9c5db199SXin Li if media_asset: 1544*9c5db199SXin Li self._cleanup_media(media_asset) 1545*9c5db199SXin Li 1546*9c5db199SXin Li if last_failed < last_waived: 1547*9c5db199SXin Li logging.error( 1548*9c5db199SXin Li 'Error: Internal waiver bookkeeping has become ' 1549*9c5db199SXin Li 'inconsistent (f=%d, w=%d)', last_failed, last_waived) 1550*9c5db199SXin Li 1551*9c5db199SXin Li msg = 'run' if session_id == None else ' retry' 1552*9c5db199SXin Li msg += '(p=%s, f=%s, w=%s)' % (last_passed, last_failed, 1553*9c5db199SXin Li last_waived) 1554*9c5db199SXin Li self.summary += msg 1555*9c5db199SXin Li logging.info('RESULT: %s %s', msg, result) 1556*9c5db199SXin Li 1557*9c5db199SXin Li # Overwrite last_all_done if the executed test count is equal 1558*9c5db199SXin Li # to the known test count of the job. 1559*9c5db199SXin Li if (not last_all_done and executable_test_count != None and 1560*9c5db199SXin Li (last_passed + last_failed in executable_test_count)): 1561*9c5db199SXin Li logging.warning('Overwriting all_done as True, since the ' 1562*9c5db199SXin Li 'explicitly set executable_test_count ' 1563*9c5db199SXin Li 'tests have run.') 1564*9c5db199SXin Li last_all_done = True 1565*9c5db199SXin Li 1566*9c5db199SXin Li # Check for no-test modules. We use the "all_done" indicator 1567*9c5db199SXin Li # provided by list_results to decide if there are outstanding 1568*9c5db199SXin Li # modules to iterate over (similar to missing tests just on a 1569*9c5db199SXin Li # per-module basis). 1570*9c5db199SXin Li notest = (last_passed + last_failed == 0 and last_all_done) 1571*9c5db199SXin Li if target_module in self._notest_modules: 1572*9c5db199SXin Li if notest: 1573*9c5db199SXin Li logging.info('Package has no tests as expected.') 1574*9c5db199SXin Li return 1575*9c5db199SXin Li else: 1576*9c5db199SXin Li # We expected no tests, but the new bundle drop must 1577*9c5db199SXin Li # have added some for us. Alert us to the situation. 1578*9c5db199SXin Li raise error.TestFail( 1579*9c5db199SXin Li 'Failed: Remove module %s from ' 1580*9c5db199SXin Li 'notest_modules directory!' % target_module) 1581*9c5db199SXin Li elif notest: 1582*9c5db199SXin Li logging.error('Did not find any tests in module. Hoping ' 1583*9c5db199SXin Li 'this is transient. Retry after reboot.') 1584*9c5db199SXin Li for current_login in current_logins: 1585*9c5db199SXin Li current_login.need_reboot() 1586*9c5db199SXin Li continue 1587*9c5db199SXin Li 1588*9c5db199SXin Li # After the no-test check, commit the pass/fail count. 1589*9c5db199SXin Li waived = last_waived 1590*9c5db199SXin Li session_id, passed, failed, all_done =\ 1591*9c5db199SXin Li last_session_id, last_passed, last_failed, last_all_done 1592*9c5db199SXin Li 1593*9c5db199SXin Li # Check if all the tests passed. 1594*9c5db199SXin Li if failed <= waived and all_done: 1595*9c5db199SXin Li break 1596*9c5db199SXin Li 1597*9c5db199SXin Li # TODO(b/127908450) Tradefed loses track of not-executed tests 1598*9c5db199SXin Li # when the commandline pattern included '*', and retry run for 1599*9c5db199SXin Li # them wrongly declares all tests passed. This is misleading. 1600*9c5db199SXin Li # Rather, we give up the retry and report the result as FAIL. 1601*9c5db199SXin Li if not all_done and '*' in ''.join(run_template): 1602*9c5db199SXin Li break 1603*9c5db199SXin Li 1604*9c5db199SXin Li if session_id == None: 1605*9c5db199SXin Li raise error.TestFail('Error: Could not find any tests in module.') 1606*9c5db199SXin Li 1607*9c5db199SXin Li if failed <= waived and all_done: 1608*9c5db199SXin Li # TODO(ihf): Make this error.TestPass('...') once 1609*9c5db199SXin Li # available. 1610*9c5db199SXin Li if steps > 0 and self._warn_on_test_retry: 1611*9c5db199SXin Li raise error.TestWarn( 1612*9c5db199SXin Li 'Passed: after %d retries passing %d tests, ' 1613*9c5db199SXin Li 'waived=%d. %s' % (steps, passed, waived, 1614*9c5db199SXin Li self.summary)) 1615*9c5db199SXin Li return 1616*9c5db199SXin Li 1617*9c5db199SXin Li raise error.TestFail( 1618*9c5db199SXin Li 'Failed: after %d retries giving up. ' 1619*9c5db199SXin Li 'passed=%d, failed=%d, waived=%d%s. %s' % 1620*9c5db199SXin Li (steps, passed, failed, waived, 1621*9c5db199SXin Li '' if all_done else ', notexec>=1', self.summary)) 1622