kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright 2015 The Chromium Authors. All rights reserved. |
| 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 an isolate bundled Telemetry benchmark. |
| 7 | |
| 8 | This script attempts to emulate the contract of gtest-style tests |
| 9 | invoked via recipes. The main contract is that the caller passes the |
| 10 | argument: |
| 11 | |
| 12 | --isolated-script-test-output=[FILENAME] |
| 13 | |
Ashley Enstad | 8148f5f | 2017-08-02 03:08:57 | [diff] [blame] | 14 | json is written to that file in the format detailed here: |
| 15 | https://www.chromium.org/developers/the-json-test-results-format |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 16 | |
Kenneth Russell | 4027405 | 2017-11-14 00:57:44 | [diff] [blame] | 17 | Optional argument: |
| 18 | |
Kenneth Russell | a649a4612 | 2017-11-21 06:39:59 | [diff] [blame] | 19 | --isolated-script-test-filter=[TEST_NAMES] |
Kenneth Russell | 4027405 | 2017-11-14 00:57:44 | [diff] [blame] | 20 | |
Kenneth Russell | a649a4612 | 2017-11-21 06:39:59 | [diff] [blame] | 21 | is a double-colon-separated ("::") list of test names, to run just that subset |
| 22 | of tests. This list is parsed by this harness and sent down via the |
| 23 | --story-filter argument. |
Kenneth Russell | 4027405 | 2017-11-14 00:57:44 | [diff] [blame] | 24 | |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 25 | This script is intended to be the base command invoked by the isolate, |
| 26 | followed by a subsequent Python script. It could be generalized to |
| 27 | invoke an arbitrary executable. |
Kenneth Russell | a649a4612 | 2017-11-21 06:39:59 | [diff] [blame] | 28 | |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 29 | """ |
| 30 | |
| 31 | import argparse |
| 32 | import json |
| 33 | import os |
| 34 | import shutil |
| 35 | import sys |
| 36 | import tempfile |
| 37 | import traceback |
| 38 | |
| 39 | import common |
| 40 | |
| 41 | # Add src/testing/ into sys.path for importing xvfb. |
| 42 | sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
| 43 | import xvfb |
nednguyen | a8950e4 | 2018-05-18 22:42:06 | [diff] [blame] | 44 | import test_env |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 45 | |
kbr | 00ab61a | 2015-12-08 05:06:33 | [diff] [blame] | 46 | # Unfortunately we need to copy these variables from ../test_env.py. |
| 47 | # Importing it and using its get_sandbox_env breaks test runs on Linux |
| 48 | # (it seems to unset DISPLAY). |
| 49 | CHROME_SANDBOX_ENV = 'CHROME_DEVEL_SANDBOX' |
| 50 | CHROME_SANDBOX_PATH = '/opt/chromium/chrome_sandbox' |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 51 | |
| 52 | def main(): |
| 53 | parser = argparse.ArgumentParser() |
| 54 | parser.add_argument( |
| 55 | '--isolated-script-test-output', type=argparse.FileType('w'), |
| 56 | required=True) |
eyaich | 4e29701 | 2016-09-20 13:40:48 | [diff] [blame] | 57 | parser.add_argument( |
eyaich | da48d3aa | 2016-09-23 18:42:51 | [diff] [blame] | 58 | '--isolated-script-test-chartjson-output', required=False) |
Ethan Kuefner | 9d00951723 | 2017-08-14 20:56:42 | [diff] [blame] | 59 | parser.add_argument( |
| 60 | '--isolated-script-test-perf-output', required=False) |
Kenneth Russell | 4027405 | 2017-11-14 00:57:44 | [diff] [blame] | 61 | parser.add_argument( |
Kenneth Russell | a649a4612 | 2017-11-21 06:39:59 | [diff] [blame] | 62 | '--isolated-script-test-filter', type=str, required=False) |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 63 | parser.add_argument('--xvfb', help='Start xvfb.', action='store_true') |
Dave Tu | aabd0b3 | 2017-08-24 08:11:29 | [diff] [blame] | 64 | parser.add_argument('--output-format', action='append') |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 65 | args, rest_args = parser.parse_known_args() |
Dave Tu | aabd0b3 | 2017-08-24 08:11:29 | [diff] [blame] | 66 | for output_format in args.output_format: |
| 67 | rest_args.append('--output-format=' + output_format) |
Stephen Martinis | 0b18efa10 | 2017-09-25 22:55:12 | [diff] [blame] | 68 | |
nednguyen | a8950e4 | 2018-05-18 22:42:06 | [diff] [blame] | 69 | rc, perf_results, json_test_results, _ = run_benchmark(args, rest_args, |
Emily Hanley | 69ccba2 | 2018-02-09 20:15:25 | [diff] [blame] | 70 | 'histograms' in args.output_format) |
Stephen Martinis | 0b18efa10 | 2017-09-25 22:55:12 | [diff] [blame] | 71 | |
Ethan Kuefner | eaf80825 | 2017-09-26 21:13:20 | [diff] [blame] | 72 | if perf_results: |
Stephen Martinis | 0b18efa10 | 2017-09-25 22:55:12 | [diff] [blame] | 73 | if args.isolated_script_test_perf_output: |
| 74 | filename = args.isolated_script_test_perf_output |
| 75 | elif args.isolated_script_test_chartjson_output: |
| 76 | filename = args.isolated_script_test_chartjson_output |
| 77 | else: |
| 78 | filename = None |
| 79 | |
| 80 | if filename is not None: |
Ethan Kuefner | eaf80825 | 2017-09-26 21:13:20 | [diff] [blame] | 81 | with open(filename, 'w') as perf_results_output_file: |
| 82 | json.dump(perf_results, perf_results_output_file) |
Stephen Martinis | 0b18efa10 | 2017-09-25 22:55:12 | [diff] [blame] | 83 | |
| 84 | json.dump(json_test_results, args.isolated_script_test_output) |
| 85 | |
| 86 | return rc |
| 87 | |
Emily Hanley | 69ccba2 | 2018-02-09 20:15:25 | [diff] [blame] | 88 | def run_benchmark(args, rest_args, histogram_results): |
nednguyen | a8950e4 | 2018-05-18 22:42:06 | [diff] [blame] | 89 | """ Run benchmark with args. |
| 90 | |
| 91 | Args: |
| 92 | args: the option object resulted from parsing commandline args required for |
| 93 | IsolatedScriptTest contract (see |
| 94 | https://cs.chromium.org/chromium/build/scripts/slave/recipe_modules/chromium_tests/steps.py?rcl=d31f256fb860701e6dc02544f2beffe4e17c9b92&l=1639). |
| 95 | rest_args: the args (list of strings) for running Telemetry benchmark. |
| 96 | histogram_results: a boolean describes whether to output histograms format |
| 97 | for the benchmark. |
| 98 | |
| 99 | Returns: a tuple of (rc, perf_results, json_test_results, benchmark_log) |
| 100 | rc: the return code of benchmark |
| 101 | perf_results: json object contains the perf test results |
| 102 | json_test_results: json object contains the Pass/Fail data of the benchmark. |
| 103 | benchmark_log: string contains the stdout/stderr of the benchmark run. |
| 104 | """ |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 105 | env = os.environ.copy() |
Dirk Pranke | eb695f6 | 2017-12-18 23:16:31 | [diff] [blame] | 106 | env['CHROME_HEADLESS'] = '1' |
| 107 | |
kbr | 00ab61a | 2015-12-08 05:06:33 | [diff] [blame] | 108 | # Assume we want to set up the sandbox environment variables all the |
| 109 | # time; doing so is harmless on non-Linux platforms and is needed |
| 110 | # all the time on Linux. |
| 111 | env[CHROME_SANDBOX_ENV] = CHROME_SANDBOX_PATH |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 112 | tempfile_dir = tempfile.mkdtemp('telemetry') |
nednguyen | a8950e4 | 2018-05-18 22:42:06 | [diff] [blame] | 113 | benchmark_log = '' |
| 114 | stdoutfile = os.path.join(tempfile_dir, 'benchmark_log.txt') |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 115 | valid = True |
Ashley Enstad | 8148f5f | 2017-08-02 03:08:57 | [diff] [blame] | 116 | num_failures = 0 |
Ethan Kuefner | eaf80825 | 2017-09-26 21:13:20 | [diff] [blame] | 117 | perf_results = None |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 118 | json_test_results = None |
| 119 | |
| 120 | results = None |
Kenneth Russell | 4027405 | 2017-11-14 00:57:44 | [diff] [blame] | 121 | cmd_args = rest_args |
Kenneth Russell | a649a4612 | 2017-11-21 06:39:59 | [diff] [blame] | 122 | if args.isolated_script_test_filter: |
| 123 | filter_list = common.extract_filter_list(args.isolated_script_test_filter) |
Kenneth Russell | 4027405 | 2017-11-14 00:57:44 | [diff] [blame] | 124 | # Need to convert this to a valid regex. |
| 125 | filter_regex = '(' + '|'.join(filter_list) + ')' |
| 126 | cmd_args = cmd_args + [ |
| 127 | '--story-filter=' + filter_regex |
| 128 | ] |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 129 | try: |
Kenneth Russell | 4027405 | 2017-11-14 00:57:44 | [diff] [blame] | 130 | cmd = [sys.executable] + cmd_args + [ |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 131 | '--output-dir', tempfile_dir, |
| 132 | '--output-format=json-test-results', |
| 133 | ] |
| 134 | if args.xvfb: |
nednguyen | a8950e4 | 2018-05-18 22:42:06 | [diff] [blame] | 135 | rc = xvfb.run_executable(cmd, env=env, stdoutfile=stdoutfile) |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 136 | else: |
nednguyen | a8950e4 | 2018-05-18 22:42:06 | [diff] [blame] | 137 | rc = test_env.run_command_with_output(cmd, env=env, stdoutfile=stdoutfile) |
| 138 | |
| 139 | with open(stdoutfile) as f: |
| 140 | benchmark_log = f.read() |
martiniss | be66fed | 2017-04-04 01:22:01 | [diff] [blame] | 141 | |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 142 | # If we have also output chartjson read it in and return it. |
| 143 | # results-chart.json is the file name output by telemetry when the |
| 144 | # chartjson output format is included |
Emily Hanley | 69ccba2 | 2018-02-09 20:15:25 | [diff] [blame] | 145 | tempfile_name = None |
| 146 | if histogram_results: |
Ethan Kuefner | eaf80825 | 2017-09-26 21:13:20 | [diff] [blame] | 147 | tempfile_name = os.path.join(tempfile_dir, 'histograms.json') |
Ethan Kuefner | eaf80825 | 2017-09-26 21:13:20 | [diff] [blame] | 148 | else: |
Emily Hanley | 69ccba2 | 2018-02-09 20:15:25 | [diff] [blame] | 149 | tempfile_name = os.path.join(tempfile_dir, 'results-chart.json') |
Ethan Kuefner | eaf80825 | 2017-09-26 21:13:20 | [diff] [blame] | 150 | |
| 151 | if tempfile_name is not None: |
| 152 | with open(tempfile_name) as f: |
| 153 | perf_results = json.load(f) |
ashleymarie | 1b0335db | 2017-07-12 16:14:01 | [diff] [blame] | 154 | |
Ashley Enstad | 81d004bf3 | 2017-08-07 15:52:57 | [diff] [blame] | 155 | # test-results.json is the file name output by telemetry when the |
| 156 | # json-test-results format is included |
| 157 | tempfile_name = os.path.join(tempfile_dir, 'test-results.json') |
| 158 | with open(tempfile_name) as f: |
| 159 | json_test_results = json.load(f) |
| 160 | num_failures = json_test_results['num_failures_by_type'].get('FAIL', 0) |
| 161 | valid = bool(rc == 0 or num_failures != 0) |
Ashley Enstad | 8148f5f | 2017-08-02 03:08:57 | [diff] [blame] | 162 | |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 163 | except Exception: |
| 164 | traceback.print_exc() |
| 165 | if results: |
| 166 | print 'results, which possibly caused exception: %s' % json.dumps( |
| 167 | results, indent=2) |
| 168 | valid = False |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 169 | finally: |
Ned Nguyen | afc2ba2 | 2018-07-20 14:39:53 | [diff] [blame] | 170 | # Add ignore_errors=True because otherwise rmtree may fail due to leaky |
| 171 | # processes of tests are still holding opened handles to files under |
| 172 | # |tempfile_dir|. For example, see crbug.com/865896 |
| 173 | shutil.rmtree(tempfile_dir, ignore_errors=True) |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 174 | |
Ashley Enstad | 8148f5f | 2017-08-02 03:08:57 | [diff] [blame] | 175 | if not valid and num_failures == 0: |
Yuzhu Shen | 0fdf020 | 2017-08-01 20:14:01 | [diff] [blame] | 176 | if rc == 0: |
| 177 | rc = 1 # Signal an abnormal exit. |
| 178 | |
nednguyen | a8950e4 | 2018-05-18 22:42:06 | [diff] [blame] | 179 | return rc, perf_results, json_test_results, benchmark_log |
kbr | 213b4f2 | 2015-11-26 00:49:28 | [diff] [blame] | 180 | |
| 181 | |
| 182 | # This is not really a "script test" so does not need to manually add |
| 183 | # any additional compile targets. |
| 184 | def main_compile_targets(args): |
| 185 | json.dump([], args.output) |
| 186 | |
| 187 | |
| 188 | if __name__ == '__main__': |
| 189 | # Conform minimally to the protocol defined by ScriptTest. |
| 190 | if 'compile_targets' in sys.argv: |
| 191 | funcs = { |
| 192 | 'run': None, |
| 193 | 'compile_targets': main_compile_targets, |
| 194 | } |
| 195 | sys.exit(common.run_script(sys.argv[1:], funcs)) |
| 196 | sys.exit(main()) |