1#!/usr/bin/env vpython3 2# Copyright 2015 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Runs a script that can run as an isolate (or not). 7 8If optional argument --isolated-script-test-output=[FILENAME] is passed 9to the script, json is written to that file in the format detailed in 10//docs/testing/json-test-results-format.md. 11 12If optional argument --isolated-script-test-filter=[TEST_NAMES] is passed to 13the script, it should be a double-colon-separated ("::") list of test names, 14to run just that subset of tests. 15 16This script is intended to be the base command invoked by the isolate, 17followed by a subsequent Python script. It could be generalized to 18invoke an arbitrary executable. 19""" 20 21import argparse 22import json 23import os 24import pprint 25import sys 26import tempfile 27 28 29# Add src/testing/ into sys.path for importing common. 30sys.path.append( 31 os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))) 32from scripts import common 33 34 35# Some harnesses understand the --isolated-script-test arguments 36# directly and prefer that they be passed through. 37KNOWN_ISOLATED_SCRIPT_TEST_RUNNERS = {'run_web_tests.py', 'run_webgpu_cts.py'} 38 39 40# Known typ test runners this script wraps. They need a different argument name 41# when selecting which tests to run. 42# TODO(dpranke): Detect if the wrapped test suite uses typ better. 43KNOWN_TYP_TEST_RUNNERS = { 44 'metrics_python_tests.py', 45 'monochrome_python_tests.py', 46 'run_blinkpy_tests.py', 47 'run_mac_signing_tests.py', 48 'run_mini_installer_tests.py', 49 'test_suite_all.py', # //tools/grit:grit_python_unittests 50} 51 52KNOWN_TYP_VPYTHON3_TEST_RUNNERS = { 53 'monochrome_python_tests.py', 54 'run_polymer_tools_tests.py', 55 'test_suite_all.py', # //tools/grit:grit_python_unittests 56} 57 58# pylint: disable=super-with-arguments 59 60class BareScriptTestAdapter(common.BaseIsolatedScriptArgsAdapter): 61 def __init__(self): 62 super().__init__() 63 # Arguments that are ignored, but added here because it's easier to ignore 64 # them than to update bot configs to not pass them. 65 common.add_emulator_args(self._parser) 66 self._parser.add_argument( 67 '--coverage-dir', type=str, help='Unused') 68 self._parser.add_argument( 69 '--use-persistent-shell', action='store_true', help='Unused') 70 71 72class IsolatedScriptTestAdapter(common.BaseIsolatedScriptArgsAdapter): 73 def generate_sharding_args(self, total_shards, shard_index): 74 # This script only uses environment variable for sharding. 75 del total_shards, shard_index # unused 76 return [] 77 78 def generate_test_also_run_disabled_tests_args(self): 79 return ['--isolated-script-test-also-run-disabled-tests'] 80 81 def generate_test_filter_args(self, test_filter_str): 82 return ['--isolated-script-test-filter=%s' % test_filter_str] 83 84 def generate_test_output_args(self, output): 85 return ['--isolated-script-test-output=%s' % output] 86 87 def generate_test_launcher_retry_limit_args(self, retry_limit): 88 return ['--isolated-script-test-launcher-retry-limit=%d' % retry_limit] 89 90 def generate_test_repeat_args(self, repeat_count): 91 return ['--isolated-script-test-repeat=%d' % repeat_count] 92 93 94class TypUnittestAdapter(common.BaseIsolatedScriptArgsAdapter): 95 96 def __init__(self): 97 super(TypUnittestAdapter, self).__init__() 98 self._temp_filter_file = None 99 100 def generate_sharding_args(self, total_shards, shard_index): 101 # This script only uses environment variable for sharding. 102 del total_shards, shard_index # unused 103 return [] 104 105 def generate_test_filter_args(self, test_filter_str): 106 filter_list = common.extract_filter_list(test_filter_str) 107 self._temp_filter_file = tempfile.NamedTemporaryFile( 108 mode='w', delete=False) 109 self._temp_filter_file.write('\n'.join(filter_list)) 110 self._temp_filter_file.close() 111 arg_name = 'test-list' 112 if any(r in self.rest_args[0] for r in KNOWN_TYP_TEST_RUNNERS): 113 arg_name = 'file-list' 114 115 return ['--%s=' % arg_name + self._temp_filter_file.name] 116 117 def generate_test_output_args(self, output): 118 return ['--write-full-results-to', output] 119 120 def generate_test_launcher_retry_limit_args(self, retry_limit): 121 return ['--isolated-script-test-launcher-retry-limit=%d' % retry_limit] 122 123 def generate_test_repeat_args(self, repeat_count): 124 return ['--isolated-script-test-repeat=%d' % repeat_count] 125 126 def clean_up_after_test_run(self): 127 if self._temp_filter_file: 128 os.unlink(self._temp_filter_file.name) 129 130 def select_python_executable(self): 131 if any(r in self.rest_args[0] for r in KNOWN_TYP_VPYTHON3_TEST_RUNNERS): 132 return 'vpython3.bat' if sys.platform == 'win32' else 'vpython3' 133 return super(TypUnittestAdapter, self).select_python_executable() 134 135 136def main(): 137 parser = argparse.ArgumentParser() 138 parser.add_argument('--script-type', choices=['isolated', 'typ', 'bare']) 139 args, _ = parser.parse_known_args() 140 141 kind = args.script_type 142 if not kind: 143 if any(r in sys.argv[1] for r in KNOWN_ISOLATED_SCRIPT_TEST_RUNNERS): 144 kind = 'isolated' 145 else: 146 kind = 'typ' 147 148 if kind == 'isolated': 149 adapter = IsolatedScriptTestAdapter() 150 elif kind == 'typ': 151 adapter = TypUnittestAdapter() 152 else: 153 adapter = BareScriptTestAdapter() 154 return adapter.run_test() 155 156# This is not really a "script test" so does not need to manually add 157# any additional compile targets. 158def main_compile_targets(args): 159 json.dump([], args.output) 160 161 162if __name__ == '__main__': 163 # Conform minimally to the protocol defined by ScriptTest. 164 if 'compile_targets' in sys.argv: 165 funcs = { 166 'run': None, 167 'compile_targets': main_compile_targets, 168 } 169 sys.exit(common.run_script(sys.argv[1:], funcs)) 170 sys.exit(main()) 171