#!/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 Annotation1 {}; ''' def escape_regex(regex): # We un-escape the space because we strip the spaces in fruit_test_common, and this would otherwise leave a # stray backslash. return re.escape(regex).replace('\\ ', ' ') class TestRegisterInstance(parameterized.TestCase): def test_bind_instance_success(self): source = ''' struct X { int n; X(int n) : n(n) { } }; fruit::Component getComponent(X* x) { return fruit::createComponent() .bindInstance(*x); } int main() { X x(34); fruit::Injector injector(getComponent, &x); X& x1 = injector.get(); Assert(&x == &x1); } ''' expect_success(COMMON_DEFINITIONS, source) def test_bind_instance_annotated_success(self): source = ''' struct X { int n; X(int n) : n(n) { } }; fruit::Component> getComponent(X* x) { return fruit::createComponent() .bindInstance>(*x); } int main() { X x(34); fruit::Injector> injector(getComponent, &x); X& x1 = injector.get>(); Assert(&x == &x1); } ''' expect_success(COMMON_DEFINITIONS, source) def test_bind_const_instance_success(self): source = ''' struct X { int n; X(int n) : n(n) { } }; fruit::Component getComponent(const X* x) { return fruit::createComponent() .bindInstance(*x); } const X x(34); int main() { fruit::Injector injector(getComponent, &x); const X& x1 = injector.get(); Assert(&x == &x1); } ''' expect_success(COMMON_DEFINITIONS, source) def test_bind_const_instance_annotated_success(self): source = ''' struct X { int n; X(int n) : n(n) { } }; fruit::Component> getComponent(const X* x) { return fruit::createComponent() .bindInstance>(*x); } const X x(34); int main() { fruit::Injector> injector(getComponent, &x); const X& x1 = injector.get>(); Assert(&x == &x1); } ''' expect_success(COMMON_DEFINITIONS, source) @parameterized.parameters([ ('X', 'X', 'X*', 'X*'), ('X', 'const X', 'const X*', 'const X*'), ('fruit::Annotated', 'fruit::Annotated', 'X*', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'const X*', 'fruit::Annotated'), ]) def test_bind_instance_two_explicit_type_arguments_success(self, XAnnot, MaybeConstXAnnot, XPtr, XPtrAnnot): source = ''' struct X { int n; X(int n) : n(n) { } }; fruit::Component getComponent(XPtr x) { return fruit::createComponent() .bindInstance(*x); } int main() { X x(34); fruit::Injector injector(getComponent, &x); XPtr x1 = injector.get(); Assert(&x == x1); } ''' expect_success(COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ 'X', 'fruit::Annotated', ]) def test_bind_instance_abstract_class_ok(self, XAnnot): source = ''' struct X { virtual void foo() = 0; }; fruit::Component<> getComponentForInstanceHelper(X* x) { return fruit::createComponent() .bindInstance(*x); } fruit::Component getComponentForInstance(X* x) { return fruit::createComponent() .install(getComponentForInstanceHelper, x) .bindInstance(*x); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('int', 'int*'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_instance_multiple_equivalent_bindings_success(self, intAnnot, intPtrAnnot): source = ''' fruit::Component<> getComponentForInstanceHelper(int* n) { return fruit::createComponent() .bindInstance(*n); } fruit::Component getComponentForInstance(int* n) { return fruit::createComponent() .install(getComponentForInstanceHelper, n) .bindInstance(*n); } int main() { int n = 5; fruit::Injector injector(getComponentForInstance, &n); if (injector.get() != &n) abort(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('int', 'int*'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_instance_multiple_equivalent_bindings_different_constness_success(self, intAnnot, intPtrAnnot): source = ''' fruit::Component<> getComponentForInstanceHelper(const int* n) { return fruit::createComponent() .bindInstance(*n); } fruit::Component getComponentForInstance(int* n) { return fruit::createComponent() .install(getComponentForInstanceHelper, n) .bindInstance(*n); } int main() { int n = 5; fruit::Injector injector(getComponentForInstance, &n); if (injector.get() != &n) abort(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('int', 'int*'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_instance_multiple_equivalent_bindings_different_constness_other_order_success(self, intAnnot, intPtrAnnot): source = ''' fruit::Component<> getComponentForInstanceHelper(const int* n) { return fruit::createComponent() .bindInstance(*n); } fruit::Component getComponentForInstance(int* n) { return fruit::createComponent() .bindInstance(*n) .install(getComponentForInstanceHelper, n); } int main() { int n = 5; fruit::Injector injector(getComponentForInstance, &n); if (injector.get() != &n) abort(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ 'X*', 'const X*', 'std::shared_ptr', ]) def test_bind_instance_non_normalized_type_error(self, XVariant): if XVariant.endswith('&'): XVariantRegexp = escape_regex(XVariant[:-1]) else: XVariantRegexp = escape_regex(XVariant) source = ''' struct X {}; fruit::Component<> getComponent(XVariant x) { return fruit::createComponent() .bindInstance(x); } ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('const X', r'const X'), ('X*', r'X\*'), ('const X*', r'const X\*'), ('X&', r'X&'), ('const X&', r'const X&'), ('std::shared_ptr', r'std::shared_ptr'), ]) def test_bind_instance_non_normalized_type_error_with_annotation(self, XVariant, XVariantRegexp): source = ''' struct X {}; fruit::Component<> getComponent(XVariant x) { return fruit::createComponent() .bindInstance>(x); } ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('const X', 'const X'), ('X*', 'X*'), ('const X*', 'const X*'), ('X&', 'X&'), ('const X&', 'const X&'), ('std::shared_ptr', 'std::shared_ptr'), ('fruit::Annotated', 'const X'), ('fruit::Annotated', 'X*'), ('fruit::Annotated', 'const X*'), ('fruit::Annotated', 'X&'), ('fruit::Annotated', 'const X&'), ('fruit::Annotated>', 'std::shared_ptr'), ('fruit::Annotated', 'const X'), ('fruit::Annotated', 'X*'), ('fruit::Annotated', 'const X*'), ('fruit::Annotated', 'X&'), ('fruit::Annotated', 'const X&'), ('fruit::Annotated', 'std::shared_ptr'), ]) def test_bind_instance_non_normalized_type_error_two_explicit_type_arguments(self, XAnnotVariant, XVariant): XVariantRegexp = escape_regex(XVariant) source = ''' struct X {}; fruit::Component<> getComponent(XVariant x) { return fruit::createComponent() .bindInstance(x); } ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X*', r'X\*'), ('const X*', r'const X\*'), ('std::shared_ptr', r'std::shared_ptr'), ]) def test_register_instance_error_must_be_reference(self, XVariant, XVariantRegex): source = ''' struct X {}; fruit::Component<> getComponentForInstance(XVariant x) { return fruit::createComponent() .bindInstance(x); } ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X*', r'X\*'), ('const X*', r'const X\*'), ('std::shared_ptr', r'std::shared_ptr'), ]) def test_register_instance_error_must_be_reference_with_annotation(self, XVariant, XVariantRegex): source = ''' struct X {}; fruit::Component<> getComponentForInstance(XVariant x) { return fruit::createComponent() .bindInstance>(x); } ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ 'X', 'fruit::Annotated', ]) def test_bind_instance_mismatched_type_arguments(self, XAnnot): source = ''' struct X {}; fruit::Component<> getComponent(int* n) { return fruit::createComponent() .bindInstance(*n); } ''' expect_compile_error( 'TypeMismatchInBindInstanceError', 'A type parameter was specified in bindInstance.. but it doesn.t match the value type', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('Base', 'Base*'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_instance_to_subclass(self, BaseAnnot, BasePtrAnnot): source = ''' struct Base { virtual void f() = 0; virtual ~Base() { } }; struct Derived : public Base { void f() override { } }; fruit::Component getComponent(Derived* derived) { return fruit::createComponent() .bindInstance(*derived); } int main() { Derived derived; fruit::Injector injector(getComponent, &derived); Base* base = injector.get(); base->f(); } ''' expect_success(COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X**', r'X\*\*'), ('std::shared_ptr*', r'std::shared_ptr\*'), ('X*&', r'X\*&'), ('fruit::Annotated', r'X\*\*'), ]) def test_bind_instance_type_not_normalized(self, XVariant, XVariantRegex): source = ''' struct X {}; using XVariantT = XVariant; fruit::Component<> getComponent(XVariantT x) { return fruit::createComponent() .bindInstance(x); } ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified.', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'), ]) def test_bind_instance_type_not_injectable_error(self, XVariant, XVariantRegex): source = ''' struct X {}; using XVariantT = XVariant; fruit::Component<> getComponent(XVariantT x) { return fruit::createComponent() .bindInstance(x); } ''' expect_compile_error( 'NonInjectableTypeError', 'The type T is not injectable.', COMMON_DEFINITIONS, source, locals()) if __name__ == '__main__': absltest.main()