blob: 726de1ada1462f344c6cce458e2ab3f5686f5d3e [file]
"""Tests for config."""
import os
import sys
import unittest
# Allow imports to be rooted in continuous-tests/ folder
sys.path.insert(
0,
os.path.realpath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)))
# pylint: disable=wrong-import-position
from cq.appengine.genfiles import build_config_pb2
import google.protobuf.text_format
_BUILD_RULES_RELPATH = 'configs/{env}/build_rules.protoascii'
_BUILD_DETAILS_RELPATH = 'configs/{env}/build_details.protoascii'
def GetBuildRules(build_config_env):
"""Returns the BuildRules proto."""
build_rules = build_config_pb2.BuildRules()
build_rules_path = os.path.join(
os.path.dirname(__file__),
_BUILD_RULES_RELPATH.format(env=build_config_env))
with open(build_rules_path) as f:
google.protobuf.text_format.Merge(f.read(), build_rules)
return build_rules
def GetBuildDetails(build_config_env):
"""Returns the BuildDetails proto."""
build_details = build_config_pb2.BuildDetails()
build_details_path = os.path.join(
os.path.dirname(__file__),
_BUILD_DETAILS_RELPATH.format(env=build_config_env))
with open(build_details_path) as f:
google.protobuf.text_format.Merge(f.read(), build_details)
return build_details
# Use a wrapping class so the test runner does not mistakenly try to run
# tests on ConfigTest, which is not a fully-implemented test class
class BaseTestCases:
class ConfigTest(unittest.TestCase):
"""A class to test a build config.
Note: The |self.env| property is initialized externally to this class. This
class will be setup twice, once with the staging config and once with the
prod config.
"""
env = None
@property
def build_rules_proto(self):
return GetBuildRules(self.env)
@property
def build_details_proto(self):
return GetBuildDetails(self.env)
def testParseBuildRules(self):
"""Tests the build_rules config is a valid proto."""
self.assertTrue(self.build_rules_proto)
def testParseBuildDetails(self):
"""Tests the build_details config is a valid proto."""
self.assertTrue(self.build_details_proto)
def testOnlySupportedGobHosts(self):
"""Tests that only supported gob_hosts are used."""
supported_hosts = {
'caraway-internal',
'chrome-internal',
'eureka-internal',
'eureka-partner',
'eureka-staging-internal',
'libassistant-internal',
'nest-camera-internal',
'nest-internal',
'team',
}
for rule in self.build_rules_proto.build_rules:
self.assertIn(rule.gob_host, supported_hosts)
def testEveryBuildRuleHasAMatchingBuildDetail(self):
"""Test that every build in a build_rule has a matching build_detail."""
referenced_builds = set()
for rule in self.build_rules_proto.build_rules:
referenced_builds.update(rule.builds)
referenced_builds.update(rule.presubmit_builds)
for build_set in rule.build_sets:
referenced_builds.update(
self.build_rules_proto.build_sets[build_set].builds)
build_detail_builds = set()
for build_name in self.build_details_proto.build_details:
build_detail_builds.add(build_name)
# Note that it is acceptable for there to be extra build_details, since
# that is how a build is enabled for manually scheduling.
missing_build_details = referenced_builds - build_detail_builds
self.assertFalse(missing_build_details)
def testScheduleValid(self):
for build, detail in self.build_details_proto.build_details.items():
if detail.schedule.min_branch and detail.schedule.max_branch:
self.assertLess(
detail.schedule.min_branch, detail.schedule.max_branch,
'Branch range ({} < {}) for {} is invalid'.format(
detail.schedule.min_branch, detail.schedule.max_branch, build
)
)
if detail.schedule.master_only and detail.schedule.release_only:
raise AssertionError(
'Can only set master only or release only for {}'.format(build)
)
def testHasRequiredFields(self):
for name, detail in self.build_details_proto.build_details.items():
self.assertTrue(
detail.HasField('config'),
f'{name} must have oneof field config set.')
which_config = detail.WhichOneof('config')
if which_config == 'device_tests':
self.assertGreater(
len(detail.device_tests.test_groups),
0,
f'{name} must have at least one test_group')
self.assertTrue(
detail.device_tests.HasField('depends_on'),
f'{name} must have a depends_on set')
self.assertGreater(
len(detail.device_tests.depends_on),
0,
f'{name} must have a non-empty depends_on')
elif which_config == 'cast_build':
self.assertTrue(
detail.cast_build.HasField('recipe'),
f'{name} must have a recipe set')
self.assertGreater(
len(detail.cast_build.recipe),
0,
f'{name} must have a non-empty recipe')
elif which_config == 'jenkins_job':
self.assertTrue(
detail.jenkins_job.HasField('job_name'),
f'{name} must have a job_name set')
self.assertGreater(
len(detail.jenkins_job.job_name),
0,
f'{name} must have a non-empty job_name')
class ProdConfigTest(BaseTestCases.ConfigTest):
env = "prod"
if __name__ == '__main__':
unittest.main()