#!/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 {}; struct Annotation2 {}; ''' class TestBindInstance(parameterized.TestCase): @parameterized.parameters([ ('X', 'X', 'const X&', 'Y'), ('X', 'const X', 'const X&', 'Y'), ('X', 'X', 'const X&', 'fruit::Annotated'), ('X', 'const X', 'const X&', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'Y'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'Y'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_interface(self, XAnnot, MaybeConstXAnnot, XConstRefAnnot, YAnnot): source = ''' struct X { virtual void f() const = 0; }; struct Y : public X { INJECT(Y()) = default; void f() const override { } }; fruit::Component getComponent() { return fruit::createComponent() .bind(); } int main() { fruit::Injector injector(getComponent); const X& x = injector.get(); x.f(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X', 'const X', 'const X&', 'Y'), ('X', 'const X', 'const X&', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'Y'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_interface_to_constant(self, XAnnot, ConstXAnnot, XConstRefAnnot, YAnnot): source = ''' struct X { virtual void f() const = 0; }; struct Y : public X { void f() const override { } }; const Y y{}; fruit::Component getComponent() { return fruit::createComponent() .bindInstance(y) .bind(); } int main() { fruit::Injector injector(getComponent); const X& x = injector.get(); x.f(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X', 'X&', 'Y'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_interface_target_bound_in_other_component(self, XAnnot, XRefAnnot, YAnnot): source = ''' struct X { virtual void f() = 0; }; struct Y : public X { void f() override { } }; fruit::Component, XAnnot> getComponent() { return fruit::createComponent() .bind(); } fruit::Component getRootComponent() { return fruit::createComponent() .registerConstructor() .install(getComponent); } int main() { fruit::Injector injector(getRootComponent); X& x = injector.get(); x.f(); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X', 'X&', 'Y'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_nonconst_interface_requires_nonconst_target(self, XAnnot, XRefAnnot, YAnnot): source = ''' struct X { virtual void f() = 0; }; struct Y : public X { void f() override { } }; fruit::Component, XAnnot> getComponent() { return fruit::createComponent() .bind(); } ''' expect_compile_error( 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError', 'The type T was declared as a const Required type in the returned Component, however a non-const binding', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X', 'Y'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_interface_to_constant_nonconst_required_const_bound_error(self, XAnnot, YAnnot): source = ''' struct X { virtual void f() const = 0; }; struct Y : public X { void f() const override { } }; const Y y{}; fruit::Component getComponent() { return fruit::createComponent() .bindInstance(y) .bind(); } ''' expect_compile_error( 'NonConstBindingRequiredButConstBindingProvidedError', 'The type T was provided as constant, however one of the constructors/providers/factories in this component', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X', 'X&', 'Y'), ('fruit::Annotated', 'fruit::Annotated', 'fruit::Annotated'), ]) def test_bind_nonconst_interface_requires_nonconst_target_abstract(self, XAnnot, XRefAnnot, YAnnot): source = ''' struct X { virtual void f() = 0; }; struct Y : public X {}; fruit::Component, XAnnot> getComponent() { return fruit::createComponent() .bind(); } ''' expect_compile_error( 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError', 'The type T was declared as a const Required type in the returned Component, however a non-const binding', COMMON_DEFINITIONS, source, locals()) @parameterized.parameters([ ('X', 'int'), ('fruit::Annotated', 'fruit::Annotated'), ]) def test_error_not_base(self, XAnnot, intAnnot): source = ''' struct X {}; fruit::Component getComponent() { return fruit::createComponent() .bind(); } ''' expect_compile_error( 'NotABaseClassOfError', 'I is not a base class of C.', COMMON_DEFINITIONS, source, locals()) # TODO: maybe the error should include the annotation here. @parameterized.parameters([ 'X', 'fruit::Annotated', ]) def test_error_bound_to_itself(self, XAnnot): source = ''' struct X {}; fruit::Component getComponent() { return fruit::createComponent() .bind(); } ''' expect_compile_error( 'InterfaceBindingToSelfError', 'The type C was bound to itself.', COMMON_DEFINITIONS, source, locals()) def test_bound_to_itself_with_annotation_error(self): source = ''' struct X {}; fruit::Component<> getComponent() { return fruit::createComponent() .registerConstructor() .bind, X>(); } ''' expect_compile_error( 'InterfaceBindingToSelfError', 'The type C was bound to itself.', COMMON_DEFINITIONS, source) def test_bound_chain_ok(self): source = ''' struct X { virtual void f() = 0; }; struct Y : public X {}; struct Z : public Y { INJECT(Z()) = default; void f() override { } }; fruit::Component getComponent() { return fruit::createComponent() .bind() .bind(); } int main() { fruit::Injector injector(getComponent); X& x = injector.get(); x.f(); } ''' expect_success(COMMON_DEFINITIONS, source) def test_bind_non_normalized_types_error(self): source = ''' struct X {}; struct Y : public std::shared_ptr {}; fruit::Component<> getComponent() { return fruit::createComponent() .bind, Y>(); } ''' expect_compile_error( 'NonClassTypeError,X>', 'A non-class type T was specified. Use C instead', COMMON_DEFINITIONS, source) if __name__ == '__main__': absltest.main()