class BuiltModelAreComplete(TestCase): def setUp(self): self._codec = YAML() def _assertItemsEqual(self, expected, actual): # For compatibility with Python 2.7, as the method # assertItemsEquals has been renamed in Python 3.3 into # 'assertCountEqual'. return self.assertEqual(sorted(expected), sorted(actual)) def test_given_a_one_component_stack(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": {} } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_one_component_stack_with_two_variables(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " type: Integer\n" " values: [1GB, 2GB]\n" " threads:\n" " type: Integer\n" " values: [64, 128, 256]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": { "threads": { "values": [64, 128, 256], "realization": [] }, "memory": { "values": ["1GB", "2GB"], "realization": [] } } } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_two_component_stack(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " requires_features: [ Python27 ]\n" " python:\n" " provides_features: [ Python27 ]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": ["Python27"], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": ["Python27"], "implementation": None, "variables": {} }, "python": { "provided_services": [], "required_services": [], "provided_features": ["Python27"], "required_features": [], "implementation": None, "variables": {} } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_docker_file(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker:\n" " file: server/Dockerfile\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": DockerFile("server/Dockerfile"), "variables": {} } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_a_docker_image(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker:\n" " image: fchauvel/camp:dev\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": DockerImage("fchauvel/camp:dev"), "variables": {} } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_a_variable_with_substitution(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " type: Text\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - targets: [ file1, path/to/file2 ]\n" " pattern: xmem=1GB\n" " replacements: [xmem=1, xmem=2, xmem=4]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": { "memory": { "values": ["1GB", "2GB", "4GB"], "realization": [ Substitution( targets=["file1", "path/to/file2"], pattern="xmem=1GB", replacements=[ "xmem=1", "xmem=2", "xmem=4" ]) ] } } } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_a_variable_with_resource_selection(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " type: Text\n" " values: [apache, nginx]\n" " realization:\n" " - select: \n" " - apache_config.ini\n" " - nginx_config.ini\n" " as: config.ini\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": { "memory": { "values": ["apache", "nginx"], "realization": [ ResourceSelection("config.ini", [ "apache_config.ini", "nginx_config.ini" ]) ] } } } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_containing_tests(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " command: mvn -B test -gs ./settings.xml\n" " reports:\n" " format: JUnit\n" " location: target/surefire-reports\n" " pattern: TEST*.xml\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": {}, "tests": TestSettings("mvn -B test -gs ./settings.xml", "JUnit", "target/surefire-reports", "TEST*.xml") } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_realization(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " realization:\n" " - select: apache_docker-compose.yml\n" " instead_of:\n" " - nginx_docker-compose.yml\n" " as: docker-compose.yml\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components": { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": {}, "realization": [ ComponentResourceSelection( "apache_docker-compose.yml", ["nginx_docker-compose.yml"], "docker-compose.yml") ] } }, "goals": { "services": ["Wonderful"], "features": [] } }) def assert_complete(self, text, expectations): model = self._codec.load_model_from(StringIO(text)) self.assertEqual(0, len(self._codec.warnings)) self.assert_goals(model.goals, expectations["goals"]) self._assertItemsEqual(expectations["services"], [each.name for each in model.services]) self._assertItemsEqual(expectations["features"], [each.name for each in model.features]) self.assertEqual(set([each.name for each in model.components]), set(expectations["components"].keys())) for each_component in model.components: expectation = expectations["components"][each_component.name] self.assert_component(each_component, expectation) def assert_goals(self, goals, expectations): self._assertItemsEqual(expectations["services"], [each.name for each in goals.services]) self._assertItemsEqual(expectations["features"], [each.name for each in goals.features]) def assert_component(self, component, expectation): self._assertItemsEqual( expectation["provided_services"], [each.name for each in component.provided_services]) self._assertItemsEqual( expectation["required_services"], [each.name for each in component.required_services]) self._assertItemsEqual( expectation["provided_features"], [each.name for each in component.provided_features]) self._assertItemsEqual( expectation["required_features"], [each.name for each in component.required_features]) self.assertEqual(expectation["implementation"], component.implementation) self.assert_variables(component, expectation["variables"]) if "tests" in expectation: self.assertEqual(expectation["tests"], component.test_settings) if "realization" in expectation: self._assertItemsEqual(expectation["realization"], component.realization) def assert_variables(self, component, variables): self.assertEqual(len(variables), len(component.variables)) for name, variable in variables.items(): match = next((variable for variable in component.variables\ if variable.name == name), None) if match: self._assertItemsEqual(match.domain, variable["values"]) self._assertItemsEqual(match.realization, variable["realization"]) else: self.fail("Component '%s' lacks variable '%s'." % (component.name, name))
class TypeMismatchesAreNotReportedWhenStringIsExpected(TestCase): def setUp(self): self._codec = YAML() def assert_no_warning_in(self, text): self._codec.load_model_from(StringIO(text)) self.assertEqual(0, len(self._codec.warnings)) def test_with_a_boolean_among_running_items(self): self.assert_no_warning_in("components:\n" " server:\n" " provides_services: [ Awesome ]\n" "goals:\n" " running: [ Awesome, True ]\n") def test_with_a_number_among_provided_services(self): self.assert_no_warning_in( "components:\n" " server:\n" " provides_services: [ Awesome, 1234.5 ]\n" "goals:\n" " running: [ Awesome ]\n") def test_with_a_number_among_required_services(self): self.assert_no_warning_in( "components:\n" " server:\n" " requires_services: [ Awesome, 1234.5 ]\n" "goals:\n" " running: [ Awesome ]\n") def test_with_a_number_among_provided_features(self): self.assert_no_warning_in( "components:\n" " server:\n" " provides_features: [ Awesome, 1234.5 ]\n" "goals:\n" " running: [ Awesome ]\n") def test_with_a_number_among_required_features(self): self.assert_no_warning_in( "components:\n" " server:\n" " requires_features: [ Awesome, 1234.5 ]\n" "goals:\n" " running: [ Awesome ]\n") def test_with_a_number_among_variable_values(self): self.assert_no_warning_in("components:\n" " server:\n" " requires_services: [ Awesome ]\n" " variables:\n" " memory:\n" " values: [ High, 1234]\n" "goals:\n" " running: [ Awesome ]\n") def test_with_a_number_among_docker_file(self): self.assert_no_warning_in("components:\n" " server:\n" " requires_services: [ Awesome ]\n" " implementation:\n" " docker:\n" " file: 1234.5\n" "goals:\n" " running: [ Awesome ]\n") def test_with_a_number_among_docker_image(self): self.assert_no_warning_in("components:\n" " server:\n" " requires_services: [ Awesome ]\n" " implementation:\n" " docker:\n" " image: 1234.5\n" "goals:\n" " running: [ Awesome ]\n")
class MissingMandatoryEntriesAreReported(TestCase): def setUp(self): self._codec = YAML() def _assertItemsEqual(self, expected, actual): # For compatibility with Python 2.7, as the method # assertItemsEquals has been renamed in Python 3.3 into # 'assertCountEqual'. return self.assertEqual(sorted(expected), sorted(actual)) def test_when_omitting_substitution_targets(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - pattern: xmem=1GB\n" " replacements: [xmem=1, xmem=2, xmem=4]\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/variables/memory/realization/#1", candidates=["targets"]) def test_when_omitting_substitution_pattern(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - targets: [ Dockerfile ]\n" " replacements: [xmem=1, xmem=2, xmem=4]\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/variables/memory/realization/#1", candidates=["pattern"]) def test_when_omitting_substitution_replacements(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - targets: [ Dockerfile ]\n" " pattern: xmem=1GB\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/variables/memory/realization/#1", candidates=["replacements"]) def test_when_omitting_the_docker_file(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker: {}\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/implementation/docker", candidates=["file", "image"]) def test_when_omitting_the_testing_commands(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " # Missing: command: mvn -B test -gs ./settings.xml\n" " reports:\n" " format: JUnit\n" " location: target/surefire-reports\n" " pattern: TEST*.xml\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/tests", candidates=["command"]) def test_when_omitting_test_report_format(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " command: mvn -B test -gs ./settings.xml\n" " reports:\n" " # Missing: format: JUnit\n" " location: target/surefire-reports\n" " pattern: TEST*.xml\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/tests/reports", candidates=["format"]) def test_when_omitting_test_report_location(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " command: mvn -B test -gs ./settings.xml\n" " reports:\n" " format: JUnit\n" " # Missing: location: target/surefire-reports\n" " pattern: TEST*.xml\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/tests/reports", candidates=["location"]) def test_when_omitting_test_report_pattern(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " command: mvn -B test -gs ./settings.xml\n" " reports:\n" " format: JUnit\n" " location: target/surefire-reports\n" " # Missing: pattern: TEST*.xml\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/tests/reports", candidates=["pattern"]) def test_when_omitting_component_realisation_discarded_resources(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " realization:\n" " - select: apache_docker-compose-yml\n" " # Missing: instead_of: \n" " # - nginx_docker-compose.yml\n" " as: docker-compose.yml\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/realization/#1", candidates=["instead_of"]) def test_when_omitting_component_realisation_selected_resource(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " realization:\n" " - # Missing select: apache_docker-compose-yml\n" " instead_of: \n" " - nginx_docker-compose.yml\n" " as: docker-compose.yml\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/realization/#1", candidates=["select"]) def assert_missing(self, text, path, candidates): try: self._codec.load_model_from(StringIO(text)) self.fail("InvalidYAMLModel should have been thrown!") except InvalidYAMLModel as error: self.assertEqual(1, len(error.warnings)) self.assertEqual(path, error.warnings[0].path) self._assertItemsEqual(candidates, error.warnings[0].candidates)
class TypeMismatchAreReported(TestCase): def setUp(self): self._codec = YAML() def test_with_a_string_as_component(self): self.assert_warning( "components: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="dict", found="str", path="components") def test_with_a_string_as_provided_services(self): self.assert_warning( "components: \n" " server:\n" " provides_services: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/provides_services") def test_with_a_string_as_required_services(self): self.assert_warning( "components: \n" " server:\n" " requires_services: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/requires_services") def test_with_a_string_as_provided_features(self): self.assert_warning( "components: \n" " server:\n" " provides_features: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/provides_features") def test_with_a_string_as_required_features(self): self.assert_warning( "components: \n" " server:\n" " requires_features: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/requires_features") def test_with_a_string_as_variables(self): self.assert_warning( "components: \n" " server:\n" " requires_features: [ Awesome ]\n" " variables: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="dict", found="str", path="components/server/variables") def test_with_a_string_as_implementation(self): self.assert_warning( "components: \n" " server:\n" " requires_features: [ Awesome ]\n" " implementation: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="dict", found="str", path="components/server/implementation") def test_with_a_string_as_goals(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" "goals: blablabla\n", expected="dict", found="str", path="goals") def test_with_a_string_as_running(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" "goals:\n" " running: blablabla\n", expected="list", found="str", path="goals/running") def test_with_a_string_as_substitution_replacements(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB ]\n" " realization:\n" " - targets: [ Dockerfile ]\n" " pattern: xmem=1GB\n" " replacements: This should not be a string!\n" "goals:\n" " running: [ Awesome ]\n", expected="list", found="str", path= "components/server/variables/memory/realization/#1/replacements", warning_count=2) def test_with_a_string_as_substitution_targets(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB ]\n" " realization:\n" " - targets: This should not be a string!\n" " pattern: xmem=1GB\n" " replacements: [xmem=1GB, xmem=2GB]\n" "goals:\n" " running: [ Awesome ]\n", expected="list", found="str", path="components/server/variables/memory/realization/#1/targets", warning_count=2) def test_with_a_number_as_selected_resource(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB ]\n" " realization:\n" " - select: 123\n" " as: file.txt\n" "goals:\n" " running: [ Awesome ]\n", expected="list", found="int", path="components/server/variables/memory/realization/#1/select", warning_count=2) def test_with_a_number_as_selected_resource_final_name(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB ]\n" " realization:\n" " - select: [\"a.txt\", \"b.txt\"]\n" " as: 123\n" "goals:\n" " running: [ Awesome ]\n", expected="str", found="int", path="components/server/variables/memory/realization/#1/as", warning_count=1) def test_with_a_number_as_component_resource_alternatives(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " realization:\n" " - select: apache_docker-compose-yml\n" " instead_of: 1234\n" " as: docker-compose.yml\n" "goals:\n" " running: [ Wonderful ]\n", expected="list", found="int", path="components/server/realization/#1/instead_of", warning_count=2) def test_with_a_number_as_component_specific_resource_alternative(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " realization:\n" " - select: apache_docker-compose-yml\n" " instead_of: \n" " - nginx_docker-compose.yml\n" " - 2\n" " as: docker-compose.yml\n" "goals:\n" " running: [ Wonderful ]\n", expected="str", found="int", path="components/server/realization/#1/instead_of/#2", warning_count=1) def test_with_a_number_as_liveness_check(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " command: echo \"Run tests!\"\n" " liveness_test: 123\n" " reports:\n" " format: JUnitnit\n" " location: this/nice/directory\n" " pattern: \"*.ini\"\n" "goals:\n" " running: [ Wonderful ]\n", expected="str", found="int", path="components/server/tests/liveness_test", warning_count=1) def assert_warning(self, text, expected, found, path, warning_count=1): try: self._codec.load_model_from(StringIO(text)) self.fail("InvalidYAMLModel should have been thrown!") except InvalidYAMLModel as error: self.assertEqual(warning_count, len(error.warnings), [str(w) for w in error.warnings]) self.assertEqual(path, error.warnings[0].path) self.assertEqual(found, error.warnings[0].found) self.assertEqual(expected, error.warnings[0].expected)
class IgnoredEntriesAreReported(TestCase): def setUp(self): self._codec = YAML() def assert_extra_in(self, text, expected): try: self._codec.load_model_from(StringIO(text)) self.fail("Should have raised an exception!") except InvalidYAMLModel as error: self.assertEqual(1, len(error.warnings)) self.assertEqual(expected, error.warnings[0].path) def test_when_an_extra_entry_is_in_the_root(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" "extra: this entry should be reported!\n" "goals:\n" " running:\n" " - Wonderful\n", expected="extra") def test_when_an_extra_entry_is_in_a_component(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " extra: this entry should be reported!\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/extra") def test_when_an_extra_entry_is_in_the_variables(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " extra: this entry should be reported!\n" " values: [ 1GB, 2GB]\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/variables/memory/extra") def test_when_an_extra_entry_is_in_a_substitution(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [ 1GB, 2GB]\n" " realization: \n" " - targets: [ file1 ]\n" " pattern: mem=1GB\n" " extra: this entry should be reported\n" " replacements: [mem=1GB, mem=2GB]\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/variables/memory/realization/#1/extra") def test_when_an_extra_entry_is_in_a_resource_selection(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [ 1GB, 2GB]\n" " realization: \n" " - select: \n" " - config_1GB\n" " - config_2GB\n" " extra: this entry should be reported\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/variables/memory/realization/#1/extra") def test_when_an_extra_entry_is_in_the_implementation(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " extra: this entry should be reported!\n" " docker:\n" " file: DockerFile\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/implementation/extra") def test_when_an_extra_entry_is_in_docker(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker:\n" " extra: this entry should be reported!\n" " file: DockerFile\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/implementation/docker/extra") def test_when_an_extra_entry_is_in_the_goals(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" "goals:\n" " extra: this entry should be reported!\n" " running:\n" " - Wonderful\n", expected="goals/extra") def test_when_an_extra_entry_is_in_the_test_settings(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " command: mvn -B test -gs ./settings.xml\n" " extra: this entry should be reported!\n" " reports:\n" " format: JUnit\n" " location: target/surefire-reports\n" " pattern: TEST*.xml\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/tests/extra") def test_when_an_extra_entry_is_in_the_test_reports(self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " tests:\n" " command: mvn -B test -gs ./settings.xml\n" " reports:\n" " extra: this entry should be reported!\n" " format: JUnit\n" " location: target/surefire-reports\n" " pattern: TEST*.xml\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/tests/reports/extra") def test_when_an_extra_entry_is_component_realization_selected_resource( self): self.assert_extra_in( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " realization:\n" " - select: apache_docker-compose.yml\n" " instead_of:\n" " - nginx_docker-compose.yml\n" " extra: stuff\n" " as: docker-compose.yml\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/realization/#1/extra")
class MissingMandatoryEntriesAreReported(TestCase): def setUp(self): self._codec = YAML() def test_when_omitting_substitution_targets(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - pattern: xmem=1GB\n" " replacements: [xmem=1, xmem=2, xmem=4]\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/variables/memory/realization/#1", candidates=["targets"]) def test_when_omitting_substitution_pattern(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - targets: [ Dockerfile ]\n" " replacements: [xmem=1, xmem=2, xmem=4]\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/variables/memory/realization/#1", candidates=["pattern"]) def test_when_omitting_substitution_replacements(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - targets: [ Dockerfile ]\n" " pattern: xmem=1GB\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/variables/memory/realization/#1", candidates=["replacements"]) def test_when_omitting_the_docker_file(self): self.assert_missing( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker: {}\n" "goals:\n" " running:\n" " - Wonderful\n", path="components/server/implementation/docker", candidates=["file", "image"]) def assert_missing(self, text, path, candidates): try: model = self._codec.load_model_from(StringIO(text)) self.fail("InvalidYAMLModel should have been thrown!") except InvalidYAMLModel as error: self.assertEqual(1, len(error.warnings)) self.assertEqual(path, error.warnings[0].path) self.assertItemsEqual(candidates, error.warnings[0].candidates)
class TypeMismatchAreReported(TestCase): def setUp(self): self._codec = YAML() def test_with_a_string_as_component(self): self.assert_warning( "components: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="dict", found="str", path="components") def test_with_a_string_as_provided_services(self): self.assert_warning( "components: \n" " server:\n" " provides_services: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/provides_services") def test_with_a_string_as_required_services(self): self.assert_warning( "components: \n" " server:\n" " requires_services: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/requires_services") def test_with_a_string_as_provided_features(self): self.assert_warning( "components: \n" " server:\n" " provides_features: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/provides_features") def test_with_a_string_as_required_features(self): self.assert_warning( "components: \n" " server:\n" " requires_features: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="list", found="str", path="components/server/requires_features") def test_with_a_string_as_variables(self): self.assert_warning( "components: \n" " server:\n" " requires_features: [ Awesome ]\n" " variables: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="dict", found="str", path="components/server/variables") def test_with_a_string_as_implementation(self): self.assert_warning( "components: \n" " server:\n" " requires_features: [ Awesome ]\n" " implementation: blablabla\n" "goals:\n" " running:\n" " - Wonderful\n", expected="dict", found="str", path="components/server/implementation") def test_with_a_string_as_goals(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" "goals: blablabla\n", expected="dict", found="str", path="goals") def test_with_a_string_as_running(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" "goals:\n" " running: blablabla\n", expected="list", found="str", path="goals/running") def test_with_a_string_as_substitution_replacements(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB ]\n" " realization:\n" " - targets: [ Dockerfile ]\n" " pattern: xmem=1GB\n" " replacements: This should not be a string!\n" "goals:\n" " running: [ Awesome ]\n", expected="list", found="str", path="components/server/variables/memory/realization/#1/replacements", warning_count=2) def test_with_a_string_as_substitution_targets(self): self.assert_warning( "components:\n" " server:\n" " provides_services: [ Awesome ]\n" " variables:\n" " memory:\n" " values: [1GB, 2GB ]\n" " realization:\n" " - targets: This should not be a string!\n" " pattern: xmem=1GB\n" " replacements: [xmem=1GB, xmem=2GB]\n" "goals:\n" " running: [ Awesome ]\n", expected="list", found="str", path="components/server/variables/memory/realization/#1/targets", warning_count=2) def assert_warning(self, text, expected, found, path, warning_count=1): try: model = self._codec.load_model_from(StringIO(text)) self.fail("InvalidYAMLModel should have been thrown!") except InvalidYAMLModel as error: self.assertEqual(warning_count, len(error.warnings), [str(w) for w in error.warnings]) self.assertEqual(path, error.warnings[0].path) self.assertEqual(found, error.warnings[0].found) self.assertEqual(expected, error.warnings[0].expected)
class IgnoredEntriesAreReported(TestCase): def setUp(self): self._codec = YAML() def assert_extra_in(self, text, expected): try : self._codec.load_model_from(StringIO(text)) fail("Should have raised an exception!") except InvalidYAMLModel as error: self.assertEqual(1, len(error.warnings)) self.assertEqual(expected, error.warnings[0].path) def test_when_an_extra_entry_is_in_the_root(self): self.assert_extra_in("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" "extra: this entry should be reported!\n" "goals:\n" " running:\n" " - Wonderful\n", expected="extra") def test_when_an_extra_entry_is_in_a_component(self): self.assert_extra_in("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " extra: this entry should be reported!\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/extra") def test_when_an_extra_entry_is_in_the_variables(self): self.assert_extra_in("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " extra: this entry should be reported!\n" " values: [ 1GB, 2GB]\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/variables/memory/extra") def test_when_an_extra_entry_is_in_a_substitution(self): self.assert_extra_in("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " values: [ 1GB, 2GB]\n" " realization: \n" " - targets: [ file1 ]\n" " pattern: mem=1GB\n" " extra: this entry should be reported\n" " replacements: [mem=1GB, mem=2GB]\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/variables/memory/realization/#1/extra") def test_when_an_extra_entry_is_in_the_implementation(self): self.assert_extra_in("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " extra: this entry should be reported!\n" " docker:\n" " file: DockerFile\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/implementation/extra") def test_when_an_extra_entry_is_in_the_docker(self): self.assert_extra_in("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker:\n" " extra: this entry should be reported!\n" " file: DockerFile\n" "goals:\n" " running:\n" " - Wonderful\n", expected="components/server/implementation/docker/extra") def test_when_an_extra_entry_is_in_the_goals(self): self.assert_extra_in("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" "goals:\n" " extra: this entry should be reported!\n" " running:\n" " - Wonderful\n", expected="goals/extra")
class BuiltModelAreComplete(TestCase): def setUp(self): self._codec = YAML() def test_given_a_one_component_stack(self): self.assert_complete("components:\n" " server:\n" " provides_services: [ Wonderful ]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components" : { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": {} } }, "goals": { "services": ["Wonderful"], "features": []} }) def test_given_a_one_component_stack_with_two_variables(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " type: Integer\n" " values: [1GB, 2GB]\n" " threads:\n" " type: Integer\n" " values: [64, 128, 256]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components" : { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": { "threads": { "values": [64, 128, 256], "realization": [] }, "memory": { "values": ["1GB", "2GB"], "realization": [] } } } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_two_component_stack(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " requires_features: [ Python27 ]\n" " python:\n" " provides_features: [ Python27 ]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": ["Python27"], "components" : { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": ["Python27"], "implementation": None, "variables": {} }, "python": { "provided_services": [], "required_services": [], "provided_features": ["Python27"], "required_features": [], "implementation": None, "variables": {} } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_docker_file(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker:\n" " file: server/Dockerfile\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components" : { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": DockerFile("server/Dockerfile"), "variables": {} } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_a_docker_image(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " implementation:\n" " docker:\n" " image: fchauvel/camp:dev\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components" : { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": DockerImage("fchauvel/camp:dev"), "variables": {} } }, "goals": { "services": ["Wonderful"], "features": [] } }) def test_given_a_component_with_a_realized_variable(self): self.assert_complete( "components:\n" " server:\n" " provides_services: [ Wonderful ]\n" " variables:\n" " memory:\n" " type: Text\n" " values: [1GB, 2GB, 4GB]\n" " realization:\n" " - targets: [ file1, path/to/file2 ]\n" " pattern: xmem=1GB\n" " replacements: [xmem=1, xmem=2, xmem=4]\n" "goals:\n" " running:\n" " - Wonderful\n", expectations={ "services": ["Wonderful"], "features": [], "components" : { "server": { "provided_services": ["Wonderful"], "required_services": [], "provided_features": [], "required_features": [], "implementation": None, "variables": { "memory": { "values": ["1GB", "2GB", "4GB"], "realization": [ Substitution( targets=["file1", "path/to/file2"], pattern="xmem=1GB", replacements=["xmem=1", "xmem=2", "xmem=4"]) ] } } } }, "goals": { "services": ["Wonderful"], "features": [] } }) def assert_complete(self, text, expectations): model = self._codec.load_model_from(StringIO(text)) self.assertEqual(0, len(self._codec.warnings)) self.assert_goals(model.goals, expectations["goals"]) self.assertItemsEqual(expectations["services"], [each.name for each in model.services]) self.assertItemsEqual(expectations["features"], [each.name for each in model.features]) self.assertEqual( set([each.name for each in model.components]), set(expectations["components"].keys())) for each_component in model.components: expectation = expectations["components"][each_component.name] self.assert_component(each_component, expectation) def assert_goals(self, goals, expectations): self.assertItemsEqual(expectations["services"], [each.name for each in goals.services]) self.assertItemsEqual(expectations["features"], [each.name for each in goals.features]) def assert_component(self, component, expectation): self.assertItemsEqual( expectation["provided_services"], [each.name for each in component.provided_services]) self.assertItemsEqual( expectation["required_services"], [each.name for each in component.required_services]) self.assertItemsEqual( expectation["provided_features"], [each.name for each in component.provided_features]) self.assertItemsEqual( expectation["required_features"], [each.name for each in component.required_features]) self.assertEqual(expectation["implementation"], component.implementation) self.assert_variables(component, expectation["variables"]) def assert_variables(self, component, variables): self.assertEqual(len(variables), len(component.variables)) for name, variable in variables.items(): match = next((variable for variable in component.variables\ if variable.name == name), None) if match: self.assertItemsEqual(match.domain, variable["values"]) self.assertItemsEqual(match.realization, variable["realization"]) else: self.fail("Component '%s' lacks variable '%s'." % (component.name, name))
class VariablesAreAssigned(TestCase): def setUp(self): self._yaml = YAML() def assert_variables_values(self, text, configuration_count, expected_values): model = self._yaml.load_model_from(BytesIO(text.encode())) self.assertFalse(self._yaml.warnings, "\n".join(str(each) for each in self._yaml.warnings)) solver = Z3Problem.from_model(model) configurations = list(solver.all_solutions()) self.assertEqual(configuration_count, len(configurations)) actual_values = {key: [] for key in expected_values} for each_configuration in configurations: for each_instance in each_configuration.instances: for variable, value in each_instance.configuration: if variable.name not in actual_values: actual_values[variable.name] = [] actual_values[variable.name].append(value) self.assertEqual(len(expected_values), len(actual_values)) for each_variable in expected_values: self.assertEqual(set(expected_values[each_variable]), set(actual_values[each_variable])) def test_given_integer_variables(self): self.assert_variables_values( "components:\n" " server:\n" " provides_services: [ MyService ]\n" " variables:\n" " memory:\n" " type: Integer\n" " max_thread:\n" " type: Integer\n" "constraints:\n" " - CInstance.forall(ci, ci.configuration.exists(val," "And([val.variable == variable('server', 'memory')," " val.value == 4])))\n" " - CInstance.forall(ci, ci.configuration.exists(val1, " "And([val1.variable == variable('server', 'max_thread'), " "ci.configuration.exists(val2, " "And([val2.variable == variable('server', 'memory'), " "val1.value * 2 == val2.value]))])))\n" "goals:\n" " running: \n" " - MyService \n", configuration_count=1, expected_values={ "memory": [4], "max_thread": [2] }) def test_given_enumerated_variables(self): self.assert_variables_values( "components:\n" " server:\n" " provides_services: [ MyService ]\n" " variables:\n" " memory:\n" " type: Symbols\n" " values: [ 1GB, 2GB ]\n" "goals:\n" " running:\n" " - MyService\n", configuration_count=2, expected_values={"memory": ["1GB", "2GB"]}) def test_given_enumerated_integer_variables(self): self.assert_variables_values( "components:\n" " server:\n" " provides_services: [ MyService ]\n" " variables:\n" " memory:\n" " type: Integer\n" " values: [ 5, 10 ]\n" "goals:\n" " running:\n" " - MyService\n", configuration_count=2, expected_values={"memory": [5, 10]}) def test_given_covered_integer_variables(self): self.assert_variables_values( "components:\n" " server:\n" " provides_services: [ MyService ]\n" " variables:\n" " memory:\n" " type: Integer\n" " values: \n" " range: [100, 200]\n" " coverage: 50\n" "goals:\n" " running:\n" " - MyService\n", configuration_count=3, expected_values={"memory": [100, 150, 200]})