| #!/usr/bin/env vpython3 | 
 | # Copyright 2017 The Chromium Authors | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | """Tests for generate_buildbot_json.py.""" | 
 |  | 
 | import contextlib | 
 | import json | 
 | import os | 
 | import re | 
 | import unittest | 
 |  | 
 | # vpython-provided modules. | 
 | from pyfakefs import fake_filesystem_unittest  # pylint: disable=import-error | 
 |  | 
 | # //testing/buildbot imports. | 
 | import generate_buildbot_json | 
 |  | 
 | # pylint: disable=super-with-arguments | 
 |  | 
 | EMPTY_PYL_FILE = """\ | 
 | { | 
 | } | 
 | """ | 
 |  | 
 | # Use this value to refer to the directory containing this code | 
 | # The tests use a fake filesystem and python filesystem calls are monkey-patched | 
 | # to use the fake filesystem, which affects abspath | 
 | THIS_DIR = os.path.dirname(os.path.abspath(__file__)) | 
 |  | 
 |  | 
 | class TestCase(fake_filesystem_unittest.TestCase): | 
 |   def setUp(self): | 
 |     self.setUpPyfakefs() | 
 |     self.fs.cwd = THIS_DIR | 
 |  | 
 |     expectations_dir = os.path.join(THIS_DIR, 'unittest_expectations') | 
 |     self.output_dir = os.path.join(expectations_dir, self.id().split('.')[-1]) | 
 |  | 
 |     # This allows reads into this directory to fallback to the real fs, which we | 
 |     # want so each test case's json can be checked-in. | 
 |     self.fs.add_real_directory(expectations_dir) | 
 |  | 
 |     self.set_args() | 
 |  | 
 |   def set_args(self, *args): | 
 |     self.args = generate_buildbot_json.BBJSONGenerator.parse_args(( | 
 |         '--output-dir', | 
 |         self.output_dir, | 
 |     ) + args) | 
 |  | 
 |   def regen_test_json(self, fakebb): | 
 |     """Regenerates a unittest's json files. | 
 |  | 
 |     Useful when making sweeping changes to pyl inputs in many test cases. Can be | 
 |     used by simply inserting a call to this method in a unit test after | 
 |     initializing its FakeBBGen object. | 
 |     """ | 
 |     contents = fakebb.generate_outputs() | 
 |     with fake_filesystem_unittest.Pause(self.fs): | 
 |       try: | 
 |         os.mkdir(self.output_dir) | 
 |       except FileExistsError: | 
 |         if not os.path.isdir(self.output_dir): | 
 |           raise | 
 |  | 
 |       fakebb.write_json_result(contents) | 
 |  | 
 |  | 
 | @contextlib.contextmanager | 
 | def dump_on_failure(fbb, dump=True): | 
 |   try: | 
 |     yield | 
 |   except: | 
 |     if dump: | 
 |       for l in fbb.printed_lines: | 
 |         print(l) | 
 |     raise | 
 |  | 
 | class FakeBBGen(generate_buildbot_json.BBJSONGenerator): | 
 |   def __init__(self, | 
 |                args, | 
 |                waterfalls, | 
 |                test_suites, | 
 |                luci_milo_cfg, | 
 |                project_pyl='{"validate_source_side_specs_have_builder": True}', | 
 |                exceptions=EMPTY_PYL_FILE, | 
 |                autoshard_exceptions=json.dumps({}), | 
 |                mixins=EMPTY_PYL_FILE, | 
 |                gn_isolate_map=EMPTY_PYL_FILE, | 
 |                variants=EMPTY_PYL_FILE, | 
 |                use_legacy_pyl_paths=False): | 
 |     super(FakeBBGen, self).__init__(args) | 
 |  | 
 |     pyl_files_dir = args.pyl_files_dir or THIS_DIR | 
 |     infra_config_dir = args.infra_config_dir | 
 |  | 
 |     if use_legacy_pyl_paths: | 
 |       test_suites_pyl_path = args.test_suites_pyl.legacy_path | 
 |       mixins_pyl_path = args.mixins_pyl.legacy_path | 
 |       gn_isolate_map_pyl_path = args.gn_isolate_map_pyl.legacy_path | 
 |       variants_pyl_path = args.variants_pyl.legacy_path | 
 |     else: | 
 |       test_suites_pyl_path = args.test_suites_pyl.generated_path | 
 |       mixins_pyl_path = args.mixins_pyl.generated_path | 
 |       gn_isolate_map_pyl_path = args.gn_isolate_map_pyl.generated_path | 
 |       variants_pyl_path = args.variants_pyl.generated_path | 
 |  | 
 |     files = { | 
 |         args.waterfalls_pyl_path: waterfalls, | 
 |         test_suites_pyl_path: test_suites, | 
 |         args.test_suite_exceptions_pyl_path: exceptions, | 
 |         args.autoshard_exceptions_json_path: autoshard_exceptions, | 
 |         mixins_pyl_path: mixins, | 
 |         gn_isolate_map_pyl_path: gn_isolate_map, | 
 |         variants_pyl_path: variants, | 
 |         os.path.join(pyl_files_dir, 'gn_isolate_map2.pyl'): | 
 |         GPU_TELEMETRY_GN_ISOLATE_MAP, | 
 |         os.path.join(infra_config_dir, 'generated/project.pyl'): project_pyl, | 
 |         os.path.join(infra_config_dir, 'generated/luci/luci-milo.cfg'): | 
 |         luci_milo_cfg, | 
 |         os.path.join(infra_config_dir, 'generated/luci/luci-milo-dev.cfg'): '', | 
 |     } | 
 |     for path, content in files.items(): | 
 |       if content is None: | 
 |         continue | 
 |       parent = os.path.abspath(os.path.dirname(path)) | 
 |       if not os.path.exists(parent): | 
 |         os.makedirs(parent) | 
 |       with open(path, 'w') as f: | 
 |         f.write(content) | 
 |  | 
 |     self.printed_lines = [] | 
 |  | 
 |   def print_line(self, line): | 
 |     self.printed_lines.append(line) | 
 |  | 
 |   # pragma pylint: disable=arguments-differ | 
 |   def check_output_file_consistency(self, verbose=False, dump=True): | 
 |     with dump_on_failure(self, dump=verbose and dump): | 
 |       super(FakeBBGen, self).check_output_file_consistency(verbose) | 
 |   # pragma pylint: enable=arguments-differ | 
 |  | 
 |  | 
 | FOO_GTESTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'dimensions':{ | 
 |             'kvm': '1', | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GTESTS_WITH_ENABLE_FEATURES_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |         'args': [ | 
 |           '--enable-features=Baz', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_CHROMEOS_TRIGGER_SCRIPT_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             "device_type": "foo_device", | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |         'os_type': 'chromeos', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_LINUX_GTESTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'linux', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | COMPOSITION_GTEST_SUITE_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | COMPOSITION_GTEST_SUITE_WITH_ARGS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'composition_tests', | 
 |         }, | 
 |         'args': [ | 
 |           '--this-is-an-argument', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_ISOLATED_SCRIPTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'isolated_scripts': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_ISOLATED_SCRIPTS_WATERFALL_ANDROID = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'android', | 
 |         'test_suites': { | 
 |           'isolated_scripts': 'composition_tests', | 
 |         }, | 
 |         'use_android_presentation': True, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_SCRIPT_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'scripts': 'foo_scripts', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_SCRIPT_WATERFALL_MACHINE_FORBIDS_SCRIPT_TESTS = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'forbid_script_tests': True, | 
 |         'test_suites': { | 
 |           'scripts': 'foo_scripts', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_SCRIPT_WATERFALL_FORBID_SCRIPT_TESTS = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'forbid_script_tests': True, | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'scripts': 'foo_scripts', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_JUNIT_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'junit_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GPU_TELEMETRY_TEST_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'win', | 
 |         'browser_config': 'release', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'gpu': '10de:1cb3', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GPU_TELEMETRY_TEST_WATERFALL_ANDROID = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'android', | 
 |         'browser_config': 'android-chromium', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'device_type': 'bullhead', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GPU_TELEMETRY_TEST_WATERFALL_ANDROID_WEBVIEW = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'android', | 
 |         'browser_config': 'not-a-real-browser', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'device_type': 'bullhead', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'android_webview_gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GPU_TELEMETRY_TEST_WATERFALL_FUCHSIA = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'fuchsia', | 
 |         'browser_config': 'fuchsia-chrome', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'kvm': '1', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GPU_TELEMETRY_TEST_WATERFALL_CAST_STREAMING = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'fuchsia', | 
 |         'browser_config': 'not-a-real-browser', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'kvm': '1', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'cast_streaming_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GPU_TELEMETRY_TEST_WATERFALL_SKYLAB = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'chromeos', | 
 |         'browser_config': 'cros-chrome', | 
 |         'use_swarming': False, | 
 |         'swarming': { | 
 |           'some_key': 'some_value', | 
 |         }, | 
 |         'test_suites': { | 
 |           'skylab_gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | NVIDIA_GPU_TELEMETRY_TEST_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'win', | 
 |         'browser_config': 'release', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'gpu': '10de:1cb3-26.21.14.3102', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | INTEL_GPU_TELEMETRY_TEST_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'win', | 
 |         'browser_config': 'release', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'gpu': '8086:5912-24.20.100.6286', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | INTEL_UHD_GPU_TELEMETRY_TEST_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'win', | 
 |         'browser_config': 'release', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'gpu': '8086:3e92-24.20.100.6286', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'composition_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | GPU_TELEMETRY_TEST_VARIANTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'win', | 
 |         'browser_config': 'release', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'gpu': '8086:3e92-24.20.100.6286', | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'matrix_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | UNKNOWN_TEST_SUITE_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'baz_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | UNKNOWN_TEST_SUITE_TYPE_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |           'foo_test_type': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | ANDROID_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Android Builder': { | 
 |         'additional_compile_targets': [ | 
 |           'bar_test', | 
 |         ], | 
 |       }, | 
 |       'Fake Android K Tester': { | 
 |         'additional_compile_targets': [ | 
 |           'bar_test', | 
 |         ], | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'device_os': 'KTU84P', | 
 |             'device_type': 'hammerhead', | 
 |             'os': 'Android', | 
 |           }, | 
 |         }, | 
 |         'os_type': 'android', | 
 |         'skip_merge_script': True, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |       'Fake Android L Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'device_os': 'LMY41U', | 
 |             'device_os_type': 'user', | 
 |             'device_type': 'hammerhead', | 
 |             'os': 'Android', | 
 |           }, | 
 |         }, | 
 |         'os_type': 'android', | 
 |         'skip_merge_script': True, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |       'Fake Android M Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'device_os': 'MMB29Q', | 
 |             'device_type': 'bullhead', | 
 |             'os': 'Android', | 
 |           }, | 
 |         }, | 
 |         'os_type': 'android', | 
 |         'use_swarming': False, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | UNKNOWN_BOT_GTESTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Unknown Bot': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | MATRIX_GTEST_SUITE_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'matrix_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | MATRIX_GTEST_SUITE_WATERFALL_MIXINS = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'mixins': ['dimension_mixin'], | 
 |         'test_suites': { | 
 |           'gtest_tests': 'matrix_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'integrity': 'high', | 
 |             'os': 'Linux', | 
 |           }, | 
 |           'expiration': 120, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_ANDROID_SWARMING = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'android_swarming': { | 
 |           'shards': 100, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_NO_DIMENSIONS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_NOT_SORTED = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |       'a_test': {}, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_ARGS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'args': [ | 
 |           '--c_arg', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_SWARMING_DIMENSION_SETS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'swarming': { | 
 |           'dimension_sets': [ | 
 |             { | 
 |               'foo': 'bar', | 
 |             }, | 
 |           ], | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_NAME = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'name': 'bar_test', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_ISOLATE_NAME = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'isolate_name': 'bar_test', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_SWARMING_NAMED_CACHES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'swarming': { | 
 |           'named_caches': [ | 
 |             { | 
 |               'name': 'cache_in_test', | 
 |               'file': 'cache_in_test_file', | 
 |             }, | 
 |           ], | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_LINUX_ARGS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'linux_args': [ | 
 |           '--no-xvfb', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_ENABLE_FEATURES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'args': [ | 
 |           '--enable-features=Foo,Bar', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_REMOVE_WATERFALL_MIXIN = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'remove_mixins': ['waterfall_mixin'], | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'integrity': 'high', | 
 |           }, | 
 |           'expiration': 120, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_REMOVE_BUILDER_MIXIN = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'remove_mixins': ['builder_mixin'], | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'integrity': 'high', | 
 |           }, | 
 |           'expiration': 120, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_SCRIPT_SUITE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_scripts': { | 
 |       'foo_test': { | 
 |         'script': 'foo.py', | 
 |         'args': ['common-arg'], | 
 |         'precommit_args': ['precommit-arg'], | 
 |         'non_precommit_args': ['non-precommit-arg'], | 
 |       }, | 
 |       'bar_test': { | 
 |         'script': 'bar.py', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | GOOD_COMPOSITION_TEST_SUITES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': { | 
 |       'bar_test': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'composition_tests': [ | 
 |       'foo_tests', | 
 |       'bar_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | BAD_COMPOSITION_TEST_SUITES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': {}, | 
 |     'foo_tests': {}, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'buggy_composition_tests': [ | 
 |       'bar_tests', | 
 |     ], | 
 |     'composition_tests': [ | 
 |       'foo_tests', | 
 |       'buggy_composition_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | CONFLICTING_COMPOSITION_TEST_SUITES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': { | 
 |       'baz_tests': { | 
 |         'args': [ | 
 |           '--bar', | 
 |         ], | 
 |       } | 
 |     }, | 
 |     'foo_tests': { | 
 |       'baz_tests': { | 
 |         'args': [ | 
 |           '--foo', | 
 |         ], | 
 |       } | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'foobar_tests': [ | 
 |       'foo_tests', | 
 |       'bar_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | DUPLICATES_COMPOSITION_TEST_SUITES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': {}, | 
 |     'buggy_composition_tests': {}, | 
 |     'foo_tests': {}, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'bar_tests': [ | 
 |       'foo_tests', | 
 |     ], | 
 |     'composition_tests': [ | 
 |       'foo_tests', | 
 |       'buggy_composition_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SCRIPT_SUITE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_scripts': { | 
 |       'foo_test': { | 
 |         'script': 'foo.py', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | UNREFED_TEST_SUITE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': {}, | 
 |     'foo_tests': {}, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | REUSING_TEST_WITH_DIFFERENT_NAME = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |       'variation_test': { | 
 |         'args': [ | 
 |           '--variation', | 
 |         ], | 
 |         'test': 'foo_test', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | COMPOSITION_SUITE_WITH_TELEMETRY_TEST_WITH_INVALID_NAME = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo': { | 
 |         'telemetry_test_name': 'foo', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'composition_tests': [ | 
 |       'foo_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | COMPOSITION_SUITE_WITH_TELEMETRY_TEST = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_tests': { | 
 |         'telemetry_test_name': 'foo', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |           'shards': 2, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |     'bar_tests': { | 
 |       'bar_test': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'composition_tests': [ | 
 |       'foo_tests', | 
 |       'bar_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | COMPOSITION_SUITE_WITH_GPU_ARGS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_tests': { | 
 |         'telemetry_test_name': 'foo', | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'args': [ | 
 |           '--gpu-vendor-id', | 
 |           '${gpu_vendor_id}', | 
 |           '--gpu-device-id', | 
 |           '${gpu_device_id}', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |     'bar_tests': { | 
 |       'bar_test': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'composition_tests': [ | 
 |       'foo_tests', | 
 |       'bar_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | COMPOSITION_SUITE_WITH_PIXEL_AND_FILTER = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'pixel': { | 
 |         'args': [ | 
 |           '--git-revision=aaaa', | 
 |           '--test-filter=bar', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |     'bar_tests': { | 
 |       'bar_test': {}, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'composition_tests': [ | 
 |       'foo_tests', | 
 |       'bar_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | GTEST_AS_ISOLATED_SCRIPT_SUITE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'script': 'foo.py', | 
 |         'use_isolated_scripts_api': True, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SCRIPT_WITH_ARGS_EXCEPTIONS = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'args': ['--fake-arg'], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SCRIPT_WITH_ARGS_SWARMING_EXCEPTIONS = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'value': 'exception', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | AUTOSHARD_EXCEPTIONS = json.dumps({ | 
 |     'chromium.test': { | 
 |         'Fake Tester': { | 
 |             'foo_test': { | 
 |                 'shards': 20, | 
 |             }, | 
 |             'foo_test_apk': { | 
 |                 'shards': 2, | 
 |             }, | 
 |             'swarming_test a_variant': { | 
 |                 'shards': 10, | 
 |             }, | 
 |         } | 
 |     }, | 
 | }) | 
 |  | 
 | NO_BAR_TEST_EXCEPTIONS = """\ | 
 | { | 
 |   'bar_test': { | 
 |     'remove_from': [ | 
 |       'Fake Tester', | 
 |     ] | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | EMPTY_BAR_TEST_EXCEPTIONS = """\ | 
 | { | 
 |   'bar_test': { | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | EXCEPTIONS_SORTED = """\ | 
 | { | 
 |   'suite_c': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'foo': 'bar', | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'suite_d': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'foo': 'baz', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | EXCEPTIONS_UNSORTED = """\ | 
 | { | 
 |   'suite_d': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'foo': 'baz', | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'suite_c': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'foo': 'bar', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | EXCEPTIONS_PER_TEST_UNSORTED = """\ | 
 | { | 
 |   'suite_d': { | 
 |     'modifications': { | 
 |       'Other Tester': { | 
 |         'foo': 'baz', | 
 |       }, | 
 |       'Fake Tester': { | 
 |         'foo': 'baz', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | EXCEPTIONS_DUPS_REMOVE_FROM = """\ | 
 | { | 
 |   'suite_d': { | 
 |     'remove_from': [ | 
 |       'Fake Tester', | 
 |       'Fake Tester', | 
 |     ], | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'foo': 'baz', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_MODIFICATIONS = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'args': [ | 
 |           '--bar', | 
 |         ], | 
 |         'swarming': { | 
 |           'hard_timeout': 600, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_EXPLICIT_NONE_EXCEPTIONS = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'modifications': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'integrity': None, | 
 |           }, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | NONEXISTENT_REMOVAL = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'remove_from': [ | 
 |       'Nonexistent Tester', | 
 |     ] | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | NONEXISTENT_MODIFICATION = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'modifications': { | 
 |       'Nonexistent Tester': { | 
 |         'args': [], | 
 |       }, | 
 |     }, | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | LUCI_MILO_CFG = """\ | 
 | consoles { | 
 |   builders { | 
 |     name: "buildbucket/luci.chromium.ci/Fake Tester" | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | LUCI_MILO_CFG_WATERFALL_SORTING = """\ | 
 | consoles { | 
 |   builders { | 
 |     name: "buildbucket/luci.chromium.ci/Fake Tester" | 
 |     name: "buildbucket/luci.chromium.ci/Really Fake Tester" | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | TEST_SUITE_SORTING_WATERFALL = """ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | TEST_SUITE_SORTED_WATERFALL = """ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |       'Really Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.zz.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |       'Really Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | TEST_SUITE_UNSORTED_WATERFALL_1 = """ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.zz.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |       'Really Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |       'Really Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | TEST_SUITE_UNSORTED_WATERFALL_2 = """ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Really Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.zz.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |       'Really Fake Tester': { | 
 |         'test_suites': { | 
 |           'gtest_tests': 'suite_a', | 
 |           'scripts': 'suite_b', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | # Note that the suites in basic_suites would be sorted after the suites in | 
 | # compound_suites. This is valid though, because each set of suites is sorted | 
 | # separately. | 
 | # suite_c is a 'gtest_tests' test | 
 | # suite_d is a 'scripts' test | 
 | TEST_SUITE_SORTED = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'suite_c': { | 
 |       'suite_c': {}, | 
 |     }, | 
 |     'suite_d': { | 
 |       'script': { | 
 |         'script': 'suite_d.py', | 
 |       } | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'suite_a': [ | 
 |       'suite_c', | 
 |     ], | 
 |     'suite_b': [ | 
 |       'suite_d', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | TEST_SUITE_UNSORTED_1 = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'suite_d': { | 
 |       'a': 'b', | 
 |     }, | 
 |     'suite_c': { | 
 |       'a': 'b', | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'suite_a': [ | 
 |       'suite_c', | 
 |     ], | 
 |     'suite_b': [ | 
 |       'suite_d', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | TEST_SUITE_UNSORTED_2 = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'suite_c': { | 
 |       'a': 'b', | 
 |     }, | 
 |     'suite_d': { | 
 |       'a': 'b', | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'suite_b': [ | 
 |       'suite_c', | 
 |     ], | 
 |     'suite_a': [ | 
 |       'suite_d', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 | TEST_SUITE_UNSORTED_3 = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'suite_d': { | 
 |       'a': 'b', | 
 |     }, | 
 |     'suite_c': { | 
 |       'a': 'b', | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'suite_b': [ | 
 |       'suite_c', | 
 |     ], | 
 |     'suite_a': [ | 
 |       'suite_d', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | TEST_SUITES_SYNTAX_ERROR = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     3: { | 
 |       'suite_c': {}, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': {}, | 
 | } | 
 | """ | 
 |  | 
 | GN_ISOLATE_MAP="""\ | 
 | { | 
 |   'foo_test': { | 
 |     'label': '//chrome/test:foo_test', | 
 |     'type': 'windowed_test_launcher', | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | GPU_TELEMETRY_GN_ISOLATE_MAP="""\ | 
 | { | 
 |   'telemetry_gpu_integration_test': { | 
 |     'label': '//chrome/test:telemetry_gpu_integration_test', | 
 |     'type': 'script', | 
 |       } | 
 | } | 
 | """ | 
 |  | 
 | GPU_TELEMETRY_GN_ISOLATE_MAP_ANDROID = """\ | 
 | { | 
 |   'telemetry_gpu_integration_test_android_chrome': { | 
 |     'label': '//chrome/test:telemetry_gpu_integration_test_android_chrome', | 
 |     'type': 'script', | 
 |       } | 
 | } | 
 | """ | 
 |  | 
 | GPU_TELEMETRY_GN_ISOLATE_MAP_ANDROID_WEBVIEW = """\ | 
 | { | 
 |   'telemetry_gpu_integration_test_android_webview': { | 
 |     'label': '//chrome/test:telemetry_gpu_integration_test_android_webview', | 
 |     'type': 'script', | 
 |       } | 
 | } | 
 | """ | 
 |  | 
 | GPU_TELEMETRY_GN_ISOLATE_MAP_FUCHSIA = """\ | 
 | { | 
 |   'telemetry_gpu_integration_test_fuchsia': { | 
 |     'label': '//chrome/test:telemetry_gpu_integration_test_fuchsia', | 
 |     'type': 'script', | 
 |       } | 
 | } | 
 | """ | 
 |  | 
 | GPU_TELEMETRY_GN_ISOLATE_MAP_CAST_STREAMING = """\ | 
 | { | 
 |   'telemetry_gpu_integration_test_fuchsia': { | 
 |     'label': '//chrome/test:telemetry_gpu_integration_test_fuchsia', | 
 |     'type': 'script', | 
 |       } | 
 | } | 
 | """ | 
 |  | 
 | GN_ISOLATE_MAP_KEY_LABEL_MISMATCH="""\ | 
 | { | 
 |   'foo_test': { | 
 |     'label': '//chrome/test:foo_test_tmp', | 
 |     'type': 'windowed_test_launcher', | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | GN_ISOLATE_MAP_USING_IMPLICIT_NAME="""\ | 
 | { | 
 |   'foo_test': { | 
 |     'label': '//chrome/foo_test', | 
 |     'type': 'windowed_test_launcher', | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | class UnitTest(TestCase): | 
 |  | 
 |   def test_dimension_sets_causes_error(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_SWARMING_DIMENSION_SETS, LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 r'.*dimension_sets is no longer supported.*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_name_causes_error(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_NAME, LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 r'.*\bname field is set\b.*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_isolate_name_causes_error(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ISOLATE_NAME, LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 r'.*\bisolate_name field is set\b.*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_good_test_suites_are_ok(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_WATERFALL, FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_good_composition_test_suites_are_ok(self): | 
 |     fbb = FakeBBGen(self.args, COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_bad_composition_test_suites_are_caught(self): | 
 |     fbb = FakeBBGen(self.args, COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     BAD_COMPOSITION_TEST_SUITES, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'compound_suites may not refer to.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_composition_test_suites_no_conflicts(self): | 
 |     fbb = FakeBBGen(self.args, COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     CONFLICTING_COMPOSITION_TEST_SUITES, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Conflicting test definitions.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_composition_test_suites_no_duplicate_names(self): | 
 |     fbb = FakeBBGen(self.args, COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     DUPLICATES_COMPOSITION_TEST_SUITES, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 '.*may not duplicate basic test suite.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_unknown_test_suites_are_caught(self): | 
 |     fbb = FakeBBGen(self.args, UNKNOWN_TEST_SUITE_WATERFALL, FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Test suite baz_tests from machine.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_unknown_test_suite_types_are_caught(self): | 
 |     fbb = FakeBBGen(self.args, UNKNOWN_TEST_SUITE_TYPE_WATERFALL, | 
 |                     FOO_TEST_SUITE, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Unknown test suite type foo_test_type.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_unrefed_test_suite_caught(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_WATERFALL, UNREFED_TEST_SUITE, | 
 |                     LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 '.*unreferenced.*bar_tests.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_good_waterfall_output(self): | 
 |     fbb = FakeBBGen(self.args, COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_good_waterfall_output_legacy_pyl_paths(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     use_legacy_pyl_paths=True) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_reusing_gtest_targets(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     REUSING_TEST_WITH_DIFFERENT_NAME, | 
 |                     LUCI_MILO_CFG, | 
 |                     gn_isolate_map=GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_load_multiple_isolate_map_files_with_duplicates(self): | 
 |     self.args.isolate_map_files = [self.args.gn_isolate_map_pyl.generated_path] | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     REUSING_TEST_WITH_DIFFERENT_NAME, | 
 |                     LUCI_MILO_CFG, | 
 |                     gn_isolate_map=GN_ISOLATE_MAP) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Duplicate targets in isolate map files.*'): | 
 |       fbb.load_configuration_files() | 
 |  | 
 |   def test_load_multiple_isolate_map_files_without_duplicates(self): | 
 |     self.args.isolate_map_files = ['gn_isolate_map2.pyl'] | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     REUSING_TEST_WITH_DIFFERENT_NAME, | 
 |                     LUCI_MILO_CFG, | 
 |                     gn_isolate_map=GN_ISOLATE_MAP) | 
 |     fbb.load_configuration_files() | 
 |     isolate_dict = {} | 
 |     isolate_map_1 = fbb.load_pyl_file(self.args.gn_isolate_map_pyl.actual_path) | 
 |     isolate_map_2 = fbb.load_pyl_file('gn_isolate_map2.pyl') | 
 |     isolate_dict.update(isolate_map_1) | 
 |     isolate_dict.update(isolate_map_2) | 
 |     self.assertEqual(isolate_dict, fbb.gn_isolate_map) | 
 |  | 
 |   def test_gn_isolate_map_with_label_mismatch(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     gn_isolate_map=GN_ISOLATE_MAP_KEY_LABEL_MISMATCH) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'key name.*foo_test.*label.*' | 
 |                                 'foo_test_tmp.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gn_isolate_map_using_implicit_gn_name(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     gn_isolate_map=GN_ISOLATE_MAP_USING_IMPLICIT_NAME) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'Malformed.*//chrome/foo_test.*for key.*' | 
 |         'foo_test.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_noop_exception_does_nothing(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EMPTY_BAR_TEST_EXCEPTIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_arg_merges(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_MODIFICATIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_enable_features_arg_merges(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_WITH_ENABLE_FEATURES_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ENABLE_FEATURES, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_linux_args(self): | 
 |     fbb = FakeBBGen(self.args, FOO_LINUX_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_LINUX_ARGS, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_filtering(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_modifications(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_MODIFICATIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_with_explicit_none(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_EXPLICIT_NONE_EXCEPTIONS, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_isolated_script_tests(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_ISOLATED_SCRIPTS_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_isolated_script_tests_android(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_ISOLATED_SCRIPTS_WATERFALL_ANDROID, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_script_with_args(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_SCRIPT_WATERFALL, | 
 |                     SCRIPT_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=SCRIPT_WITH_ARGS_EXCEPTIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_script(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_SCRIPT_WATERFALL, | 
 |                     FOO_SCRIPT_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_script_machine_forbids_scripts(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_SCRIPT_WATERFALL_MACHINE_FORBIDS_SCRIPT_TESTS, | 
 |                     FOO_SCRIPT_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'Attempted to generate a script test on tester.*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_script_waterfall_forbids_scripts(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_SCRIPT_WATERFALL_FORBID_SCRIPT_TESTS, | 
 |                     FOO_SCRIPT_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'Attempted to generate a script test on tester.*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_gpu_telemetry_test_with_invalid_name(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_WATERFALL, | 
 |                     COMPOSITION_SUITE_WITH_TELEMETRY_TEST_WITH_INVALID_NAME, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'telemetry test names must end with test or tests.*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gpu_telemetry_tests(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_WATERFALL, | 
 |                     COMPOSITION_SUITE_WITH_TELEMETRY_TEST, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gpu_telemetry_tests_android(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_WATERFALL_ANDROID, | 
 |                     COMPOSITION_SUITE_WITH_TELEMETRY_TEST, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP_ANDROID) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gpu_telemetry_tests_android_webview(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_WATERFALL_ANDROID_WEBVIEW, | 
 |                     COMPOSITION_SUITE_WITH_TELEMETRY_TEST, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP_ANDROID_WEBVIEW) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gpu_telemetry_tests_fuchsia(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_WATERFALL_FUCHSIA, | 
 |                     COMPOSITION_SUITE_WITH_TELEMETRY_TEST, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP_FUCHSIA) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gpu_telemetry_tests_cast_streaming(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_WATERFALL_CAST_STREAMING, | 
 |                     COMPOSITION_SUITE_WITH_TELEMETRY_TEST, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP_CAST_STREAMING) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gpu_telemetry_tests_skylab(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_WATERFALL_SKYLAB, | 
 |                     COMPOSITION_SUITE_WITH_TELEMETRY_TEST, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_nvidia_gpu_telemetry_tests(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     NVIDIA_GPU_TELEMETRY_TEST_WATERFALL, | 
 |                     COMPOSITION_SUITE_WITH_GPU_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_intel_gpu_telemetry_tests(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     INTEL_GPU_TELEMETRY_TEST_WATERFALL, | 
 |                     COMPOSITION_SUITE_WITH_GPU_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gpu_telemetry_tests_with_variants(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     GPU_TELEMETRY_TEST_VARIANTS_WATERFALL, | 
 |                     MATRIX_COMPOUND_MIXED_VARIANTS_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP, | 
 |                     variants=MULTI_VARIANTS_FILE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_intel_uhd_gpu_telemetry_tests(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     INTEL_UHD_GPU_TELEMETRY_TEST_WATERFALL, | 
 |                     COMPOSITION_SUITE_WITH_GPU_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_gtest_as_isolated_Script(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     GTEST_AS_ISOLATED_SCRIPT_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     gn_isolate_map=GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_ungenerated_output_files_are_caught(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     COMPOSITION_GTEST_SUITE_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NO_BAR_TEST_EXCEPTIONS) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_output_file_consistency(verbose=True, dump=False) | 
 |     joined_lines = ' '.join(fbb.printed_lines) | 
 |     self.assertRegex( | 
 |         joined_lines, 'File chromium.test.json did not have the following' | 
 |         ' expected contents:.*') | 
 |     self.assertRegex(joined_lines, r'.*--- expected.*') | 
 |     self.assertRegex(joined_lines, r'.*\+\+\+ current.*') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_android_output_options(self): | 
 |     fbb = FakeBBGen(self.args, ANDROID_WATERFALL, FOO_TEST_SUITE, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_android_swarming(self): | 
 |     fbb = FakeBBGen(self.args, ANDROID_WATERFALL, | 
 |                     FOO_TEST_SUITE_ANDROID_SWARMING, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_nonexistent_removal_raises(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NONEXISTENT_REMOVAL) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'The following nonexistent machines.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_nonexistent_modification_raises(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=NONEXISTENT_MODIFICATION) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'The following nonexistent machines.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_waterfall_args(self): | 
 |     fbb = FakeBBGen(self.args, COMPOSITION_GTEST_SUITE_WITH_ARGS_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_chromeos_trigger_script_output(self): | 
 |     fbb = FakeBBGen(self.args, FOO_CHROMEOS_TRIGGER_SCRIPT_WATERFALL, | 
 |                     FOO_TEST_SUITE, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_relative_pyl_file_dir(self): | 
 |     self.set_args('--pyl-files-dir=relative/path') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     REUSING_TEST_WITH_DIFFERENT_NAME, | 
 |                     LUCI_MILO_CFG, | 
 |                     gn_isolate_map=GN_ISOLATE_MAP) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_nonexistent_bot_raises(self): | 
 |     fbb = FakeBBGen(self.args, UNKNOWN_BOT_GTESTS_WATERFALL, FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_nonexistent_bot_raises_when_no_project_pyl_exists(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     UNKNOWN_BOT_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     project_pyl=None) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_nonexistent_bot_does_not_raise_when_validation_disabled(self): | 
 |     fbb = FakeBBGen( | 
 |         self.args, | 
 |         UNKNOWN_BOT_GTESTS_WATERFALL, | 
 |         FOO_TEST_SUITE, | 
 |         LUCI_MILO_CFG, | 
 |         project_pyl='{"validate_source_side_specs_have_builder": False}') | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |  | 
 |   def test_waterfalls_must_be_sorted(self): | 
 |     fbb = FakeBBGen(self.args, TEST_SUITE_SORTED_WATERFALL, TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG_WATERFALL_SORTING) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |     fbb = FakeBBGen(self.args, TEST_SUITE_UNSORTED_WATERFALL_1, | 
 |                     TEST_SUITE_SORTED, LUCI_MILO_CFG_WATERFALL_SORTING) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         ('The following files have invalid keys: ' + | 
 |          re.escape(self.args.waterfalls_pyl_path)), | 
 |     ): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = '\n'.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\+ chromium\..*test.*') | 
 |     self.assertRegex(joined_lines, r'.*\- chromium\..*test.*') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |     fbb = FakeBBGen(self.args, TEST_SUITE_UNSORTED_WATERFALL_2, | 
 |                     TEST_SUITE_SORTED, LUCI_MILO_CFG_WATERFALL_SORTING) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         ('The following files have invalid keys: ' + | 
 |          re.escape(self.args.waterfalls_pyl_path)), | 
 |     ): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = ' '.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\+.*Fake Tester.*') | 
 |     self.assertRegex(joined_lines, r'.*\-.*Fake Tester.*') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_suite_exceptions_must_be_sorted(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     TEST_SUITE_SORTING_WATERFALL, | 
 |                     TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EXCEPTIONS_SORTED) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |     fbb = FakeBBGen(self.args, | 
 |                     TEST_SUITE_SORTING_WATERFALL, | 
 |                     TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EXCEPTIONS_DUPS_REMOVE_FROM) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = ' '.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\- Fake Tester.*') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_suite_exceptions_no_dups_remove_from(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     TEST_SUITE_SORTING_WATERFALL, | 
 |                     TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EXCEPTIONS_SORTED) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |     fbb = FakeBBGen(self.args, | 
 |                     TEST_SUITE_SORTING_WATERFALL, | 
 |                     TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EXCEPTIONS_PER_TEST_UNSORTED) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = ' '.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\+ Fake Tester.*') | 
 |     self.assertRegex(joined_lines, r'.*\- Fake Tester.*') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_suite_exceptions_per_test_must_be_sorted(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     TEST_SUITE_SORTING_WATERFALL, | 
 |                     TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EXCEPTIONS_SORTED) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |     fbb = FakeBBGen(self.args, | 
 |                     TEST_SUITE_SORTING_WATERFALL, | 
 |                     TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EXCEPTIONS_UNSORTED) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = ' '.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\+ suite_.*') | 
 |     self.assertRegex(joined_lines, r'.*\- suite_.*') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_suites_must_be_sorted(self): | 
 |     fbb = FakeBBGen(self.args, TEST_SUITE_SORTING_WATERFALL, TEST_SUITE_SORTED, | 
 |                     LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |     for unsorted in ( | 
 |         TEST_SUITE_UNSORTED_1, | 
 |         TEST_SUITE_UNSORTED_2, | 
 |         TEST_SUITE_UNSORTED_3, | 
 |     ): | 
 |       fbb = FakeBBGen(self.args, TEST_SUITE_SORTING_WATERFALL, unsorted, | 
 |                       LUCI_MILO_CFG) | 
 |       with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |         fbb.check_input_file_consistency(verbose=True) | 
 |       joined_lines = ' '.join(fbb.printed_lines) | 
 |       self.assertRegex(joined_lines, r'.*\+ suite_.*') | 
 |       self.assertRegex(joined_lines, r'.*\- suite_.*') | 
 |       fbb.printed_lines = [] | 
 |       self.assertFalse(fbb.printed_lines) | 
 |  | 
 |  | 
 | FOO_GTESTS_WATERFALL_MIXIN_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'mixins': ['waterfall_mixin'], | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GTESTS_BUILDER_MIXIN_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'mixins': ['builder_mixin'], | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_LINUX_GTESTS_BUILDER_MIXIN_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'os_type': 'linux', | 
 |         'mixins': ['builder_mixin'], | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Linux', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GTESTS_BUILDER_MIXIN_NON_SWARMING_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'mixins': ['random_mixin'], | 
 |         'swarming': {}, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GTESTS_DIMENSIONS_MIXIN_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'mixins': ['dimension_mixin'], | 
 |         'swarming': {}, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GPU_TELEMETRY_TEST_DIMENSIONS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'mixins': ['dimension_mixin'], | 
 |         'os_type': 'win', | 
 |         'browser_config': 'release', | 
 |         'test_suites': { | 
 |           'gpu_telemetry_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | # Swarming mixins must be a list, a single string is not allowed. | 
 | FOO_GTESTS_INVALID_LIST_MIXIN_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'mixins': 'dimension_mixin', | 
 |         'swarming': {}, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 | FOO_GTESTS_INVALID_NOTFOUND_MIXIN_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'mixins': ['nonexistant'], | 
 |         'swarming': {}, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GTESTS_TEST_MIXIN_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'mixins': ['waterfall_mixin'], | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'swarming': {}, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_GTESTS_SORTING_MIXINS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'mixins': ['a_mixin', 'b_mixin', 'c_mixin'], | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'kvm': '1', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_MIXIN = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'integrity': 'high', | 
 |             'os': 'Linux', | 
 |           }, | 
 |           'expiration': 120, | 
 |         }, | 
 |         'mixins': ['test_mixin'], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | FOO_TEST_SUITE_WITH_TEST_COMMON = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'test_common': { | 
 |           'args': ['test-common-arg'], | 
 |           'mixins': ['test-common-mixin'], | 
 |         }, | 
 |         'args': ['test-arg'], | 
 |         'mixins': ['test-mixin'], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MIXIN_ARGS = """\ | 
 | { | 
 |   'builder_mixin': { | 
 |     'args': [], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MIXIN_ARGS_NOT_LIST = """\ | 
 | { | 
 |   'builder_mixin': { | 
 |     'args': 'I am not a list', | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MIXIN_LINUX_ARGS = """\ | 
 | { | 
 |   'builder_mixin': { | 
 |     'args': [ '--mixin-argument' ], | 
 |     'linux_args': [ '--linux-mixin-argument' ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MIXIN_APPEND = """\ | 
 | { | 
 |   'builder_mixin': { | 
 |     '$mixin_append': { | 
 |       'args': [ '--mixin-argument' ], | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MIXINS_FAIL_IF_UNUSED_FALSE = """\ | 
 | { | 
 |   'test_mixin': { | 
 |     'fail_if_unused': False, | 
 |     'swarming': { | 
 |       'value': 'test', | 
 |     }, | 
 |   }, | 
 |   'unused_mixin': { | 
 |     'fail_if_unused': False, | 
 |     'args': ['--unused'], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | # These mixins are invalid; if passed to check_input_file_consistency, they will | 
 | # fail. These are used for output file consistency checks. | 
 | SWARMING_MIXINS = """\ | 
 | { | 
 |   'builder_mixin': { | 
 |     'swarming': { | 
 |       'value': 'builder', | 
 |     }, | 
 |   }, | 
 |   'dimension_mixin': { | 
 |     'swarming': { | 
 |       'dimensions': { | 
 |         'iama': 'mixin', | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'random_mixin': { | 
 |     'value': 'random', | 
 |   }, | 
 |   'test_mixin': { | 
 |     'swarming': { | 
 |       'value': 'test', | 
 |     }, | 
 |   }, | 
 |   'waterfall_mixin': { | 
 |     'swarming': { | 
 |       'value': 'waterfall', | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SWARMING_NAMED_CACHES = """\ | 
 | { | 
 |   'builder_mixin': { | 
 |     'swarming': { | 
 |       'named_caches': [ | 
 |         { | 
 |           'name': 'cache', | 
 |           'file': 'cache_file', | 
 |         }, | 
 |       ], | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SWARMING_MIXINS_DUPLICATED = """\ | 
 | { | 
 |   'builder_mixin': { | 
 |     'value': 'builder', | 
 |   }, | 
 |   'builder_mixin': { | 
 |     'value': 'builder', | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SWARMING_MIXINS_UNSORTED = """\ | 
 | { | 
 |   'b_mixin': { | 
 |     'b': 'b', | 
 |   }, | 
 |   'a_mixin': { | 
 |     'a': 'a', | 
 |   }, | 
 |   'c_mixin': { | 
 |     'c': 'c', | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SWARMING_MIXINS_SORTED = """\ | 
 | { | 
 |   'a_mixin': { | 
 |     'a': 'a', | 
 |   }, | 
 |   'b_mixin': { | 
 |     'b': 'b', | 
 |   }, | 
 |   'c_mixin': { | 
 |     'c': 'c', | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | TEST_COMMON_MIXINS = """\ | 
 | { | 
 |   'test-common-mixin': { | 
 |     'args': ['test-common-mixin-arg'], | 
 |   }, | 
 |   'test-mixin': { | 
 |     'args': ['test-mixin-arg'], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | class MixinTests(TestCase): | 
 |   """Tests for the mixins feature.""" | 
 |   def test_mixins_must_be_sorted(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_SORTING_MIXINS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_SORTING_MIXINS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_UNSORTED) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = '\n'.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\+ ._mixin.*') | 
 |     self.assertRegex(joined_lines, r'.*\- ._mixin.*') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_waterfall(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_waterfall_exception_overrides(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=SCRIPT_WITH_ARGS_SWARMING_EXCEPTIONS, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_autoshard_exceptions(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=SCRIPT_WITH_ARGS_SWARMING_EXCEPTIONS, | 
 |                     autoshard_exceptions=AUTOSHARD_EXCEPTIONS, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_autoshard_exceptions_variant_names(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_TEST_WITH_TEST_KEY, | 
 |                     LUCI_MILO_CFG, | 
 |                     autoshard_exceptions=AUTOSHARD_EXCEPTIONS, | 
 |                     variants=VARIANTS_FILE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_builder(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_builder_non_swarming(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_NON_SWARMING_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_suite(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_dimension(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_DIMENSIONS_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_dimension_gpu(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GPU_TELEMETRY_TEST_DIMENSIONS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS, | 
 |                     gn_isolate_map=GPU_TELEMETRY_GN_ISOLATE_MAP) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_unreferenced(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 '.*mixins are unreferenced.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_unused(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_INVALID_NOTFOUND_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_fail_if_unused_false(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=MIXINS_FAIL_IF_UNUSED_FALSE) | 
 |     fbb.check_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_list(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_INVALID_LIST_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     with self.assertRaises(generate_buildbot_json.BBGenErr): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_no_duplicate_keys(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_DUPLICATED) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         ('The following files have invalid keys: ' + | 
 |          re.escape(self.args.mixins_pyl.actual_path)), | 
 |     ): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = '\n'.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\- builder_mixin') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_no_duplicate_keys_basic_test_suite(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_WATERFALL, FOO_TEST_SUITE_NOT_SORTED, | 
 |                     LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         ('The following files have invalid keys: ' + | 
 |          re.escape(self.args.test_suites_pyl.actual_path)), | 
 |     ): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     joined_lines = '\n'.join(fbb.printed_lines) | 
 |     self.assertRegex(joined_lines, r'.*\- a_test') | 
 |     self.assertRegex(joined_lines, r'.*\+ a_test') | 
 |     fbb.printed_lines = [] | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_type_assert_printing_help(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_WATERFALL, TEST_SUITES_SYNTAX_ERROR, | 
 |                     LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         f'Invalid \\.pyl file ' | 
 |         f"'{re.escape(self.args.test_suites_pyl.actual_path)}'.*", | 
 |     ): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertEqual(fbb.printed_lines, [ | 
 |         f'== {self.args.test_suites_pyl.actual_path} ==', | 
 |         '<snip>', | 
 |         '1 {', | 
 |         "2   'basic_suites': {", | 
 |         '--------------------------------------------------------------------' | 
 |         '------------', | 
 |         '3     3: {', | 
 |         '-------^------------------------------------------------------------' | 
 |         '------------', | 
 |         "4       'suite_c': {},", | 
 |         '5     },', | 
 |         '<snip>', | 
 |     ]) | 
 |  | 
 |   def test_mixin_append(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=MIXIN_APPEND) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 r'.*\$mixin_append is no longer supported.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_swarming_named_caches(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_SWARMING_NAMED_CACHES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_NAMED_CACHES) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_args_field_not_list(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=MIXIN_ARGS_NOT_LIST) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 '"args" must be a list'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_args_field_merging(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=MIXIN_ARGS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_linux_args_field_merging(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_LINUX_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=MIXIN_LINUX_ARGS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_remove_mixin_test_remove_waterfall(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_REMOVE_WATERFALL_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_remove_mixin_test_remove_builder(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_BUILDER_MIXIN_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_REMOVE_BUILDER_MIXIN, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_test_common(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_TEST_COMMON, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=TEST_COMMON_MIXINS) | 
 |     fbb.check_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |  | 
 | TEST_SUITE_WITH_PARAMS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': { | 
 |       'bar_test': { | 
 |         'args': ['--no-xvfb'], | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'device_os': 'NMF26U' | 
 |           }, | 
 |         }, | 
 |         'should_retry_with_patch': False, | 
 |       }, | 
 |       'bar_test_test': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'kvm': '1' | 
 |           }, | 
 |           'hard_timeout': 1000 | 
 |         }, | 
 |         'should_retry_with_patch': True | 
 |       } | 
 |     }, | 
 |     'foo_tests': { | 
 |       'foo_test_empty': {}, | 
 |       'foo_test': { | 
 |         'args': [ | 
 |           '--jobs=1', | 
 |           '--verbose' | 
 |         ], | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'device_os': 'MMB29Q' | 
 |           }, | 
 |           'hard_timeout': 1800 | 
 |         } | 
 |       }, | 
 |       'pls': { | 
 |         'swarming': { | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'composition_tests': [ | 
 |         'foo_tests', | 
 |         'bar_tests', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 | TEST_QUERY_BOTS_OUTPUT = { | 
 |     'Fake Android M Tester': { | 
 |         'gtest_tests': [{ | 
 |             'name': 'foo_test', | 
 |             'test': 'foo_test', | 
 |         }] | 
 |     }, | 
 |     'Fake Android L Tester': { | 
 |         'gtest_tests': [{ | 
 |             'test': | 
 |             'foo_test', | 
 |             'args': [ | 
 |                 '--gs-results-bucket=chromium-result-details', | 
 |                 '--recover-devices' | 
 |             ], | 
 |             'merge': { | 
 |                 'script': '//testing/merge_scripts/standard_gtest_merge.py' | 
 |             }, | 
 |             'name': | 
 |             'foo_test', | 
 |             'swarming': { | 
 |                 'dimensions': { | 
 |                     'device_os': 'LMY41U', | 
 |                     'device_os_type': 'user', | 
 |                     'device_type': 'hammerhead', | 
 |                     'os': 'Android' | 
 |                 }, | 
 |             } | 
 |         }] | 
 |     }, | 
 |     'Fake Android K Tester': { | 
 |         'additional_compile_targets': ['bar_test'], | 
 |         'gtest_tests': [{ | 
 |             'test': | 
 |             'foo_test', | 
 |             'args': [ | 
 |                 '--gs-results-bucket=chromium-result-details', | 
 |                 '--recover-devices' | 
 |             ], | 
 |             'merge': { | 
 |                 'script': '//testing/merge_scripts/standard_gtest_merge.py' | 
 |             }, | 
 |             'name': | 
 |             'foo_test', | 
 |             'swarming': { | 
 |                 'dimensions': { | 
 |                     'device_os': 'KTU84P', | 
 |                     'device_os_type': 'userdebug', | 
 |                     'device_type': 'hammerhead', | 
 |                     'os': 'Android', | 
 |                 }, | 
 |             } | 
 |         }] | 
 |     }, | 
 |     'Android Builder': { | 
 |         'additional_compile_targets': ['bar_test'] | 
 |     } | 
 | } | 
 | TEST_QUERY_BOTS_TESTS_OUTPUT = { | 
 |     'Fake Android M Tester': [{ | 
 |         'name': 'foo_test', | 
 |         'test': 'foo_test', | 
 |     }], | 
 |     'Fake Android L Tester': [{ | 
 |         'test': | 
 |         'foo_test', | 
 |         'args': | 
 |         ['--gs-results-bucket=chromium-result-details', '--recover-devices'], | 
 |         'merge': { | 
 |             'script': '//testing/merge_scripts/standard_gtest_merge.py' | 
 |         }, | 
 |         'name': | 
 |         'foo_test', | 
 |         'swarming': { | 
 |             'dimensions': { | 
 |                 'device_os': 'LMY41U', | 
 |                 'device_os_type': 'user', | 
 |                 'device_type': 'hammerhead', | 
 |                 'os': 'Android' | 
 |             }, | 
 |         } | 
 |     }], | 
 |     'Android Builder': [], | 
 |     'Fake Android K Tester': [{ | 
 |         'test': | 
 |         'foo_test', | 
 |         'args': | 
 |         ['--gs-results-bucket=chromium-result-details', '--recover-devices'], | 
 |         'merge': { | 
 |             'script': '//testing/merge_scripts/standard_gtest_merge.py' | 
 |         }, | 
 |         'name': | 
 |         'foo_test', | 
 |         'swarming': { | 
 |             'dimensions': { | 
 |                 'device_os': 'KTU84P', | 
 |                 'device_os_type': 'userdebug', | 
 |                 'device_type': 'hammerhead', | 
 |                 'os': 'Android' | 
 |             }, | 
 |         } | 
 |     }] | 
 | } | 
 |  | 
 | TEST_QUERY_BOT_OUTPUT = { | 
 |     'additional_compile_targets': ['bar_test'], | 
 |     'gtest_tests': [ | 
 |         { | 
 |             'test': | 
 |             'foo_test', | 
 |             'args': [ | 
 |                 '--gs-results-bucket=chromium-result-details', | 
 |                 '--recover-devices', | 
 |             ], | 
 |             'merge': { | 
 |                 'script': '//testing/merge_scripts/standard_gtest_merge.py', | 
 |             }, | 
 |             'name': | 
 |             'foo_test', | 
 |             'swarming': { | 
 |                 'dimensions': { | 
 |                     'device_os': 'KTU84P', | 
 |                     'device_os_type': 'userdebug', | 
 |                     'device_type': 'hammerhead', | 
 |                     'os': 'Android', | 
 |                 }, | 
 |             }, | 
 |         }, | 
 |     ], | 
 | } | 
 | TEST_QUERY_BOT_TESTS_OUTPUT = [{ | 
 |     'test': | 
 |     'foo_test', | 
 |     'args': | 
 |     ['--gs-results-bucket=chromium-result-details', '--recover-devices'], | 
 |     'merge': { | 
 |         'script': '//testing/merge_scripts/standard_gtest_merge.py' | 
 |     }, | 
 |     'name': | 
 |     'foo_test', | 
 |     'swarming': { | 
 |         'dimensions': { | 
 |             'device_os': 'LMY41U', | 
 |             'device_os_type': 'user', | 
 |             'device_type': 'hammerhead', | 
 |             'os': 'Android' | 
 |         }, | 
 |     } | 
 | }] | 
 |  | 
 | TEST_QUERY_TESTS_OUTPUT = { | 
 |     'bar_test': { | 
 |         'name': 'bar_test', | 
 |         'swarming': { | 
 |             'dimensions': { | 
 |                 'os': 'Linux' | 
 |             }, | 
 |         } | 
 |     }, | 
 |     'foo_test': { | 
 |         'name': 'foo_test', | 
 |         'swarming': { | 
 |             'dimensions': { | 
 |                 'os': 'Linux' | 
 |             }, | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | TEST_QUERY_TESTS_MULTIPLE_PARAMS_OUTPUT = ['foo_test'] | 
 |  | 
 | TEST_QUERY_TESTS_DIMENSION_PARAMS_OUTPUT = ['bar_test'] | 
 |  | 
 | TEST_QUERY_TESTS_SWARMING_PARAMS_OUTPUT = ['bar_test_test'] | 
 |  | 
 | TEST_QUERY_TESTS_PARAMS_OUTPUT = ['bar_test_test'] | 
 |  | 
 | TEST_QUERY_TESTS_PARAMS_FALSE_OUTPUT = ['bar_test'] | 
 |  | 
 | TEST_QUERY_TEST_OUTPUT = { | 
 |     'name': 'foo_test', | 
 |     'swarming': { | 
 |         'dimensions': { | 
 |             'os': 'Linux', | 
 |         }, | 
 |     }, | 
 | } | 
 |  | 
 | TEST_QUERY_TEST_BOTS_OUTPUT = [ | 
 |     'Fake Android K Tester', | 
 |     'Fake Android L Tester', | 
 |     'Fake Android M Tester', | 
 | ] | 
 |  | 
 | TEST_QUERY_TEST_BOTS_ISOLATED_SCRIPTS_OUTPUT = ['Fake Tester'] | 
 |  | 
 | TEST_QUERY_TEST_BOTS_NO_BOTS_OUTPUT = [] | 
 |  | 
 |  | 
 | class QueryTests(TestCase): | 
 |   """Tests for the query feature.""" | 
 |   def test_query_bots(self): | 
 |     self.set_args('--query=bots') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_BOTS_OUTPUT) | 
 |  | 
 |   def test_query_bots_invalid(self): | 
 |     self.set_args('--query=bots/blah/blah') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_bots_json(self): | 
 |     self.set_args('--query=bots', '--json=result.json') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_query_bots_tests(self): | 
 |     self.set_args('--query=bots/tests') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_BOTS_TESTS_OUTPUT) | 
 |  | 
 |   def test_query_invalid_bots_tests(self): | 
 |     self.set_args('--query=bots/tdfjdk') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_bot(self): | 
 |     self.set_args('--query=bot/Fake Android K Tester') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.maxDiff = None  # pragma pylint: disable=attribute-defined-outside-init | 
 |     self.assertEqual(query_json, TEST_QUERY_BOT_OUTPUT) | 
 |  | 
 |   def test_query_bot_invalid_id(self): | 
 |     self.set_args('--query=bot/bot1') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_bot_invalid_query_too_many(self): | 
 |     self.set_args('--query=bot/Fake Android K Tester/blah/blah') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_bot_invalid_query_no_tests(self): | 
 |     self.set_args('--query=bot/Fake Android K Tester/blahs') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_bot_tests(self): | 
 |     self.set_args('--query=bot/Fake Android L Tester/tests') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_BOT_TESTS_OUTPUT) | 
 |  | 
 |   def test_query_tests(self): | 
 |     self.set_args('--query=tests') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TESTS_OUTPUT) | 
 |  | 
 |   def test_query_tests_invalid(self): | 
 |     self.set_args('--query=tests/blah/blah') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_tests_multiple_params(self): | 
 |     self.set_args('--query=tests/--jobs=1&--verbose') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     TEST_SUITE_WITH_PARAMS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TESTS_MULTIPLE_PARAMS_OUTPUT) | 
 |  | 
 |   def test_query_tests_invalid_params(self): | 
 |     self.set_args('--query=tests/device_os?') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     TEST_SUITE_WITH_PARAMS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_tests_dimension_params(self): | 
 |     self.set_args('--query=tests/device_os:NMF26U') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     TEST_SUITE_WITH_PARAMS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TESTS_DIMENSION_PARAMS_OUTPUT) | 
 |  | 
 |   def test_query_tests_swarming_params(self): | 
 |     self.set_args('--query=tests/hard_timeout:1000') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     TEST_SUITE_WITH_PARAMS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TESTS_SWARMING_PARAMS_OUTPUT) | 
 |  | 
 |   def test_query_tests_params(self): | 
 |     self.set_args('--query=tests/should_retry_with_patch:true') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     TEST_SUITE_WITH_PARAMS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TESTS_PARAMS_OUTPUT) | 
 |  | 
 |   def test_query_tests_params_false(self): | 
 |     self.set_args('--query=tests/should_retry_with_patch:false') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     TEST_SUITE_WITH_PARAMS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TESTS_PARAMS_FALSE_OUTPUT) | 
 |  | 
 |   def test_query_test(self): | 
 |     self.set_args('--query=test/foo_test') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TEST_OUTPUT) | 
 |  | 
 |   def test_query_test_invalid_id(self): | 
 |     self.set_args('--query=test/foo_foo') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_test_invalid_length(self): | 
 |     self.set_args('--query=test/foo_tests/foo/foo') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_test_bots(self): | 
 |     self.set_args('--query=test/foo_test/bots') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TEST_BOTS_OUTPUT) | 
 |  | 
 |   def test_query_test_bots_isolated_scripts(self): | 
 |     self.set_args('--query=test/foo_test/bots') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_ISOLATED_SCRIPTS_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TEST_BOTS_ISOLATED_SCRIPTS_OUTPUT) | 
 |  | 
 |   def test_query_test_bots_invalid(self): | 
 |     self.set_args('--query=test/foo_tests/foo') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |   def test_query_test_bots_no_bots(self): | 
 |     self.set_args('--query=test/bar_tests/bots') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     fbb.query(fbb.args) | 
 |     query_json = json.loads(''.join(fbb.printed_lines)) | 
 |     self.assertEqual(query_json, TEST_QUERY_TEST_BOTS_NO_BOTS_OUTPUT) | 
 |  | 
 |   def test_query_invalid(self): | 
 |     self.set_args('--query=foo') | 
 |     fbb = FakeBBGen(self.args, | 
 |                     ANDROID_WATERFALL, | 
 |                     GOOD_COMPOSITION_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS_SORTED) | 
 |     with self.assertRaises(SystemExit) as cm: | 
 |       fbb.query(fbb.args) | 
 |       self.assertEqual(cm.exception.code, 1) | 
 |     self.assertTrue(fbb.printed_lines) | 
 |  | 
 |  | 
 | FOO_TEST_SUITE_WITH_ENABLE_FEATURES_SEPARATE_ENTRIES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'args': [ | 
 |           '--enable-features', | 
 |           'Foo,Bar', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | FOO_TEST_REPLACEMENTS_REMOVE_NO_VALUE = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'replacements': { | 
 |       'Fake Tester': { | 
 |         'args': { | 
 |           '--c_arg': None, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | FOO_TEST_REPLACEMENTS_REMOVE_VALUE = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'replacements': { | 
 |       'Fake Tester': { | 
 |         'args': { | 
 |           '--enable-features': None, | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | FOO_TEST_REPLACEMENTS_REPLACE_VALUE = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'replacements': { | 
 |       'Fake Tester': { | 
 |         'args': { | 
 |           '--enable-features': 'Bar,Baz', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | FOO_TEST_REPLACEMENTS_INVALID_KEY = """\ | 
 | { | 
 |   'foo_test': { | 
 |     'replacements': { | 
 |       'Fake Tester': { | 
 |         'invalid': { | 
 |           '--enable-features': 'Bar,Baz', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | class ReplacementTests(TestCase): | 
 |   """Tests for the arg replacement feature.""" | 
 |   def test_replacement_valid_remove_no_value(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_REPLACEMENTS_REMOVE_NO_VALUE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_replacement_valid_remove_value(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ENABLE_FEATURES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_REPLACEMENTS_REMOVE_VALUE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_replacement_valid_replace_value(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ENABLE_FEATURES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_REPLACEMENTS_REPLACE_VALUE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_replacement_valid_replace_value_separate_entries(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ENABLE_FEATURES_SEPARATE_ENTRIES, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_REPLACEMENTS_REPLACE_VALUE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_replacement_invalid_key_not_valid(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_REPLACEMENTS_INVALID_KEY) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Given replacement key *'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_replacement_invalid_key_not_found(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=FOO_TEST_REPLACEMENTS_REPLACE_VALUE) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Could not find *'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |  | 
 | FOO_TEST_SUITE_WITH_MAGIC_ARGS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'args': [ | 
 |           '$$MAGIC_SUBSTITUTION_TestOnlySubstitution', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | FOO_TEST_SUITE_WITH_INVALID_MAGIC_ARGS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |         'args': [ | 
 |           '$$MAGIC_SUBSTITUTION_NotARealSubstitution', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | class MagicSubstitutionTests(TestCase): | 
 |   """Tests for the magic substitution feature.""" | 
 |   def test_valid_function(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_MAGIC_ARGS, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_invalid_function(self): | 
 |     fbb = FakeBBGen(self.args, FOO_GTESTS_WATERFALL, | 
 |                     FOO_TEST_SUITE_WITH_INVALID_MAGIC_ARGS, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Magic substitution function *'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |  | 
 | # Matrix compound composition test suites | 
 |  | 
 | MATRIX_COMPOUND_EMPTY = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': { | 
 |       'bar_test': {}, | 
 |     }, | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': {}, | 
 |       'bar_tests': {}, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_EMPTY_WITH_DESCRIPTION = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': { | 
 |       'bar_test': { | 
 |         'description': 'This is a bar test', | 
 |       }, | 
 |     }, | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': {}, | 
 |       'bar_tests': {}, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_MISSING_IDENTIFIER = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'missing-identifier', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | VARIANTS_FILE_MISSING_IDENTIFIER = """\ | 
 | { | 
 |   'missing-identifier': {} | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_EMPTY_IDENTIFIER = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'empty-identifier', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | EMPTY_IDENTIFIER_VARIANTS = """\ | 
 | { | 
 |   'empty-identifier': { | 
 |     'identifier': '', | 
 |     'swarming': { | 
 |       'dimensions': { | 
 |         'foo': 'empty identifier not allowed', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_TRAILING_IDENTIFIER = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'trailing-whitespace', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | TRAILING_WHITESPACE_VARIANTS = """\ | 
 | { | 
 |   'trailing-whitespace': { | 
 |     'identifier': 'id ', | 
 |     'swarming': { | 
 |       'dimensions': { | 
 |         'foo': 'trailing whitespace not allowed', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_REF_NONEXISTENT = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'bar_test': {}, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_REF_COMPOSITION = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': { | 
 |     'sample_composition': { | 
 |       'foo_tests': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'sample_composition': {}, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOSITION_REF_MATRIX = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': {}, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'a_test': { | 
 |       'foo_tests': {}, | 
 |     }, | 
 |     'matrix_tests': { | 
 |       'a_test': {}, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_VARIANTS_MIXINS_MERGE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'set': { | 
 |         'mixins': [ 'test_mixin' ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           { | 
 |             'mixins': [ 'dimension_mixin' ], | 
 |           }, | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_VARIANTS_MIXINS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'set': { | 
 |         'mixins': [ 'test_mixin' ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           { | 
 |             'mixins': [ | 
 |               'dimension_mixin', | 
 |               'waterfall_mixin', | 
 |               'builder_mixin', | 
 |               'random_mixin' | 
 |             ], | 
 |           }, | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_VARIANTS_MIXINS_REMOVE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'set': { | 
 |         'remove_mixins': ['builder_mixin'], | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           { | 
 |             'mixins': [ 'builder_mixin' ], | 
 |           } | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_CONFLICTING_TEST_SUITES = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'bar_tests': { | 
 |       'baz_tests': { | 
 |         'args': [ | 
 |           '--bar', | 
 |         ], | 
 |       } | 
 |     }, | 
 |     'foo_tests': { | 
 |       'baz_tests': { | 
 |         'args': [ | 
 |           '--foo', | 
 |         ], | 
 |       } | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'bar_tests': { | 
 |         'variants': [ | 
 |           'a_variant', | 
 |         ], | 
 |       }, | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'a_variant', | 
 |         ] | 
 |       } | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_EMPTY_VARIANTS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'baz_tests': { | 
 |         'args': [ | 
 |           '--foo', | 
 |         ], | 
 |       } | 
 |     }, | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [], | 
 |       } | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_TARGETS_ARGS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'args_test': { | 
 |         'args': [ | 
 |           '--iam' | 
 |         ], | 
 |       }, | 
 |     } | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'an-arg', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | ARGS_VARIANTS_FILE = """\ | 
 | { | 
 |   'an-arg': { | 
 |     'identifier': 'args', | 
 |     'args': [ | 
 |       '--anarg', | 
 |     ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_TARGETS_MIXINS = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'mixins_test': { | 
 |         'mixins': [ 'test_mixin' ], | 
 |       }, | 
 |     } | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'mixins': [ 'random_mixin' ], | 
 |         'variants': [ | 
 |           'mixins', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MIXINS_VARIANTS_FILE = """\ | 
 | { | 
 |   'mixins': { | 
 |     'identifier': 'mixins', | 
 |     'mixins': [ 'dimension_mixin' ], | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_TARGETS_SWARMING = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'swarming_test': { | 
 |         'swarming': { | 
 |           'foo': 'bar', | 
 |           'dimensions': { | 
 |             'foo': 'bar', | 
 |           }, | 
 |         }, | 
 |       }, | 
 |     } | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'swarming-variant', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SWARMING_VARIANTS_FILE = """\ | 
 | { | 
 |   'swarming-variant': { | 
 |     'identifier': 'swarming', | 
 |     'swarming': { | 
 |       'a': 'b', | 
 |       'dimensions': { | 
 |         'hello': 'world', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_VARIANTS_REF = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'swarming_test': {}, | 
 |     } | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'a_variant' | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_VARIANTS_REF_WITH_DESCRIPTION = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'swarming_test': { | 
 |         'description': 'This is a swarming test.' | 
 |       }, | 
 |     } | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'a_variant' | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_TEST_WITH_TEST_KEY = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'swarming_test': { | 
 |           'test': 'foo_test_apk' | 
 |       }, | 
 |     } | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'a_variant', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_MIXED_VARIANTS_REF = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'swarming_test': {}, | 
 |     } | 
 |   }, | 
 |   'matrix_compound_suites': { | 
 |     'matrix_tests': { | 
 |       'foo_tests': { | 
 |         'variants': [ | 
 |           'a_variant', | 
 |           'b_variant', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | VARIANTS_FILE = """\ | 
 | { | 
 |   'a_variant': { | 
 |     'args': [ | 
 |       '--platform', | 
 |       'device', | 
 |       '--version', | 
 |       '1' | 
 |     ], | 
 |     'identifier': 'a_variant' | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | VARIANTS_FILE_WITH_DESCRIPTION = """\ | 
 | { | 
 |   'a_variant': { | 
 |     'identifier': 'a_variant', | 
 |     'description': 'Variant description.' | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | MULTI_VARIANTS_FILE = """\ | 
 | { | 
 |   'a_variant': { | 
 |     'args': [ | 
 |       '--platform', | 
 |       'device', | 
 |       '--version', | 
 |       '1' | 
 |     ], | 
 |     'identifier': 'a_variant' | 
 |   }, | 
 |   'b_variant': { | 
 |     'args': [ | 
 |       '--platform', | 
 |       'sim', | 
 |       '--version', | 
 |       '2' | 
 |     ], | 
 |     'identifier': 'b_variant' | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | # # Dictionary composition test suite outputs | 
 |  | 
 | EMPTY_SKYLAB_TEST_EXCEPTIONS = """\ | 
 | { | 
 |   'tast.foo OCTOPUS_TOT': { | 
 |     'remove_from': [ | 
 |       'Fake Tester', | 
 |     ] | 
 |   }, | 
 |   'tast.foo OCTOPUS_TOT-1': { | 
 |     'remove_from': [ | 
 |       'Fake Tester', | 
 |     ] | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_SKYLAB_WATERFALL_WITH_NO_CROS_BOARD = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'skylab_tests': 'cros_skylab_basic_x86', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | MATRIX_SKYLAB_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'skylab_tests': 'cros_skylab_basic_x86', | 
 |         }, | 
 |         'cros_board': 'octopus', | 
 |         'cros_dut_pool': 'chromium', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | MATRIX_SKYLAB_WATERFALL_WITH_BUILD_TARGET_VARIANT = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Fake Tester': { | 
 |         'test_suites': { | 
 |           'skylab_tests': 'cros_skylab_basic_x86', | 
 |         }, | 
 |         'cros_board': 'octopus', | 
 |         'cros_build_target': 'octopus-arc-t', | 
 |         'cros_dut_pool': 'chromium', | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_SKYLAB_TFC_REF = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'cros_skylab_basic': { | 
 |       'chrome_all_tast_tests': { | 
 |         'tast_expr': 'dummy expr', | 
 |         'timeout': 3600, | 
 |         'cros_test_tags': ['group:mainline'], | 
 |         'cros_test_tags_exclude': ['informational'], | 
 |         'cros_test_names_exclude_from_file': ['disabled.txt'], | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': {}, | 
 |   'matrix_compound_suites': { | 
 |     'cros_skylab_basic_x86': { | 
 |       'cros_skylab_basic': {}, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | MATRIX_COMPOUND_SKYLAB_REF = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'autotest_suites': { | 
 |       'autotest_suite': { | 
 |       }, | 
 |     }, | 
 |     'cros_skylab_basic': { | 
 |       'benchmark_suite': { | 
 |         'benchmark': 'something', | 
 |       }, | 
 |       'chrome_all_tast_tests': { | 
 |         'tast_expr': 'dummy expr', | 
 |         'timeout': 3600, | 
 |       }, | 
 |       'gtest_suite': { }, | 
 |       'lacros_all_tast_tests': { | 
 |         'tast_expr': 'lacros expr', | 
 |         'timeout': 3600, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': {}, | 
 |   'matrix_compound_suites': { | 
 |     'cros_skylab_basic_x86': { | 
 |       'autotest_suites': { | 
 |         'variants': [ | 
 |           'octopus-89-with-autotest-name', | 
 |         ], | 
 |       }, | 
 |       'cros_skylab_basic': { | 
 |         'tast_expr': 'dummy expr', | 
 |         'variants': [ | 
 |           'octopus-89', | 
 |           'octopus-88', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SKYLAB_VARIANTS = """\ | 
 | { | 
 |   'octopus-89': { | 
 |     'skylab': { | 
 |       'cros_board': 'octopus', | 
 |       'cros_model': 'casta', | 
 |       'cros_img': 'octopus-release/R89-13655.0.0', | 
 |     }, | 
 |     'enabled': True, | 
 |     'identifier': 'OCTOPUS_TOT', | 
 |   }, | 
 |   'octopus-89-with-autotest-name': { | 
 |     'skylab': { | 
 |       'cros_img': 'octopus-release/R89-13655.0.0', | 
 |       'autotest_name': 'unique_autotest_name', | 
 |     }, | 
 |     'enabled': True, | 
 |     'identifier': 'OCTOPUS_TOT', | 
 |   }, | 
 |   'octopus-88': { | 
 |     'skylab': { | 
 |       'cros_img': 'octopus-release/R88-13597.23.0', | 
 |     }, | 
 |     'enabled': True, | 
 |     'identifier': 'OCTOPUS_TOT-1', | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | SKYLAB_VARIANTS_WITH_BUILD_VARIANT = """\ | 
 | { | 
 |   'octopus-89': { | 
 |     'skylab': { | 
 |       'cros_board': 'octopus', | 
 |       'cros_model': 'casta', | 
 |       'cros_img': 'octopus-arc-t-release/R89-13655.0.0', | 
 |     }, | 
 |     'enabled': True, | 
 |     'identifier': 'OCTOPUS_TOT', | 
 |   }, | 
 |   'octopus-89-with-autotest-name': { | 
 |     'skylab': { | 
 |       'cros_img': 'octopus-arc-t-release/R89-13655.0.0', | 
 |       'autotest_name': 'unique_autotest_name', | 
 |     }, | 
 |     'enabled': True, | 
 |     'identifier': 'OCTOPUS_TOT', | 
 |   }, | 
 |   'octopus-88': { | 
 |     'skylab': { | 
 |       'cros_img': 'octopus-arc-t-release/R88-13597.23.0', | 
 |     }, | 
 |     'enabled': True, | 
 |     'identifier': 'OCTOPUS_TOT-1', | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | ENABLED_AND_DISABLED_MATRIX_COMPOUND_SKYLAB_REF = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'cros_skylab_basic': { | 
 |       'tast.basic': { | 
 |         'skylab': { | 
 |           'tast_expr': 'dummy expr', | 
 |           'shard_level_retries_on_ctp': 2, | 
 |         }, | 
 |         'suite': 'tast.basic', | 
 |         'timeout': 3600, | 
 |       }, | 
 |       'tast.foo': { | 
 |         'skylab': { | 
 |           'tast_expr': 'dummy expr', | 
 |         }, | 
 |         'suite': 'tast.foo', | 
 |         'timeout': 3600, | 
 |       }, | 
 |     }, | 
 |   }, | 
 |   'compound_suites': {}, | 
 |   'matrix_compound_suites': { | 
 |     'cros_skylab_basic_x86': { | 
 |       'cros_skylab_basic': { | 
 |         'variants': [ | 
 |           'enabled', | 
 |           'disabled', | 
 |         ], | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | ENABLED_AND_DISABLED_VARIANTS = """\ | 
 | { | 
 |   'enabled': { | 
 |     'skylab': { | 
 |       'cros_board': 'octopus', | 
 |       'cros_img': 'octopus-release/R89-13655.0.0', | 
 |     }, | 
 |     'enabled': True, | 
 |     'identifier': 'OCTOPUS_TOT', | 
 |   }, | 
 |   'disabled': { | 
 |     'skylab': { | 
 |       'cros_board': 'octopus', | 
 |       'cros_img': 'octopus-release/R88-13597.23.0', | 
 |     }, | 
 |     'enabled': False, | 
 |     'identifier': 'OCTOPUS_TOT-1', | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | class MatrixCompositionTests(TestCase): | 
 |  | 
 |   def test_good_structure_no_configs(self): | 
 |     """ | 
 |     Tests matrix compound test suite structure with no configs, | 
 |     no conflicts and no bad references | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_EMPTY, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_good_structure_with_description(self): | 
 |     """ | 
 |     Tests matrix compound test suite structure with description. | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_EMPTY_WITH_DESCRIPTION, LUCI_MILO_CFG) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_missing_identifier(self): | 
 |     """ | 
 |     Variant is missing an identifier | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_MISSING_IDENTIFIER, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=VARIANTS_FILE_MISSING_IDENTIFIER) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'Missing required identifier field in matrix compound suite*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_empty_identifier(self): | 
 |     """ | 
 |     Variant identifier is empty. | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_EMPTY_IDENTIFIER, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=EMPTY_IDENTIFIER_VARIANTS) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'Identifier field can not be "" in matrix compound suite*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_trailing_identifier(self): | 
 |     """ | 
 |     Variant identifier has trailing whitespace. | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_TRAILING_IDENTIFIER, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=TRAILING_WHITESPACE_VARIANTS) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'Identifier field can not have leading and trailing whitespace in' | 
 |         ' matrix compound suite*'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_noexistent_ref(self): | 
 |     """ | 
 |     Test referencing a non-existent basic test suite | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_REF_NONEXISTENT, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Unable to find reference to *'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_ref_to_composition(self): | 
 |     """ | 
 |     Test referencing another composition test suite | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_REF_COMPOSITION, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'matrix_compound_suites may not refer to other *'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_ref_to_matrix(self): | 
 |     """ | 
 |     Test referencing another matrix test suite | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOSITION_REF_MATRIX, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'matrix_compound_suites may not refer to other *'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_conflicting_names(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_CONFLICTING_TEST_SUITES, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=VARIANTS_FILE) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Conflicting test definitions.*'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_empty_variants(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_EMPTY_VARIANTS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_variants_swarming_dict_merge_args(self): | 
 |     """ | 
 |     Test targets with swarming dictionary defined by both basic and matrix | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_TARGETS_ARGS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS, | 
 |                     variants=ARGS_VARIANTS_FILE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_variants_swarming_dict_merge_mixins(self): | 
 |     """ | 
 |     Test targets with swarming dictionary defined by both basic and matrix | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_TARGETS_MIXINS, | 
 |                     LUCI_MILO_CFG, | 
 |                     mixins=SWARMING_MIXINS, | 
 |                     variants=MIXINS_VARIANTS_FILE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_variants_swarming_dict_swarming(self): | 
 |     """ | 
 |     Test targets with swarming dictionary defined by both basic and matrix | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_TARGETS_SWARMING, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=SWARMING_VARIANTS_FILE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_variant_test_suite_with_test_key(self): | 
 |     """ | 
 |     Test targets in matrix compound test suites with variants | 
 |     """ | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_TEST_WITH_TEST_KEY, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=VARIANTS_FILE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_variants_with_description(self): | 
 |     """Test variants with description field""" | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_VARIANTS_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=VARIANTS_FILE_WITH_DESCRIPTION) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_both_test_suite_and_variants_with_description(self): | 
 |     """Test both test suite and variants with description field""" | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_VARIANTS_REF_WITH_DESCRIPTION, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=VARIANTS_FILE_WITH_DESCRIPTION) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_variants_pyl_ref(self): | 
 |     """Test targets with variants string ref""" | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_VARIANTS_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=VARIANTS_FILE) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_variants_pyl_no_ref(self): | 
 |     """Test targets with variants string ref, not defined in variants.pyl""" | 
 |     fbb = FakeBBGen(self.args, MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_VARIANTS_REF, LUCI_MILO_CFG) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'Missing variant definition for *'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |  | 
 |   def test_variants_pyl_all_unreferenced(self): | 
 |     """Test targets with variants in variants.pyl, unreferenced in tests""" | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_GTEST_SUITE_WATERFALL, | 
 |                     MATRIX_COMPOUND_VARIANTS_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     variants=MULTI_VARIANTS_FILE) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'The following variants were unreferenced *'): | 
 |       fbb.check_input_file_consistency(verbose=True) | 
 |  | 
 |   def test_good_skylab_matrix_with_variants(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_SKYLAB_WATERFALL, | 
 |                     MATRIX_COMPOUND_SKYLAB_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EMPTY_SKYLAB_TEST_EXCEPTIONS, | 
 |                     variants=SKYLAB_VARIANTS) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_good_skylab_matrix_with_tfc(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_SKYLAB_WATERFALL, | 
 |                     MATRIX_COMPOUND_SKYLAB_TFC_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EMPTY_SKYLAB_TEST_EXCEPTIONS) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_good_skylab_matrix_with_build_target_variant_and_variants(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_SKYLAB_WATERFALL_WITH_BUILD_TARGET_VARIANT, | 
 |                     MATRIX_COMPOUND_SKYLAB_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EMPTY_SKYLAB_TEST_EXCEPTIONS, | 
 |                     variants=SKYLAB_VARIANTS_WITH_BUILD_VARIANT) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_enabled_and_disabled_skylab_matrix_with_variants(self): | 
 |     """Test with disabled variants""" | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_SKYLAB_WATERFALL, | 
 |                     ENABLED_AND_DISABLED_MATRIX_COMPOUND_SKYLAB_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EMPTY_SKYLAB_TEST_EXCEPTIONS, | 
 |                     variants=ENABLED_AND_DISABLED_VARIANTS) | 
 |     # some skylab test variant is disabled; the corresponding skylab tests | 
 |     # is not generated. | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_invalid_skylab_matrix_with_variants(self): | 
 |     fbb = FakeBBGen(self.args, | 
 |                     MATRIX_SKYLAB_WATERFALL_WITH_NO_CROS_BOARD, | 
 |                     MATRIX_COMPOUND_SKYLAB_REF, | 
 |                     LUCI_MILO_CFG, | 
 |                     exceptions=EMPTY_SKYLAB_TEST_EXCEPTIONS, | 
 |                     variants=SKYLAB_VARIANTS) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'skylab tests must specify cros_board.'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |  | 
 | MAC_TEST_SUITE = """\ | 
 | { | 
 |   'basic_suites': { | 
 |     'foo_tests': { | 
 |       'foo_test': { | 
 |       }, | 
 |     }, | 
 |   }, | 
 | } | 
 | """ | 
 |  | 
 | NO_DIMENSIONS_GTESTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Mac': { | 
 |         'swarming': {}, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | NO_OS_GTESTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Mac': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'foo': 'bar', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'gtest_tests': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | MAC_ISOLATED_SCRIPTS_WATERFALL = """\ | 
 | [ | 
 |   { | 
 |     'project': 'chromium', | 
 |     'bucket': 'ci', | 
 |     'name': 'chromium.test', | 
 |     'machines': { | 
 |       'Mac': { | 
 |         'swarming': { | 
 |           'dimensions': { | 
 |             'os': 'Mac', | 
 |           }, | 
 |         }, | 
 |         'test_suites': { | 
 |           'isolated_scripts': 'foo_tests', | 
 |         }, | 
 |       }, | 
 |     }, | 
 |   }, | 
 | ] | 
 | """ | 
 |  | 
 | MAC_LUCI_MILO_CFG = """\ | 
 | consoles { | 
 |   builders { | 
 |     name: "buildbucket/luci.chromium.ci/Mac" | 
 |   } | 
 | } | 
 | """ | 
 |  | 
 |  | 
 | class SwarmingTests(TestCase): | 
 |   def test_builder_with_no_dimension_fails(self): | 
 |     fbb = FakeBBGen(self.args, NO_DIMENSIONS_GTESTS_WATERFALL, MAC_TEST_SUITE, | 
 |                     MAC_LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     with self.assertRaisesRegex( | 
 |         generate_buildbot_json.BBGenErr, | 
 |         'dimensions must be specified for all swarmed tests'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_builder_with_no_os_dimension_fails(self): | 
 |     fbb = FakeBBGen(self.args, NO_OS_GTESTS_WATERFALL, MAC_TEST_SUITE, | 
 |                     MAC_LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'os must be specified for all swarmed tests'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |   def test_mac_builder_with_no_cpu_dimension_in_isolated_script_fails(self): | 
 |     fbb = FakeBBGen(self.args, MAC_ISOLATED_SCRIPTS_WATERFALL, MAC_TEST_SUITE, | 
 |                     MAC_LUCI_MILO_CFG) | 
 |     fbb.check_input_file_consistency(verbose=True) | 
 |     with self.assertRaisesRegex(generate_buildbot_json.BBGenErr, | 
 |                                 'cpu must be specified for mac'): | 
 |       fbb.check_output_file_consistency(verbose=True) | 
 |     self.assertFalse(fbb.printed_lines) | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   unittest.main() |