blob: 966c8cb54f7629aa27ed5c133289038013fe8387 [file]
#!/usr/bin/env vpython3
#
# Copyright 2023 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import json
import os
import subprocess
import sys
import tempfile
import time
from e2e_divider import divide_run
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import devtools_paths
def parse_options(cli_args):
parser = argparse.ArgumentParser(description='Run tests')
parser.add_argument(
'--test-suite-source-dir',
dest='test_suite_source_dir',
default='test/e2e',
help=
'Path to the source folder containing the tests, relative to the current working directory.'
)
parser.add_argument(
'--jobs',
dest='jobs',
default=4,
help='Number of parallel runners to use (if supported). Defaults to 1.'
)
parser.add_argument(
'--test-file-pattern',
dest='test_file_pattern',
default=None,
help=
'A comma separated glob (or just a file path) to select specific test files to execute.'
)
parser.add_argument('--iterations',
dest='iterations',
default=1,
help='Number of test iterations.')
parser.add_argument('--no-color',
dest='no_color',
action='store_true',
help='Prints results without color.')
parser.add_argument('--no-indent',
dest='no_indent',
action='store_true',
help='Prints results without indentation.')
return parser.parse_args(cli_args)
class ColoredPrint:
def __init__(self, no_color):
self.color_codes = {
'no_color': '0',
'red': '0;31',
'green': '0;32',
'yellow': '1;33',
'blue': '0;34',
'purple': '0;35',
'gray': '0;37',
'white': '1;37',
}
self.no_color = no_color
if os.popen('tput colors').read().strip() == '256':
self.supports_color = True
else:
self.supports_color = False
def cprint(self, text, color='no_color', end='\n'):
if self.supports_color and not self.no_color:
print(f'\033[{self.color_codes[color]}m {text} \033[0m', end=end)
else:
print(text, end=end)
def merge_files(file_path_list, new_file_path):
combined_file = open(new_file_path, 'w')
for file_path in file_path_list:
file = open(file_path, 'r')
file_content = file.read()
combined_file.write(file_content)
file.close()
os.remove(file_path)
combined_file.close()
return combined_file
if __name__ == '__main__':
args = parse_options(sys.argv[1:])
commands = divide_run(chunks=int(args.jobs),
test_suite_source_dir=args.test_suite_source_dir,
pattern=args.test_file_pattern,
iterations=int(args.iterations))
env = os.environ.copy()
results_log_files = []
processes = []
for i in range(len(commands)):
for k, v in commands[i]['env'].items():
env[k] = v
temp = tempfile.NamedTemporaryFile(delete=False)
open(temp.name, 'a')
results_log_files.append(temp)
processes.append(
subprocess.Popen(commands[i]['command'] +
['--mocha-reporter', 'json-stream'],
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT))
cprint = ColoredPrint(args.no_color).cprint
start_time = time.time()
while None in [p.poll() for p in processes]:
for i in range(len(processes)):
line = processes[i].stdout.readline()
if line:
results_log_files[i].write(line)
decoded_line = line.decode("utf-8")
try:
json_line = json.loads(decoded_line)
formatted_line = json_line
if not args.no_indent:
formatted_line = json.dumps(json_line, indent=2)
if 'pass' in json_line:
cprint(formatted_line, color='green')
elif 'fail' in json_line:
cprint(formatted_line, color='red')
elif 'end' in json_line:
cprint(formatted_line, color='yellow')
else:
cprint(json_line)
except ValueError as e:
cprint(decoded_line, end='')
for f in results_log_files:
f.close()
failed_tests = []
tests, passes, pending, failures = 0, 0, 0, 0
for i in range(len(commands)):
with open(results_log_files[i].name, "r") as f:
for l in f.readlines():
try:
json_line = json.loads(l)
if 'fail' in json_line:
failed_tests.append(json.loads(l)[1])
if 'end' in json_line:
result = json.loads(l)[1]
tests += result['tests']
passes += result['passes']
pending += result['pending']
failures += result['failures']
except:
pass
for tf in results_log_files:
tf.close()
print('\n\nFailed tests:')
for f in failed_tests:
print(json.dumps(f, indent=2))
print()
print()
cprint(f'Total tests: {tests}', 'yellow')
cprint(f'Passed: {passes}', 'green')
cprint(f'Pending: {pending}', 'blue')
cprint(f'Failures: {failures}', 'red')
print()
if not args.no_failure_screenshots_file and failed_tests:
merge_files([
f'{devtools_paths.devtools_root_path()}/out/failure_screenshots_{i}.html'
for i in range(len(commands))
], f'{devtools_paths.devtools_root_path()}/out/failure_screenshots.html'
)
cprint(
f'Failure screenshots: {devtools_paths.devtools_root_path()}/out/failure_screenshots.html',
'white')
print()
cprint(
'Run Time: ' + str(round(
(time.time() - start_time) / 60, 2)) + ' minutes', 'white')