#!/usr/bin/env python3 # Copyright 2016 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS-IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from absl.testing import parameterized from fruit_test_common import * COMMON_DEFINITIONS = ''' #include "test_common.h" struct X {}; struct Y {}; struct Annotation1 {}; using IntAnnot1 = fruit::Annotated; using XAnnot1 = fruit::Annotated; struct Annotation2 {}; using IntAnnot2 = fruit::Annotated; using XAnnot2 = fruit::Annotated; ''' class TestComponentAndInjectorParams(parameterized.TestCase): @multiple_parameters([ ('X', 'X'), ('X', 'const X'), ('fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated'), ], [ 'Component', 'NormalizedComponent', 'Injector', ]) def test_duplicate_type(self, XAnnot, MaybeConstXAnnot, Class): source = ''' InstantiateType(fruit::Class) ''' expect_compile_error( 'RepeatedTypesError', 'A type was specified more than once.', COMMON_DEFINITIONS, source, locals()) @multiple_parameters([ ('X', 'const X'), ('fruit::Annotated', 'fruit::Annotated'), ], [ 'Component', 'NormalizedComponent', 'Injector', ]) def test_duplicate_type_different_constness(self, XAnnot, ConstXAnnot, Class): source = ''' InstantiateType(fruit::Class) ''' expect_compile_error( 'RepeatedTypesError', 'A type was specified more than once.', COMMON_DEFINITIONS, source, locals()) def test_duplicate_type_with_different_annotation_ok(self): source = ''' fruit::Component getComponent() { return fruit::createComponent() .registerConstructor() .registerConstructor(); } int main() { fruit::Injector injector1(getComponent); injector1.get(); injector1.get(); fruit::NormalizedComponent normalizedComponent(getComponent); fruit::Injector injector2(getComponent); injector2.get(); injector2.get(); } ''' expect_success( COMMON_DEFINITIONS, source) @multiple_parameters([ ('X', 'X'), ('X', 'const X'), ('fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated'), ], [ 'Component', 'NormalizedComponent', ]) def test_duplicate_type_in_required(self, XAnnot, MaybeConstXAnnot, Class): source = ''' InstantiateType(fruit::Class>) ''' expect_compile_error( 'RepeatedTypesError', 'A type was specified more than once.', COMMON_DEFINITIONS, source, locals()) @multiple_parameters([ 'Component', 'NormalizedComponent', ], [ ('X', 'const X'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_component_duplicate_type_in_required_different_constness(self, Class, XAnnot, ConstXAnnot): source = ''' InstantiateType(fruit::Class>) ''' expect_compile_error( 'RepeatedTypesError', 'A type was specified more than once.', COMMON_DEFINITIONS, source, locals()) @multiple_parameters([ ('X', 'X'), ('X', 'const X'), ('fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated'), ], [ 'Component', 'NormalizedComponent', ]) def test_same_type_in_required_and_provided(self, XAnnot, MaybeConstXAnnot, Class): source = ''' InstantiateType(fruit::Class, MaybeConstXAnnot>) ''' expect_compile_error( 'RepeatedTypesError', 'A type was specified more than once.', COMMON_DEFINITIONS, source, locals()) @multiple_parameters([ ('X', 'X', 'const X'), ('X', 'const X', 'X'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ], [ 'Component', 'NormalizedComponent', ]) def test_same_type_in_required_and_provided_different_constness(self, XAnnot, XAnnotInRequirements, XAnnotInProvides, Class): source = ''' InstantiateType(fruit::Class, XAnnotInProvides>) ''' expect_compile_error( 'RepeatedTypesError', 'A type was specified more than once.', COMMON_DEFINITIONS, source, locals()) def test_same_type_in_required_and_provided_different_annotation_ok(self): source = ''' fruit::Component, XAnnot2> getComponent() { return fruit::createComponent() .registerConstructor(); } fruit::Component getRootComponent() { return fruit::createComponent() .install(getComponent) .registerConstructor(); } fruit::Component<> getEmptyComponent() { return fruit::createComponent(); } int main() { fruit::Injector injector1(getRootComponent); injector1.get(); injector1.get(); fruit::NormalizedComponent normalizedComponent(getRootComponent); fruit::Injector injector2(normalizedComponent, getEmptyComponent); injector2.get(); injector2.get(); } ''' expect_success( COMMON_DEFINITIONS, source) @multiple_parameters([ ('X*', r'X\*'), ('const X*', r'const X\*'), ('X&', r'X&'), ('const X&', r'const X&'), ('std::shared_ptr', r'std::shared_ptr'), ('fruit::Annotated', r'X\*'), ('fruit::Annotated', r'const X\*'), ('fruit::Annotated', r'X&'), ('fruit::Annotated', r'const X&'), ('fruit::Annotated>', r'std::shared_ptr'), ], [ 'Component', 'NormalizedComponent', 'Injector', ]) def test_error_non_class_type(self, XVariantAnnot, XVariantRegexp, Class): source = ''' InstantiateType(fruit::Class) ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead.', COMMON_DEFINITIONS, source, locals()) @multiple_parameters([ ('const X', 'const X'), ('fruit::Annotated', 'const X'), ], [ 'Component', 'NormalizedComponent', 'Injector', ]) def test_const_provided_type_ok(self, XVariantAnnot, XVariantRegexp, Class): source = ''' InstantiateType(fruit::Class) ''' expect_success( COMMON_DEFINITIONS, source, locals()) @multiple_parameters([ ('X*', r'X\*'), ('const X*', r'const X\*'), ('X&', r'X&'), ('const X&', r'const X&'), ('std::shared_ptr', r'std::shared_ptr'), ('fruit::Annotated', r'X\*'), ('fruit::Annotated', r'const X\*'), ('fruit::Annotated', r'X&'), ('fruit::Annotated', r'const X&'), ('fruit::Annotated>', r'std::shared_ptr'), ], [ 'Component', 'NormalizedComponent', ]) def test_error_non_class_type_in_requirements(self, XVariantAnnot, XVariantRegexp, Class): source = ''' InstantiateType(fruit::Class>) ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('const Z', 'Z'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_const_class_type_ok(self, ConstZAnnot, ZAnnot): source = ''' struct Z {}; const Z z{}; fruit::Component getComponent() { return fruit::createComponent() .bindInstance(z); } fruit::Component<> getEmptyComponent() { return fruit::createComponent(); } int main() { fruit::NormalizedComponent normalizedComponent(getComponent); fruit::Injector injector(normalizedComponent, getEmptyComponent); injector.get(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('const Z', 'Z'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_const_class_type_in_requirements_ok(self, ConstZAnnot, ZAnnot): source = ''' struct Z {}; fruit::Component> getComponent() { return fruit::createComponent(); } const Z z{}; fruit::Component getEmptyComponent() { return fruit::createComponent() .bindInstance(z); } int main() { fruit::NormalizedComponent> normalizedComponent(getComponent); fruit::Injector injector(normalizedComponent, getEmptyComponent); injector.get(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ 'Component', 'NormalizedComponent', ]) def test_two_required_lists_error(self, Class): source = ''' InstantiateType(fruit::Class, fruit::Required>) ''' expect_compile_error( 'RequiredTypesInComponentArgumentsError>', 'A Required<...> type was passed as a non-first template parameter to fruit::Component or fruit::NormalizedComponent', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ 'Component', 'NormalizedComponent', ]) def test_required_list_not_first_argument_error(self, Class): source = ''' InstantiateType(fruit::Class>) ''' expect_compile_error( 'RequiredTypesInComponentArgumentsError>', 'A Required<...> type was passed as a non-first template parameter to fruit::Component or fruit::NormalizedComponent', COMMON_DEFINITIONS, source, locals()) def test_multiple_required_types_ok(self): source = ''' fruit::Component> getEmptyComponent() { return fruit::createComponent(); } fruit::Component getComponent() { return fruit::createComponent() .install(getEmptyComponent) .registerConstructor() .registerConstructor(); } int main() { fruit::NormalizedComponent> normalizedComponent(getEmptyComponent); fruit::Injector injector(normalizedComponent, getComponent); injector.get(); } ''' expect_success( COMMON_DEFINITIONS, source) @parameterized.parameters([ ('X', 'Y'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_error_requirements_in_injector(self, XAnnot, YAnnot): source = ''' InstantiateType(fruit::Injector, XAnnot>) ''' expect_compile_error( 'InjectorWithRequirementsError', 'Injectors can.t have requirements.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X', 'Y'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_error_requirements_in_injector_second_argument(self, XAnnot, YAnnot): source = ''' InstantiateType(fruit::Injector>) ''' expect_compile_error( 'InjectorWithRequirementsError', 'Injectors can.t have requirements.', COMMON_DEFINITIONS, source, locals()) if __name__ == '__main__': absltest.main()