Home | History | Annotate | Download | only in tests
      1 #!/usr/bin/env python3
      2 #  Copyright 2016 Google Inc. All Rights Reserved.
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #      http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS-IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 import pytest
     16 
     17 from fruit_test_common import *
     18 
     19 COMMON_DEFINITIONS = '''
     20     #include "test_common.h"
     21 
     22     struct X;
     23 
     24     struct Annotation1 {};
     25     using XAnnot = fruit::Annotated<Annotation1, X>;
     26 
     27     struct Annotation2 {};
     28     '''
     29 
     30 @pytest.mark.parametrize('XVariant,XVariantRegexp', [
     31     ('X*', 'X\*'),
     32     ('const X*', 'const X\*'),
     33     ('X&', 'X&'),
     34     ('const X&', 'const X&'),
     35     ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
     36 ])
     37 def test_error_non_class_type_parameter(XVariant, XVariantRegexp):
     38     source = '''
     39         struct X {};
     40 
     41         fruit::Provider<XVariant> provider;
     42         '''
     43     expect_compile_error(
     44         'NonClassTypeError<XVariantRegexp,X>',
     45         'A non-class type T was specified. Use C instead',
     46         COMMON_DEFINITIONS,
     47         source,
     48         locals())
     49 
     50 def test_error_annotated_type_parameter():
     51     source = '''
     52         struct X {};
     53 
     54         fruit::Provider<XAnnot> provider;
     55         '''
     56     expect_compile_error(
     57         'AnnotatedTypeError<fruit::Annotated<Annotation1,X>,X>',
     58         'An annotated type was specified where a non-annotated type was expected.',
     59         COMMON_DEFINITIONS,
     60         source)
     61 
     62 @pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [
     63     ('X', 'fruit::Provider<X>', 'X', 'X'),
     64     ('X', 'fruit::Provider<X>', 'X', 'const X&'),
     65     ('X', 'fruit::Provider<X>', 'X', 'const X*'),
     66     ('X', 'fruit::Provider<X>', 'X', 'X&'),
     67     ('X', 'fruit::Provider<X>', 'X', 'X*'),
     68     ('X', 'fruit::Provider<X>', 'X', 'std::shared_ptr<X>'),
     69     ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<X>'),
     70     ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<const X>'),
     71     ('X', 'fruit::Provider<const X>', 'const X', 'const X&'),
     72     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<X>>', 'X', 'const X&'),
     73     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'),
     74 ])
     75 def test_provider_get_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam):
     76     source = '''
     77         struct X {
     78           using Inject = X();
     79         };
     80 
     81         fruit::Component<XBindingInInjector> getComponent() {
     82           return fruit::createComponent();
     83         }
     84 
     85         int main() {
     86           fruit::Injector<XBindingInInjector> injector(getComponent);
     87           fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>();
     88 
     89           XProviderGetParam x = provider.get<XProviderGetParam>();
     90           (void)x;
     91         }
     92         '''
     93     expect_success(
     94         COMMON_DEFINITIONS,
     95         source,
     96         locals())
     97 
     98 @pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [
     99     ('const X', 'fruit::Provider<const X>', 'const X', 'X'),
    100     ('const X', 'fruit::Provider<const X>', 'const X', 'const X&'),
    101     ('const X', 'fruit::Provider<const X>', 'const X', 'const X*'),
    102     ('const X', 'fruit::Provider<const X>', 'const X', 'fruit::Provider<const X>'),
    103     ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'),
    104 ])
    105 def test_provider_get_const_binding_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam):
    106     XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '')
    107     source = '''
    108         struct X {};
    109         
    110         const X x{};
    111 
    112         fruit::Component<XBindingInInjector> getComponent() {
    113           return fruit::createComponent()
    114               .bindInstance<XBindingInInjectorWithoutConst, X>(x);
    115         }
    116 
    117         int main() {
    118           fruit::Injector<XBindingInInjector> injector(getComponent);
    119           fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>();
    120 
    121           XProviderGetParam x = provider.get<XProviderGetParam>();
    122           (void)x;
    123         }
    124         '''
    125     expect_success(
    126         COMMON_DEFINITIONS,
    127         source,
    128         locals())
    129 
    130 def test_provider_get_during_injection_ok():
    131     source = '''
    132         struct X {
    133           INJECT(X()) = default;
    134           void foo() {
    135           }
    136         };
    137 
    138         struct Y {
    139           X x;
    140           INJECT(Y(fruit::Provider<X> xProvider))
    141             : x(xProvider.get<X>()) {
    142           }
    143 
    144           void foo() {
    145             x.foo();
    146           }
    147         };
    148 
    149         struct Z {
    150           Y y;
    151           INJECT(Z(fruit::Provider<Y> yProvider))
    152               : y(yProvider.get<Y>()) {
    153           }
    154 
    155           void foo() {
    156             y.foo();
    157           }
    158         };
    159 
    160         fruit::Component<Z> getZComponent() {
    161           return fruit::createComponent();
    162         }
    163 
    164         int main() {
    165           fruit::Injector<Z> injector(getZComponent);
    166           fruit::Provider<Z> provider(injector);
    167           // During provider.get<Z>(), yProvider.get() is called, and during that xProvider.get()
    168           // is called.
    169           Z z = provider.get<Z>();
    170           z.foo();
    171         }
    172         '''
    173     expect_success(
    174         COMMON_DEFINITIONS,
    175         source)
    176 
    177 def test_provider_get_error_type_not_provided():
    178     source = '''
    179         struct X {};
    180         struct Y {};
    181 
    182         void f(fruit::Provider<X> provider) {
    183           provider.get<Y>();
    184         }
    185         '''
    186     expect_compile_error(
    187         'TypeNotProvidedError<Y>',
    188         'Trying to get an instance of T, but it is not provided by this Provider/Injector.',
    189         COMMON_DEFINITIONS,
    190         source)
    191 
    192 @pytest.mark.parametrize('XVariant,XVariantRegex', [
    193     ('X**', r'X\*\*'),
    194     ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
    195     ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'),
    196     ('X* const', r'X\* const'),
    197     ('const X* const', r'const X\* const'),
    198     ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
    199     ('X*&', r'X\*&'),
    200     ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
    201     ('void', r'void'),
    202     ('fruit::Annotated<Annotation1, fruit::Annotated<Annotation1, X>>', r'fruit::Annotated<Annotation1, X>'),
    203 ])
    204 def test_provider_get_error_type_not_injectable(XVariant,XVariantRegex):
    205     source = '''
    206         struct X {};
    207 
    208         void f(fruit::Provider<X> provider) {
    209           provider.get<XVariant>();
    210         }
    211         '''
    212     expect_compile_error(
    213         'NonInjectableTypeError<XVariantRegex>',
    214         'The type T is not injectable',
    215         COMMON_DEFINITIONS,
    216         source,
    217         locals())
    218 
    219 @pytest.mark.parametrize('XProviderGetParam,XProviderGetParamRegex', [
    220     ('X&', 'X&'),
    221     ('X*', 'X\*'),
    222     ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
    223     ('fruit::Provider<X>', 'fruit::Provider<X>'),
    224 ])
    225 def test_const_provider_get_does_not_allow_injecting_nonconst_variants(XProviderGetParam, XProviderGetParamRegex):
    226     source = '''
    227         void f(fruit::Provider<const X> provider) {
    228           provider.get<XProviderGetParam>();
    229         }
    230         '''
    231     expect_compile_error(
    232         'TypeProvidedAsConstOnlyError<XProviderGetParamRegex>',
    233         'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector',
    234         COMMON_DEFINITIONS,
    235         source,
    236         locals())
    237 
    238 @pytest.mark.parametrize('Y_PROVIDER_ANNOT', [
    239     ('fruit::Provider<Y>'),
    240     ('ANNOTATED(Annotation1, fruit::Provider<Y>)'),
    241 ])
    242 def test_lazy_injection_with_annotations(Y_PROVIDER_ANNOT):
    243     source = '''
    244         struct Y : public ConstructionTracker<Y> {
    245           using Inject = Y();
    246         };
    247 
    248         struct X : public ConstructionTracker<X> {
    249           INJECT(X(Y_PROVIDER_ANNOT provider)) : provider(provider) {
    250           }
    251 
    252           void run() {
    253             Y* y(provider);
    254             (void) y;
    255           }
    256 
    257           fruit::Provider<Y> provider;
    258         };
    259 
    260         fruit::Component<X> getComponent() {
    261           return fruit::createComponent();
    262         }
    263         
    264         fruit::Component<> getEmptyComponent() {
    265           return fruit::createComponent();
    266         }
    267 
    268         int main() {
    269           fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
    270           fruit::Injector<X> injector(normalizedComponent, getComponent);
    271 
    272           Assert(X::num_objects_constructed == 0);
    273           Assert(Y::num_objects_constructed == 0);
    274 
    275           X* x(injector);
    276 
    277           Assert(X::num_objects_constructed == 1);
    278           Assert(Y::num_objects_constructed == 0);
    279 
    280           x->run();
    281 
    282           Assert(X::num_objects_constructed == 1);
    283           Assert(Y::num_objects_constructed == 1);
    284         }
    285         '''
    286     expect_success(
    287         COMMON_DEFINITIONS,
    288         source,
    289         locals())
    290 
    291 if __name__== '__main__':
    292     main(__file__)
    293