blob: 984c398943817d7add973b19e736cc70d190c008 [file] [log] [blame]
#!/usr/bin/env vpython3
# Copyright 2021 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Script to generate the test spec JSON files. Calls Chromium's generate_buildbot_json.
=== NOTE: DO NOT RUN THIS SCRIPT DIRECTLY. ===
Run scripts/run_code_generation.py instead to update necessary hashes.
"""
import os
import pprint
import sys
import subprocess
import tempfile
d = os.path.dirname
THIS_DIR = d(os.path.abspath(__file__))
TESTING_BBOT_DIR = os.path.join(d(d(THIS_DIR)), 'testing', 'buildbot')
sys.path.insert(0, TESTING_BBOT_DIR)
import generate_buildbot_json
# Add custom mixins here.
ADDITIONAL_MIXINS = {
'angle_skia_gold_test': {
'args': [
'--git-revision=${got_angle_revision}',
# BREAK GLASS IN CASE OF EMERGENCY
# Uncommenting this argument will bypass all interactions with Skia
# Gold in any tests that use it. This is meant as a temporary
# emergency stop in case of a Gold outage that's affecting the bots.
# '--bypass-skia-gold-functionality',
],
'precommit_args': [
'--gerrit-issue=${patch_issue}',
'--gerrit-patchset=${patch_set}',
'--buildbucket-id=${buildbucket_build_id}',
# This normally evaluates to "0", but will evaluate to "1" if
# "Use-Permissive-Angle-Pixel-Comparison: True" is present as a
# CL footer.
'--use-permissive-pixel-comparison=${use_permissive_angle_pixel_comparison}',
],
},
'samsung_s22': {
'swarming': {
'dimensions': {
'device_os': 'UP1A.231005.007',
'device_os_type': 'user',
'device_type': 's5e9925',
'os': 'Android'
}
}
},
'timeout_120m': {
'swarming': {
'hard_timeout': 7200,
'io_timeout': 300
}
},
'temp_band_below_30C': {
'swarming': {
'dimensions': {
'temp_band': '<30'
}
}
},
}
MIXIN_FILE_NAME = os.path.join(THIS_DIR, 'mixins.pyl')
MIXINS_PYL_TEMPLATE = """\
# GENERATED FILE - DO NOT EDIT.
# Generated by {script_name} using data from {data_source}
#
# Copyright 2021 The ANGLE Project Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This is a .pyl, or "Python Literal", file. You can treat it just like a
# .json file, with the following exceptions:
# * all keys must be quoted (use single quotes, please);
# * comments are allowed, using '#' syntax; and
# * trailing commas are allowed.
#
# For more info see Chromium's mixins.pyl in testing/buildbot.
{mixin_data}
"""
def main():
if len(sys.argv) > 1:
gen_bb_json = os.path.join(TESTING_BBOT_DIR, 'generate_buildbot_json.py')
mixins_pyl = os.path.join(TESTING_BBOT_DIR, 'mixins.pyl')
inputs = [
'test_suite_exceptions.pyl', 'test_suites.pyl', 'variants.pyl', 'waterfalls.pyl',
gen_bb_json, mixins_pyl
]
outputs = ['angle.json', 'mixins.pyl']
if sys.argv[1] == 'inputs':
print(','.join(inputs))
return 0
if sys.argv[1] == 'outputs':
print(','.join(outputs))
return 0
# --verify-only enables dirty checks without relying on checked in hashes.
# Compares the content of the existing file with the generated content.
verify_only = '--verify-only' in sys.argv
if verify_only:
with tempfile.TemporaryDirectory() as temp_dir:
return run_generator(verify_only, temp_dir)
else:
return run_generator(verify_only, None)
def write_or_verify_file(filename, content, verify_only):
if verify_only:
try:
with open(filename) as f:
# Note: .gitattributes "* text=auto" handles LF <-> CRLF on Windows
return f.read() == content
except FileNotFoundError:
return False
else:
with open(filename, 'w') as fout:
fout.write(content)
return True
def run_generator(verify_only, temp_dir):
override_args = ['--pyl-files-dir', THIS_DIR]
if verify_only:
override_args += ['--output-dir', temp_dir]
args = generate_buildbot_json.BBJSONGenerator.parse_args(override_args)
generator = generate_buildbot_json.BBJSONGenerator(args)
generator.load_configuration_files()
generator.resolve_configuration_files()
chromium_mixins = generator.load_pyl_file(os.path.join(TESTING_BBOT_DIR, 'mixins.pyl'))
seen_mixins = set()
for waterfall in generator.waterfalls:
seen_mixins = seen_mixins.union(waterfall.get('mixins', set()))
for bot_name, tester in waterfall['machines'].items():
seen_mixins = seen_mixins.union(tester.get('mixins', set()))
for suite in generator.test_suites.values():
if isinstance(suite, list):
# Don't care about this, it's a composition, which shouldn't include a
# swarming mixin.
continue
for test in suite.values():
assert isinstance(test, dict)
seen_mixins = seen_mixins.union(test.get('mixins', set()))
found_mixins = ADDITIONAL_MIXINS.copy()
for mixin in seen_mixins:
if mixin in found_mixins:
continue
assert (mixin in chromium_mixins), 'Error with %s mixin' % mixin
found_mixins[mixin] = chromium_mixins[mixin]
pp = pprint.PrettyPrinter(indent=2)
format_data = {
'script_name': os.path.basename(__file__),
'data_source': 'waterfall.pyl and Chromium\'s mixins.pyl',
'mixin_data': pp.pformat(found_mixins),
}
generated_mixin_pyl = MIXINS_PYL_TEMPLATE.format(**format_data)
if not write_or_verify_file(MIXIN_FILE_NAME, generated_mixin_pyl, verify_only):
print('infra/specs/mixins.pyl dirty')
return 1
if generator.main() != 0:
print('buildbot (pyl to json) generation failed')
return 1
if verify_only:
for waterfall in generator.waterfalls:
filename = waterfall['name'] + '.json' # angle.json, might have more in future
with open(os.path.join(temp_dir, filename)) as f:
content = f.read()
angle_filename = os.path.join(THIS_DIR, filename)
if not write_or_verify_file(angle_filename, content, True):
print('infra/specs/%s dirty' % filename)
return 1
return 0
if __name__ == '__main__': # pragma: no cover
sys.exit(main())