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     struct Annotation3 {};
     30     
     31     template <typename T>
     32     using WithNoAnnotation = T;
     33     
     34     template <typename T>
     35     using WithAnnotation1 = fruit::Annotated<Annotation1, T>;
     36     '''
     37 
     38 def test_register_constructor_success_copyable_and_movable():
     39     source = '''
     40         struct X {
     41           INJECT(X()) = default;
     42           X(X&&) = default;
     43           X(const X&) = default;
     44         };
     45 
     46         fruit::Component<X> getComponent() {
     47           return fruit::createComponent();
     48         }
     49 
     50         int main() {
     51           fruit::Injector<X> injector(getComponent);
     52           injector.get<X*>();
     53         }
     54         '''
     55     expect_success(
     56         COMMON_DEFINITIONS,
     57         source)
     58 
     59 def test_register_constructor_success_movable_only():
     60     source = '''
     61         struct X {
     62           INJECT(X()) = default;
     63           X(X&&) = default;
     64           X(const X&) = delete;
     65         };
     66 
     67         fruit::Component<X> getComponent() {
     68           return fruit::createComponent();
     69         }
     70 
     71         int main() {
     72           fruit::Injector<X> injector(getComponent);
     73           injector.get<X*>();
     74         }
     75         '''
     76     expect_success(
     77         COMMON_DEFINITIONS,
     78         source)
     79 
     80 def test_register_constructor_success_not_movable():
     81     source = '''
     82         struct X {
     83           INJECT(X()) = default;
     84           X(X&&) = delete;
     85           X(const X&) = delete;
     86         };
     87 
     88         fruit::Component<X> getComponent() {
     89           return fruit::createComponent();
     90         }
     91 
     92         int main() {
     93           fruit::Injector<X> injector(getComponent);
     94           injector.get<X*>();
     95         }
     96         '''
     97     expect_success(
     98         COMMON_DEFINITIONS,
     99         source)
    100 
    101 # TODO: consider moving to test_normalized_component.py
    102 @pytest.mark.parametrize('XAnnot,YAnnot,MaybeConstYAnnot,ZAnnot', [
    103     ('X', 'Y', 'Y', 'Z'),
    104     ('X', 'Y', 'const Y', 'Z'),
    105     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation2, const Y>', 'fruit::Annotated<Annotation3, Z>'),
    106 ])
    107 def test_autoinject_with_annotation_success(XAnnot, YAnnot, MaybeConstYAnnot, ZAnnot):
    108     source = '''
    109         struct X {
    110           using Inject = X();
    111         };
    112 
    113         struct Y : public ConstructionTracker<Y> {
    114           using Inject = Y();
    115         };
    116 
    117         struct Z {
    118           using Inject = Z();
    119         };
    120 
    121         fruit::Component<ZAnnot, MaybeConstYAnnot, XAnnot> getComponent() {
    122           return fruit::createComponent();
    123         }
    124         
    125         fruit::Component<> getEmptyComponent() {
    126           return fruit::createComponent();
    127         }
    128 
    129         int main() {
    130           fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
    131           fruit::Injector<MaybeConstYAnnot> injector(normalizedComponent, getComponent);
    132 
    133           Assert(Y::num_objects_constructed == 0);
    134           injector.get<YAnnot>();
    135           Assert(Y::num_objects_constructed == 1);
    136         }
    137         '''
    138     expect_success(
    139         COMMON_DEFINITIONS,
    140         source,
    141         locals())
    142 
    143 def test_autoinject_annotation_in_signature_return_type():
    144     source = '''
    145         struct X {
    146           using Inject = XAnnot();
    147         };
    148 
    149         fruit::Component<XAnnot> getComponent() {
    150           return fruit::createComponent();
    151         }
    152         '''
    153     expect_compile_error(
    154         'InjectTypedefWithAnnotationError<X>',
    155         'C::Inject is a signature that returns an annotated type',
    156         COMMON_DEFINITIONS,
    157         source)
    158 
    159 def test_autoinject_wrong_class_in_typedef():
    160     source = '''
    161         struct X {
    162           using Inject = X();
    163         };
    164 
    165         struct Y : public X {
    166         };
    167 
    168         fruit::Component<Y> getComponent() {
    169           return fruit::createComponent();
    170         }
    171         '''
    172     expect_compile_error(
    173         'InjectTypedefForWrongClassError<Y,X>',
    174         'C::Inject is a signature, but does not return a C. Maybe the class C has no Inject typedef and',
    175         COMMON_DEFINITIONS,
    176         source)
    177 
    178 def test_register_constructor_error_abstract_class():
    179     source = '''
    180         struct X {
    181           X(int*) {}
    182 
    183           virtual void foo() = 0;
    184         };
    185 
    186         fruit::Component<X> getComponent() {
    187           return fruit::createComponent()
    188             .registerConstructor<fruit::Annotated<Annotation1, X>(int*)>();
    189         }
    190         '''
    191     if re.search('GNU|MSVC', CXX_COMPILER_NAME) is not None:
    192         expect_generic_compile_error(
    193             'invalid abstract return type'
    194             '|.X.: cannot instantiate abstract class',
    195             COMMON_DEFINITIONS,
    196             source)
    197     else:
    198         expect_compile_error(
    199             'CannotConstructAbstractClassError<X>',
    200             'The specified class can.t be constructed because it.s an abstract class',
    201             COMMON_DEFINITIONS,
    202             source)
    203 
    204 def test_register_constructor_error_malformed_signature():
    205     source = '''
    206         struct X {
    207           X(int) {}
    208         };
    209 
    210         fruit::Component<X> getComponent() {
    211           return fruit::createComponent()
    212             .registerConstructor<X[]>();
    213         }
    214         '''
    215     expect_compile_error(
    216         'NotASignatureError<X\[\]>',
    217         'CandidateSignature was specified as parameter, but it.s not a signature. Signatures are of the form',
    218         COMMON_DEFINITIONS,
    219         source)
    220 
    221 def test_register_constructor_error_malformed_signature_autoinject():
    222     source = '''
    223         struct X {
    224           using Inject = X[];
    225           X(int) {}
    226         };
    227 
    228         fruit::Component<X> getComponent() {
    229           return fruit::createComponent();
    230         }
    231         '''
    232     expect_compile_error(
    233         'InjectTypedefNotASignatureError<X,X\[\]>',
    234         'C::Inject should be a typedef to a signature',
    235         COMMON_DEFINITIONS,
    236         source)
    237 
    238 @pytest.mark.parametrize('charPtrAnnot', [
    239     'char*',
    240     'fruit::Annotated<Annotation1, char*>',
    241 ])
    242 def test_register_constructor_does_not_exist_error(charPtrAnnot):
    243     source = '''
    244         struct X {
    245           X(int*) {}
    246         };
    247 
    248         fruit::Component<X> getComponent() {
    249           return fruit::createComponent()
    250             .registerConstructor<X(charPtrAnnot)>();
    251         }
    252         '''
    253     expect_compile_error(
    254         'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>',
    255         'contains an Inject typedef but it.s not constructible with the specified types',
    256         COMMON_DEFINITIONS,
    257         source,
    258         locals())
    259 
    260 @pytest.mark.parametrize('charPtrAnnot', [
    261     'char*',
    262     'fruit::Annotated<Annotation1, char*>',
    263 ])
    264 def test_autoinject_constructor_does_not_exist_error(charPtrAnnot):
    265     source = '''
    266         struct X {
    267           using Inject = X(charPtrAnnot);
    268           X(int*) {}
    269         };
    270 
    271         fruit::Component<X> getComponent() {
    272           return fruit::createComponent();
    273         }
    274         '''
    275     expect_compile_error(
    276         'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>',
    277         'contains an Inject typedef but it.s not constructible with the specified types',
    278         COMMON_DEFINITIONS,
    279         source,
    280         locals())
    281 
    282 def test_autoinject_abstract_class_error():
    283     source = '''
    284         struct X {
    285           using Inject = fruit::Annotated<Annotation1, X>();
    286 
    287           virtual void scale() = 0;
    288           // Note: here we "forgot" to implement scale() (on purpose, for this test) so X is an abstract class.
    289         };
    290 
    291         fruit::Component<fruit::Annotated<Annotation1, X>> getComponent() {
    292           return fruit::createComponent();
    293         }
    294         '''
    295     expect_compile_error(
    296         'CannotConstructAbstractClassError<X>',
    297         'The specified class can.t be constructed because it.s an abstract class.',
    298         COMMON_DEFINITIONS,
    299         source)
    300 
    301 @pytest.mark.parametrize('WithAnnotation', [
    302     'WithNoAnnotation',
    303     'WithAnnotation1',
    304 ])
    305 @pytest.mark.parametrize('YVariant', [
    306     'Y',
    307     'const Y',
    308     'Y*',
    309     'const Y*',
    310     'Y&',
    311     'const Y&',
    312     'std::shared_ptr<Y>',
    313     'fruit::Provider<Y>',
    314     'fruit::Provider<const Y>',
    315 ])
    316 def test_register_constructor_with_param_success(WithAnnotation, YVariant):
    317     source = '''
    318         struct Y {};
    319         struct X {
    320           X(YVariant) {
    321           }
    322         };
    323         
    324         fruit::Component<WithAnnotation<Y>> getYComponent() {
    325           return fruit::createComponent()
    326             .registerConstructor<WithAnnotation<Y>()>();
    327         }
    328 
    329         fruit::Component<X> getComponent() {
    330           return fruit::createComponent()
    331             .install(getYComponent)
    332             .registerConstructor<X(WithAnnotation<YVariant>)>();
    333         }
    334 
    335         int main() {
    336           fruit::Injector<X> injector(getComponent);
    337           injector.get<X>();
    338         }
    339         '''
    340     expect_success(
    341         COMMON_DEFINITIONS,
    342         source,
    343         locals())
    344 
    345 @pytest.mark.parametrize('WithAnnotation', [
    346     'WithNoAnnotation',
    347     'WithAnnotation1',
    348 ])
    349 @pytest.mark.parametrize('YVariant', [
    350     'Y',
    351     'const Y',
    352     'const Y*',
    353     'const Y&',
    354     'fruit::Provider<const Y>',
    355 ])
    356 def test_register_constructor_with_param_const_binding_success(WithAnnotation, YVariant):
    357     source = '''
    358         struct Y {};
    359         struct X {
    360           X(YVariant) {
    361           }
    362         };
    363         
    364         const Y y{};
    365         
    366         fruit::Component<WithAnnotation<const Y>> getYComponent() {
    367           return fruit::createComponent()
    368             .bindInstance<WithAnnotation<Y>, Y>(y);
    369         }
    370 
    371         fruit::Component<X> getComponent() {
    372           return fruit::createComponent()
    373             .install(getYComponent)
    374             .registerConstructor<X(WithAnnotation<YVariant>)>();
    375         }
    376 
    377         int main() {
    378           fruit::Injector<X> injector(getComponent);
    379           injector.get<X>();
    380         }
    381         '''
    382     expect_success(
    383         COMMON_DEFINITIONS,
    384         source,
    385         locals())
    386 
    387 @pytest.mark.parametrize('WithAnnotation,YAnnotRegex', [
    388     ('WithNoAnnotation', 'Y'),
    389     ('WithAnnotation1', 'fruit::Annotated<Annotation1,Y>'),
    390 ])
    391 @pytest.mark.parametrize('YVariant', [
    392     'Y*',
    393     'Y&',
    394     'std::shared_ptr<Y>',
    395     'fruit::Provider<Y>',
    396 ])
    397 def test_register_constructor_with_param_error_nonconst_param_required(WithAnnotation, YAnnotRegex, YVariant):
    398     source = '''
    399         struct Y {};
    400         struct X {
    401           X(YVariant);
    402         };
    403         
    404         fruit::Component<WithAnnotation<const Y>> getYComponent();
    405 
    406         fruit::Component<> getComponent() {
    407           return fruit::createComponent()
    408             .install(getYComponent)
    409             .registerConstructor<X(WithAnnotation<YVariant>)>();
    410         }
    411         '''
    412     expect_compile_error(
    413         'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
    414         'The type T was provided as constant, however one of the constructors/providers/factories in this component',
    415         COMMON_DEFINITIONS,
    416         source,
    417         locals())
    418 
    419 @pytest.mark.parametrize('WithAnnotation,YAnnotRegex', [
    420     ('WithNoAnnotation', 'Y'),
    421     ('WithAnnotation1', 'fruit::Annotated<Annotation1, Y>'),
    422 ])
    423 @pytest.mark.parametrize('YVariant', [
    424     'Y*',
    425     'Y&',
    426     'std::shared_ptr<Y>',
    427     'fruit::Provider<Y>',
    428 ])
    429 def test_register_constructor_with_param_error_nonconst_param_required_install_after(WithAnnotation, YAnnotRegex, YVariant):
    430     source = '''
    431         struct Y {};
    432         struct X {
    433           X(YVariant);
    434         };
    435         
    436         fruit::Component<WithAnnotation<const Y>> getYComponent();
    437 
    438         fruit::Component<> getComponent() {
    439           return fruit::createComponent()
    440             .registerConstructor<X(WithAnnotation<YVariant>)>()
    441             .install(getYComponent);
    442         }
    443         '''
    444     expect_compile_error(
    445         'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
    446         'The type T was provided as constant, however one of the constructors/providers/factories in this component',
    447         COMMON_DEFINITIONS,
    448         source,
    449         locals())
    450 
    451 def test_register_constructor_requiring_nonconst_then_requiring_const_ok():
    452     source = '''
    453         struct X {};
    454         
    455         struct Y {
    456           Y(X&) {}
    457         };
    458         
    459         struct Z {
    460           Z(const X&) {}
    461         };
    462 
    463         fruit::Component<Y, Z> getRootComponent() {
    464           return fruit::createComponent()
    465             .registerConstructor<Y(X&)>()
    466             .registerConstructor<Z(const X&)>()
    467             .registerConstructor<X()>();
    468         }
    469         
    470         int main() {
    471           fruit::Injector<Y, Z> injector(getRootComponent);
    472           injector.get<Y>();
    473           injector.get<Z>();
    474         }
    475         '''
    476     expect_success(
    477         COMMON_DEFINITIONS,
    478         source,
    479         locals())
    480 
    481 def test_register_constructor_requiring_nonconst_then_requiring_const_declaring_const_requirement_error():
    482     source = '''
    483         struct X {};
    484         
    485         struct Y {
    486           Y(X&) {}
    487         };
    488         
    489         struct Z {
    490           Z(const X&) {}
    491         };
    492 
    493         fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
    494           return fruit::createComponent()
    495             .registerConstructor<Y(X&)>()
    496             .registerConstructor<Z(const X&)>();
    497         }
    498         '''
    499     expect_compile_error(
    500         'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
    501         'The type T was declared as a const Required type in the returned Component, however',
    502         COMMON_DEFINITIONS,
    503         source,
    504         locals())
    505 
    506 def test_register_constructor_requiring_const_then_requiring_nonconst_ok():
    507     source = '''
    508         struct X {};
    509         
    510         struct Y {
    511           Y(const X&) {}
    512         };
    513         
    514         struct Z {
    515           Z(X&) {}
    516         };
    517 
    518         fruit::Component<Y, Z> getRootComponent() {
    519           return fruit::createComponent()
    520             .registerConstructor<Y(const X&)>()
    521             .registerConstructor<Z(X&)>()
    522             .registerConstructor<X()>();
    523         }
    524         
    525         int main() {
    526           fruit::Injector<Y, Z> injector(getRootComponent);
    527           injector.get<Y>();
    528           injector.get<Z>();
    529         }
    530         '''
    531     expect_success(
    532         COMMON_DEFINITIONS,
    533         source,
    534         locals())
    535 
    536 def test_register_constructor_requiring_const_then_requiring_nonconst_declaring_const_requirement_error():
    537     source = '''
    538         struct X {};
    539         
    540         struct Y {
    541           Y(const X&) {}
    542         };
    543         
    544         struct Z {
    545           Z(X&) {}
    546         };
    547 
    548         fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
    549           return fruit::createComponent()
    550             .registerConstructor<Y(const X&)>()
    551             .registerConstructor<Z(X&)>();
    552         }
    553         '''
    554     expect_compile_error(
    555         'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
    556         'The type T was declared as a const Required type in the returned Component, however',
    557         COMMON_DEFINITIONS,
    558         source,
    559         locals())
    560 
    561 @pytest.mark.parametrize('YVariant,YVariantRegex', [
    562     ('Y**', r'Y\*\*'),
    563     ('std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
    564     ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
    565     ('Y*&', r'Y\*&'),
    566     ('Y(*)()', r'Y(\((__cdecl)?\*\))?\((void)?\)'),
    567     ('fruit::Annotated<Annotation1, Y**>', r'Y\*\*'),
    568 ])
    569 def test_register_constructor_with_param_error_type_not_injectable(YVariant, YVariantRegex):
    570     source = '''
    571         struct Y {};
    572         struct X {
    573           X(YVariant);
    574         };
    575         
    576         fruit::Component<> getComponent() {
    577           return fruit::createComponent()
    578             .registerConstructor<X(YVariant)>();
    579         }
    580         '''
    581     expect_compile_error(
    582         'NonInjectableTypeError<YVariantRegex>',
    583         'The type T is not injectable.',
    584         COMMON_DEFINITIONS,
    585         source,
    586         locals())
    587 
    588 
    589 if __name__== '__main__':
    590     main(__file__)
    591