#!/usr/bin/env python # # Copyright (C) 2020-2021 Apple Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import json import os import unittest import loadConfig class ConfigDotJSONTest(unittest.TestCase): def get_config(self): cwd = os.path.dirname(os.path.abspath(__file__)) return json.load(open(os.path.join(cwd, 'config.json'))) def test_configuration(self): cwd = os.path.dirname(os.path.abspath(__file__)) loadConfig.loadBuilderConfig({}, is_test_mode_enabled=True, master_prefix_path=cwd) def test_tab_character(self): cwd = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(cwd, 'config.json'), 'r') as config: self.assertTrue('\t' not in config.read(), 'Tab character found in config.json, please use spaces instead of tabs.') def test_builder_keys(self): config = self.get_config() valid_builder_keys = ['additionalArguments', 'architectures', 'builddir', 'configuration', 'description', 'defaultProperties', 'device_model', 'env', 'factory', 'icon', 'locks', 'name', 'platform', 'properties', 'remotes', 'runTests', 'shortname', 'tags', 'triggers', 'workernames', 'workerbuilddir'] for builder in config.get('builders', []): for key in builder: self.assertTrue(key in valid_builder_keys, 'Unexpected key "{}" for builder {}'.format(key, builder.get('name'))) def test_multiple_scheduers_for_builder(self): config = self.get_config() builder_to_schduler_map = {} for scheduler in config.get('schedulers'): for buildername in scheduler.get('builderNames'): self.assertTrue(buildername not in builder_to_schduler_map, 'builder {} appears multiple times in schedulers.'.format(buildername)) builder_to_schduler_map[buildername] = scheduler.get('name') def test_schduler_contains_valid_builder_name(self): config = self.get_config() builder_name_list = [builder['name'] for builder in config['builders']] for scheduler in config.get('schedulers'): for buildername in scheduler.get('builderNames'): self.assertTrue(buildername in builder_name_list, 'builder "{}" in scheduler "{}" is invalid.'.format(buildername, scheduler.get('name'))) def test_single_builder_for_triggerable_scheduler(self): config = self.get_config() for scheduler in config['schedulers']: if scheduler.get('type') == 'Triggerable': self.assertTrue(len(scheduler.get('builderNames')) == 1, 'scheduler "{}" triggers multiple builders.'.format(scheduler['name'])) class TagsForBuilderTest(unittest.TestCase): def verifyTags(self, builderName, expectedTags): tags = loadConfig.getTagsForBuilder({'name': builderName}) self.assertEqual(sorted(tags), sorted(expectedTags)) def test_getTagsForBuilder(self): self.verifyTags('EWS', []) self.verifyTags('TryBot-10-EWS', []) self.verifyTags('11-EWS', []) self.verifyTags('32-EWS', ['32']) self.verifyTags('iOS-11-EWS', ['iOS']) self.verifyTags('iOS(11),(test)-EWS', ['iOS', 'test']) self.verifyTags('Windows-EWS', ['Windows']) self.verifyTags('Windows_Windows', ['Windows']) self.verifyTags('GTK-Build-EWS', ['GTK', 'Build']) self.verifyTags('GTK-WK2-Tests-EWS', ['GTK', 'WK2', 'Tests']) self.verifyTags('macOS-Sierra-Release-WK1-EWS', ['Sierra', 'Release', 'macOS', 'WK1']) self.verifyTags('macOS-High-Sierra-Release-32bit-WK2-EWS', ['macOS', 'High', 'Sierra', 'Release', 'WK2', '32bit']) def test_tags_type(self): tags = loadConfig.getTagsForBuilder({'name': 'iOS-11-EWS'}) self.assertEqual(tags, ['iOS']) self.assertEqual(type(tags[0]), str) def test_getInvalidTags(self): invalidTags = loadConfig.getInvalidTags() expectedTags = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', 'EWS', 'TryBot'] self.assertEqual(invalidTags, expectedTags) class TestcheckValidWorker(unittest.TestCase): def test_invalid_worker(self): with self.assertRaises(Exception) as context: loadConfig.checkValidWorker({}) self.assertEqual(context.exception.args, ('Worker is None or Empty.',)) def test_worker_with_missing_name(self): with self.assertRaises(Exception) as context: loadConfig.checkValidWorker({'platform': 'mac-sierra'}) self.assertEqual(context.exception.args, ('Worker "{\'platform\': \'mac-sierra\'}" does not have name defined.',)) def test_worker_with_missing_platName(self): with self.assertRaises(Exception) as context: loadConfig.checkValidWorker({'name': 'ews101'}) self.assertEqual(context.exception.args, ('Worker ews101 does not have platform defined.',)) def test_valid_worker(self): loadConfig.checkValidWorker({'name': 'ews101', 'platform': 'mac-sierra'}) class TestcheckValidBuilder(unittest.TestCase): def test_invalid_builder(self): with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {}) self.assertEqual(context.exception.args, ('Builder is None or Empty.',)) def test_builder_with_missing_name(self): with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {'platform': 'mac-sierra'}) self.assertEqual(context.exception.args, ('Builder "{\'platform\': \'mac-sierra\'}" does not have name defined.',)) def test_builder_with_invalid_identifier(self): with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {'name': 'mac-wk2(test)', 'shortname': 'mac-wk2'}) self.assertEqual(context.exception.args, ('Builder name mac-wk2(test) is not a valid buildbot identifier.',)) def test_builder_with_extra_long_name(self): longName = 'a' * 71 with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {'name': longName, 'shortname': 'a'}) self.assertEqual(context.exception.args, ('Builder name {} is longer than maximum allowed by Buildbot (70 characters).'.format(longName),)) def test_builder_with_invalid_configuration(self): with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {'name': 'mac-wk2', 'shortname': 'mac-wk2', 'configuration': 'asan'}) self.assertEqual(context.exception.args, ('Invalid configuration: asan for builder: mac-wk2',)) def test_builder_with_missing_factory(self): with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {'name': 'mac-wk2', 'shortname': 'mac-wk2', 'configuration': 'release'}) self.assertEqual(context.exception.args, ('Builder mac-wk2 does not have factory defined.',)) def test_builder_with_missing_scheduler(self): with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {'name': 'mac-wk2', 'shortname': 'mac-wk2', 'configuration': 'release', 'factory': 'WK2Factory', 'platform': 'mac-sierra', 'triggers': ['api-tests-mac-ews']}) self.assertEqual(context.exception.args, ('Trigger: api-tests-mac-ews in builder mac-wk2 does not exist in list of Trigerrable schedulers.',)) def test_builder_with_missing_platform(self): with self.assertRaises(Exception) as context: loadConfig.checkValidBuilder({}, {'name': 'mac-wk2', 'shortname': 'mac-wk2', 'configuration': 'release', 'factory': 'WK2Factory'}) self.assertEqual(context.exception.args, ('Builder mac-wk2 does not have platform defined.',)) def test_valid_builder(self): loadConfig.checkValidBuilder({}, {'name': 'macOS-High-Sierra-WK2-EWS', 'shortname': 'mac-wk2', 'configuration': 'release', 'factory': 'WK2Factory', 'platform': 'mac-sierra'}) class TestcheckWorkersAndBuildersForConsistency(unittest.TestCase): def __init__(self, *args, **kwargs): self.WK2Builder = {'name': 'macOS-High-Sierra-WK2-EWS', 'shortname': 'mac-wk2', 'factory': 'WK2Factory', 'platform': 'mac-sierra', 'workernames': ['ews101', 'ews102']} self.ews101 = {'name': 'ews101', 'platform': 'mac-sierra'} self.ews102 = {'name': 'ews102', 'platform': 'ios-11'} super(TestcheckWorkersAndBuildersForConsistency, self).__init__(*args, **kwargs) def test_checkWorkersAndBuildersForConsistency(self): with self.assertRaises(Exception) as context: loadConfig.checkWorkersAndBuildersForConsistency({}, [], [self.WK2Builder]) self.assertEqual(context.exception.args, ('Builder macOS-High-Sierra-WK2-EWS has worker ews101, which is not defined in workers list!',)) def test_checkWorkersAndBuildersForConsistency1(self): with self.assertRaises(Exception) as context: loadConfig.checkWorkersAndBuildersForConsistency({}, [self.ews101, self.ews102], [self.WK2Builder]) self.assertEqual(context.exception.args, ('Builder "macOS-High-Sierra-WK2-EWS" is for platform "mac-sierra", but has worker "ews102" for platform "ios-11"!',)) def test_duplicate_worker(self): with self.assertRaises(Exception) as context: loadConfig.checkWorkersAndBuildersForConsistency({}, [self.ews101, self.ews101], [self.WK2Builder]) self.assertEqual(context.exception.args, ('Duplicate worker entry found for ews101.',)) def test_success(self): loadConfig.checkWorkersAndBuildersForConsistency({}, [self.ews101, {'name': 'ews102', 'platform': 'mac-sierra'}], [self.WK2Builder]) if __name__ == '__main__': from steps_unittest_old import BuildBotConfigLoader BuildBotConfigLoader()._add_dependent_modules_to_sys_modules() import loadConfig unittest.main()