#!/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. import pytest from fruit_test_common import * COMMON_DEFINITIONS = ''' #include "test_common.h" struct X; struct Annotation1 {}; using XAnnot = fruit::Annotated; struct Annotation2 {}; ''' @pytest.mark.parametrize('XVariant,XVariantRegexp', [ ('X*', 'X\*'), ('const X*', 'const X\*'), ('X&', 'X&'), ('const X&', 'const X&'), ('std::shared_ptr', 'std::shared_ptr'), ]) def test_error_non_class_type_parameter(XVariant, XVariantRegexp): source = ''' struct X {}; fruit::Provider provider; ''' expect_compile_error( 'NonClassTypeError', 'A non-class type T was specified. Use C instead', COMMON_DEFINITIONS, source, locals()) def test_error_annotated_type_parameter(): source = ''' struct X {}; fruit::Provider provider; ''' expect_compile_error( 'AnnotatedTypeError,X>', 'An annotated type was specified where a non-annotated type was expected.', COMMON_DEFINITIONS, source) @pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [ ('X', 'fruit::Provider', 'X', 'X'), ('X', 'fruit::Provider', 'X', 'const X&'), ('X', 'fruit::Provider', 'X', 'const X*'), ('X', 'fruit::Provider', 'X', 'X&'), ('X', 'fruit::Provider', 'X', 'X*'), ('X', 'fruit::Provider', 'X', 'std::shared_ptr'), ('X', 'fruit::Provider', 'X', 'fruit::Provider'), ('X', 'fruit::Provider', 'X', 'fruit::Provider'), ('X', 'fruit::Provider', 'const X', 'const X&'), ('fruit::Annotated', 'fruit::Annotated>', 'X', 'const X&'), ('fruit::Annotated', 'fruit::Annotated>', 'const X', 'const X&'), ]) def test_provider_get_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam): source = ''' struct X { using Inject = X(); }; fruit::Component getComponent() { return fruit::createComponent(); } int main() { fruit::Injector injector(getComponent); fruit::Provider provider = injector.get(); XProviderGetParam x = provider.get(); (void)x; } ''' expect_success( COMMON_DEFINITIONS, source, locals()) @pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [ ('const X', 'fruit::Provider', 'const X', 'X'), ('const X', 'fruit::Provider', 'const X', 'const X&'), ('const X', 'fruit::Provider', 'const X', 'const X*'), ('const X', 'fruit::Provider', 'const X', 'fruit::Provider'), ('fruit::Annotated', 'fruit::Annotated>', 'const X', 'const X&'), ]) def test_provider_get_const_binding_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam): XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '') source = ''' struct X {}; const X x{}; fruit::Component getComponent() { return fruit::createComponent() .bindInstance(x); } int main() { fruit::Injector injector(getComponent); fruit::Provider provider = injector.get(); XProviderGetParam x = provider.get(); (void)x; } ''' expect_success( COMMON_DEFINITIONS, source, locals()) def test_provider_get_during_injection_ok(): source = ''' struct X { INJECT(X()) = default; void foo() { } }; struct Y { X x; INJECT(Y(fruit::Provider xProvider)) : x(xProvider.get()) { } void foo() { x.foo(); } }; struct Z { Y y; INJECT(Z(fruit::Provider yProvider)) : y(yProvider.get()) { } void foo() { y.foo(); } }; fruit::Component getZComponent() { return fruit::createComponent(); } int main() { fruit::Injector injector(getZComponent); fruit::Provider provider(injector); // During provider.get(), yProvider.get() is called, and during that xProvider.get() // is called. Z z = provider.get(); z.foo(); } ''' expect_success( COMMON_DEFINITIONS, source) def test_provider_get_error_type_not_provided(): source = ''' struct X {}; struct Y {}; void f(fruit::Provider provider) { provider.get(); } ''' expect_compile_error( 'TypeNotProvidedError', 'Trying to get an instance of T, but it is not provided by this Provider/Injector.', COMMON_DEFINITIONS, source) @pytest.mark.parametrize('XVariant,XVariantRegex', [ ('X**', r'X\*\*'), ('std::shared_ptr*', r'std::shared_ptr\*'), ('const std::shared_ptr', r'const std::shared_ptr'), ('X* const', r'X\* const'), ('const X* const', r'const X\* const'), ('std::nullptr_t', r'(std::)?nullptr(_t)?'), ('X*&', r'X\*&'), ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'), ('void', r'void'), ('fruit::Annotated>', r'fruit::Annotated'), ]) def test_provider_get_error_type_not_injectable(XVariant,XVariantRegex): source = ''' struct X {}; void f(fruit::Provider provider) { provider.get(); } ''' expect_compile_error( 'NonInjectableTypeError', 'The type T is not injectable', COMMON_DEFINITIONS, source, locals()) @pytest.mark.parametrize('XProviderGetParam,XProviderGetParamRegex', [ ('X&', 'X&'), ('X*', 'X\*'), ('std::shared_ptr', 'std::shared_ptr'), ('fruit::Provider', 'fruit::Provider'), ]) def test_const_provider_get_does_not_allow_injecting_nonconst_variants(XProviderGetParam, XProviderGetParamRegex): source = ''' void f(fruit::Provider provider) { provider.get(); } ''' expect_compile_error( 'TypeProvidedAsConstOnlyError', 'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector', COMMON_DEFINITIONS, source, locals()) @pytest.mark.parametrize('Y_PROVIDER_ANNOT', [ ('fruit::Provider'), ('ANNOTATED(Annotation1, fruit::Provider)'), ]) def test_lazy_injection_with_annotations(Y_PROVIDER_ANNOT): source = ''' struct Y : public ConstructionTracker { using Inject = Y(); }; struct X : public ConstructionTracker { INJECT(X(Y_PROVIDER_ANNOT provider)) : provider(provider) { } void run() { Y* y(provider); (void) y; } fruit::Provider provider; }; fruit::Component getComponent() { return fruit::createComponent(); } fruit::Component<> getEmptyComponent() { return fruit::createComponent(); } int main() { fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent); fruit::Injector injector(normalizedComponent, getComponent); Assert(X::num_objects_constructed == 0); Assert(Y::num_objects_constructed == 0); X* x(injector); Assert(X::num_objects_constructed == 1); Assert(Y::num_objects_constructed == 0); x->run(); Assert(X::num_objects_constructed == 1); Assert(Y::num_objects_constructed == 1); } ''' expect_success( COMMON_DEFINITIONS, source, locals()) if __name__== '__main__': main(__file__)