Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/maint/rebaseline_tests.py
4150 views
1
#!/usr/bin/env python3
2
# Copyright 2024 The Emscripten Authors. All rights reserved.
3
# Emscripten is available under two separate licenses, the MIT license and the
4
# University of Illinois/NCSA Open Source License. Both these licenses can be
5
# found in the LICENSE file.
6
7
"""Automatically rebaseline tests that have codesize expectations and create
8
a git commit containing the resulting changes along with readable details of
9
the generated changes.
10
"""
11
12
import argparse
13
import json
14
import os
15
import subprocess
16
import statistics
17
import sys
18
19
script_dir = os.path.dirname(os.path.abspath(__file__))
20
root_dir = os.path.dirname(os.path.dirname(script_dir))
21
22
sys.path.insert(0, root_dir)
23
from tools import utils, shared
24
25
TESTS = [
26
'other.test_small_js_flags',
27
'other.*code_size*',
28
'other.*codesize*',
29
'skip:other.test_jspi_code_size',
30
]
31
32
33
def run(cmd, **args):
34
return subprocess.check_output(cmd, text=True, cwd=root_dir, **args)
35
36
37
all_deltas = []
38
39
40
def read_size_from_json(content):
41
json_data = json.loads(content)
42
if 'total' in json_data:
43
return json_data['total']
44
# If `total` if not in the json dict then just use the first key. This happens when only one
45
# file size is reported (in this case we don't calculate or store the `total`).
46
first_key = list(json_data.keys())[0]
47
return json_data[first_key]
48
49
50
def process_changed_file(filename):
51
content = open(filename).read()
52
old_content = run(['git', 'show', f'HEAD:{filename}'])
53
print(f'processing {filename}')
54
55
ext = os.path.splitext(filename)[1]
56
if ext == '.size':
57
size = int(content.strip())
58
old_size = int(old_content.strip())
59
elif ext == '.json':
60
size = read_size_from_json(content)
61
old_size = read_size_from_json(old_content)
62
else:
63
# Unhandled file type
64
return f'{filename} updated\n'
65
66
filename = utils.removeprefix(filename, 'test/')
67
delta = size - old_size
68
percent_delta = delta * 100 / old_size
69
all_deltas.append(percent_delta)
70
return f'{filename}: {old_size} => {size} [{delta:+} bytes / {percent_delta:+.2f}%]\n'
71
72
73
def main():
74
parser = argparse.ArgumentParser()
75
parser.add_argument('-s', '--skip-tests', action='store_true', help="Don't actually run the tests, just analyze the existing results")
76
parser.add_argument('-b', '--new-branch', action='store_true', help='Create a new branch containing the updates')
77
parser.add_argument('-c', '--clear-cache', action='store_true', help='Clear the cache before rebaselining (useful when working with llvm changes)')
78
parser.add_argument('-n', '--check-only', dest='check_only', action='store_true', help='Return non-zero if test expectations are out of date, and skip creating a git commit')
79
args = parser.parse_args()
80
81
if args.clear_cache:
82
run(['./emcc', '--clear-cache'])
83
84
if not args.skip_tests:
85
if not args.check_only and run(['git', 'status', '-uno', '--porcelain']).strip():
86
print('tree is not clean')
87
return 1
88
89
subprocess.check_call([shared.bat_suffix(os.path.join('test', 'runner')), '--rebaseline', '--browser=0'] + TESTS, cwd=root_dir)
90
91
output = run(['git', 'status', '-uno', '--porcelain'])
92
filenames = []
93
for line in output.splitlines():
94
filename = line.strip().rsplit(' ', 1)[1]
95
if filename.startswith('test') and os.path.isfile(filename):
96
filenames.append(filename)
97
98
if not filenames:
99
print('test expectations are up-to-date')
100
return 0
101
102
if args.check_only:
103
message = f'''Test expectations are out-of-date
104
105
The following ({len(filenames)}) test expectation files were updated by
106
running the tests with `--rebaseline`:
107
108
```
109
'''
110
else:
111
message = f'''
112
Automatic rebaseline of codesize expectations. NFC
113
114
This is an automatic change generated by tools/maint/rebaseline_tests.py.
115
116
The following ({len(filenames)}) test expectation files were updated by
117
running the tests with `--rebaseline`:
118
119
```
120
'''
121
122
for file in filenames:
123
message += process_changed_file(file)
124
125
if all_deltas:
126
message += f'\nAverage change: {statistics.mean(all_deltas):+.2f}% ({min(all_deltas):+.2f}% - {max(all_deltas):+.2f}%)\n'
127
128
message += '```\n'
129
130
print(message)
131
if args.check_only:
132
return 1
133
134
if args.new_branch:
135
run(['git', 'checkout', '-b', 'rebaseline_tests'])
136
run(['git', 'add', '-u', '.'])
137
run(['git', 'commit', '-F', '-'], input=message)
138
139
return 2
140
141
142
if __name__ == '__main__':
143
sys.exit(main())
144
145