xref: /aosp_15_r20/external/angle/infra/specs/generate_test_spec_json.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#!/usr/bin/env vpython3
2# Copyright 2021 The ANGLE Project 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"""Script to generate the test spec JSON files. Calls Chromium's generate_buildbot_json.
6
7=== NOTE: DO NOT RUN THIS SCRIPT DIRECTLY. ===
8Run scripts/run_code_generation.py instead to update necessary hashes.
9
10"""
11
12import os
13import pprint
14import sys
15import subprocess
16import tempfile
17
18d = os.path.dirname
19THIS_DIR = d(os.path.abspath(__file__))
20TESTING_BBOT_DIR = os.path.join(d(d(THIS_DIR)), 'testing', 'buildbot')
21sys.path.insert(0, TESTING_BBOT_DIR)
22
23import generate_buildbot_json
24
25# Add custom mixins here.
26ADDITIONAL_MIXINS = {
27    'angle_skia_gold_test': {
28        'args': [
29            '--git-revision=${got_angle_revision}',
30            # BREAK GLASS IN CASE OF EMERGENCY
31            # Uncommenting this argument will bypass all interactions with Skia
32            # Gold in any tests that use it. This is meant as a temporary
33            # emergency stop in case of a Gold outage that's affecting the bots.
34            # '--bypass-skia-gold-functionality',
35        ],
36        'precommit_args': [
37            '--gerrit-issue=${patch_issue}',
38            '--gerrit-patchset=${patch_set}',
39            '--buildbucket-id=${buildbucket_build_id}',
40            # This normally evaluates to "0", but will evaluate to "1" if
41            # "Use-Permissive-Angle-Pixel-Comparison: True" is present as a
42            # CL footer.
43            '--use-permissive-pixel-comparison=${use_permissive_angle_pixel_comparison}',
44        ],
45    },
46    'samsung_s22': {
47        'swarming': {
48            'dimensions': {
49                'device_os': 'UP1A.231005.007',
50                'device_os_type': 'user',
51                'device_type': 's5e9925',
52                'os': 'Android'
53            }
54        }
55    },
56    'timeout_120m': {
57        'swarming': {
58            'hard_timeout': 7200,
59            'io_timeout': 300
60        }
61    },
62    'temp_band_below_30C': {
63        'swarming': {
64            'dimensions': {
65                'temp_band': '<30'
66            }
67        }
68    },
69}
70
71MIXIN_FILE_NAME = os.path.join(THIS_DIR, 'mixins.pyl')
72MIXINS_PYL_TEMPLATE = """\
73# GENERATED FILE - DO NOT EDIT.
74# Generated by {script_name} using data from {data_source}
75#
76# Copyright 2021 The ANGLE Project Authors. All rights reserved.
77# Use of this source code is governed by a BSD-style license that can be
78# found in the LICENSE file.
79#
80# This is a .pyl, or "Python Literal", file. You can treat it just like a
81# .json file, with the following exceptions:
82# * all keys must be quoted (use single quotes, please);
83# * comments are allowed, using '#' syntax; and
84# * trailing commas are allowed.
85#
86# For more info see Chromium's mixins.pyl in testing/buildbot.
87
88{mixin_data}
89"""
90
91
92def main():
93    if len(sys.argv) > 1:
94        gen_bb_json = os.path.join(TESTING_BBOT_DIR, 'generate_buildbot_json.py')
95        mixins_pyl = os.path.join(TESTING_BBOT_DIR, 'mixins.pyl')
96        inputs = [
97            'test_suite_exceptions.pyl', 'test_suites.pyl', 'variants.pyl', 'waterfalls.pyl',
98            gen_bb_json, mixins_pyl
99        ]
100        outputs = ['angle.json', 'mixins.pyl']
101        if sys.argv[1] == 'inputs':
102            print(','.join(inputs))
103            return 0
104        if sys.argv[1] == 'outputs':
105            print(','.join(outputs))
106            return 0
107
108    # --verify-only enables dirty checks without relying on checked in hashes.
109    # Compares the content of the existing file with the generated content.
110    verify_only = '--verify-only' in sys.argv
111
112    if verify_only:
113        with tempfile.TemporaryDirectory() as temp_dir:
114            return run_generator(verify_only, temp_dir)
115    else:
116        return run_generator(verify_only, None)
117
118
119def write_or_verify_file(filename, content, verify_only):
120    if verify_only:
121        try:
122            with open(filename) as f:
123                # Note: .gitattributes "* text=auto" handles LF <-> CRLF on Windows
124                return f.read() == content
125        except FileNotFoundError:
126            return False
127    else:
128        with open(filename, 'w') as fout:
129            fout.write(content)
130            return True
131
132
133def run_generator(verify_only, temp_dir):
134    override_args = ['--pyl-files-dir', THIS_DIR]
135    if verify_only:
136        override_args += ['--output-dir', temp_dir]
137    args = generate_buildbot_json.BBJSONGenerator.parse_args(override_args)
138    generator = generate_buildbot_json.BBJSONGenerator(args)
139    generator.load_configuration_files()
140    generator.resolve_configuration_files()
141
142    chromium_mixins = generator.load_pyl_file(os.path.join(TESTING_BBOT_DIR, 'mixins.pyl'))
143
144    seen_mixins = set()
145    for waterfall in generator.waterfalls:
146        seen_mixins = seen_mixins.union(waterfall.get('mixins', set()))
147        for bot_name, tester in waterfall['machines'].items():
148            seen_mixins = seen_mixins.union(tester.get('mixins', set()))
149    for suite in generator.test_suites.values():
150        if isinstance(suite, list):
151            # Don't care about this, it's a composition, which shouldn't include a
152            # swarming mixin.
153            continue
154        for test in suite.values():
155            assert isinstance(test, dict)
156            seen_mixins = seen_mixins.union(test.get('mixins', set()))
157
158    found_mixins = ADDITIONAL_MIXINS.copy()
159    for mixin in seen_mixins:
160        if mixin in found_mixins:
161            continue
162        assert (mixin in chromium_mixins), 'Error with %s mixin' % mixin
163        found_mixins[mixin] = chromium_mixins[mixin]
164
165    pp = pprint.PrettyPrinter(indent=2)
166
167    format_data = {
168        'script_name': os.path.basename(__file__),
169        'data_source': 'waterfall.pyl and Chromium\'s mixins.pyl',
170        'mixin_data': pp.pformat(found_mixins),
171    }
172    generated_mixin_pyl = MIXINS_PYL_TEMPLATE.format(**format_data)
173
174    if not write_or_verify_file(MIXIN_FILE_NAME, generated_mixin_pyl, verify_only):
175        print('infra/specs/mixins.pyl dirty')
176        return 1
177
178    if generator.main() != 0:
179        print('buildbot (pyl to json) generation failed')
180        return 1
181
182    if verify_only:
183        for waterfall in generator.waterfalls:
184            filename = waterfall['name'] + '.json'  # angle.json, might have more in future
185            with open(os.path.join(temp_dir, filename)) as f:
186                content = f.read()
187            angle_filename = os.path.join(THIS_DIR, filename)
188            if not write_or_verify_file(angle_filename, content, True):
189                print('infra/specs/%s dirty' % filename)
190                return 1
191
192    return 0
193
194
195if __name__ == '__main__':  # pragma: no cover
196    sys.exit(main())
197