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 Annotation1 {};
     23     '''
     24 
     25 def escape_regex(regex):
     26     # We un-escape the space because we strip the spaces in fruit_test_common, and this would otherwise leave a
     27     # stray backslash.
     28     return re.escape(regex).replace('\\ ', ' ')
     29 
     30 def test_bind_instance_success():
     31     source = '''
     32         struct X {
     33           int n;
     34 
     35           X(int n)
     36             : n(n) {
     37           }
     38         };
     39 
     40         fruit::Component<X> getComponent(X* x) {
     41           return fruit::createComponent()
     42             .bindInstance(*x);
     43         }
     44 
     45         int main() {
     46           X x(34);
     47           fruit::Injector<X> injector(getComponent, &x);
     48           X& x1 = injector.get<X&>();
     49           Assert(&x == &x1);
     50         }
     51         '''
     52     expect_success(COMMON_DEFINITIONS, source)
     53 
     54 def test_bind_instance_annotated_success():
     55     source = '''
     56         struct X {
     57           int n;
     58 
     59           X(int n)
     60             : n(n) {
     61           }
     62         };
     63 
     64         fruit::Component<fruit::Annotated<Annotation1, X>> getComponent(X* x) {
     65           return fruit::createComponent()
     66             .bindInstance<fruit::Annotated<Annotation1, X>>(*x);
     67         }
     68 
     69         int main() {
     70           X x(34);
     71           fruit::Injector<fruit::Annotated<Annotation1, X>> injector(getComponent, &x);
     72           X& x1 = injector.get<fruit::Annotated<Annotation1, X&>>();
     73           Assert(&x == &x1);
     74         }
     75         '''
     76     expect_success(COMMON_DEFINITIONS, source)
     77 
     78 def test_bind_const_instance_success():
     79     source = '''
     80         struct X {
     81           int n;
     82 
     83           X(int n)
     84             : n(n) {
     85           }
     86         };
     87 
     88         fruit::Component<const X> getComponent(const X* x) {
     89           return fruit::createComponent()
     90             .bindInstance(*x);
     91         }
     92 
     93         const X x(34);
     94         
     95         int main() {
     96           fruit::Injector<const X> injector(getComponent, &x);
     97           const X& x1 = injector.get<const X&>();
     98           Assert(&x == &x1);
     99         }
    100         '''
    101     expect_success(COMMON_DEFINITIONS, source)
    102 
    103 def test_bind_const_instance_annotated_success():
    104     source = '''
    105         struct X {
    106           int n;
    107 
    108           X(int n)
    109             : n(n) {
    110           }
    111         };
    112 
    113         fruit::Component<fruit::Annotated<Annotation1, const X>> getComponent(const X* x) {
    114           return fruit::createComponent()
    115             .bindInstance<fruit::Annotated<Annotation1, X>>(*x);
    116         }
    117 
    118         const X x(34);
    119         
    120         int main() {
    121           fruit::Injector<fruit::Annotated<Annotation1, const X>> injector(getComponent, &x);
    122           const X& x1 = injector.get<fruit::Annotated<Annotation1, const X&>>();
    123           Assert(&x == &x1);
    124         }
    125         '''
    126     expect_success(COMMON_DEFINITIONS, source)
    127 
    128 @pytest.mark.parametrize('XAnnot,MaybeConstXAnnot,XPtr,XPtrAnnot', [
    129     ('X', 'X', 'X*', 'X*'),
    130     ('X', 'const X', 'const X*', 'const X*'),
    131     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'X*', 'fruit::Annotated<Annotation1, X*>'),
    132     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'const X*', 'fruit::Annotated<Annotation1, const X*>'),
    133 ])
    134 def test_bind_instance_two_explicit_type_arguments_success(XAnnot, MaybeConstXAnnot, XPtr, XPtrAnnot):
    135     source = '''
    136         struct X {
    137           int n;
    138 
    139           X(int n)
    140             : n(n) {
    141           }
    142         };
    143 
    144         fruit::Component<MaybeConstXAnnot> getComponent(XPtr x) {
    145           return fruit::createComponent()
    146             .bindInstance<XAnnot, X>(*x);
    147         }
    148 
    149         int main() {
    150           X x(34);
    151           fruit::Injector<MaybeConstXAnnot> injector(getComponent, &x);
    152           XPtr x1 = injector.get<XPtrAnnot>();
    153           Assert(&x == x1);
    154         }
    155         '''
    156     expect_success(COMMON_DEFINITIONS, source, locals())
    157 
    158 @pytest.mark.parametrize('XAnnot', [
    159     'X',
    160     'fruit::Annotated<Annotation1, X>',
    161 ])
    162 def test_bind_instance_abstract_class_ok(XAnnot):
    163     source = '''
    164         struct X {
    165           virtual void foo() = 0;
    166         };
    167         
    168         fruit::Component<> getComponentForInstanceHelper(X* x) {
    169           return fruit::createComponent()
    170             .bindInstance<XAnnot, X>(*x);
    171         }
    172 
    173         fruit::Component<XAnnot> getComponentForInstance(X* x) {
    174           return fruit::createComponent()
    175             .install(getComponentForInstanceHelper, x)
    176             .bindInstance<XAnnot, X>(*x);
    177         }
    178         '''
    179     expect_success(
    180         COMMON_DEFINITIONS,
    181         source,
    182         locals())
    183 
    184 @pytest.mark.parametrize('intAnnot,intPtrAnnot', [
    185     ('int', 'int*'),
    186     ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
    187 ])
    188 def test_bind_instance_multiple_equivalent_bindings_success(intAnnot, intPtrAnnot):
    189     source = '''
    190         fruit::Component<> getComponentForInstanceHelper(int* n) {
    191           return fruit::createComponent()
    192             .bindInstance<intAnnot, int>(*n);
    193         }
    194         
    195         fruit::Component<intAnnot> getComponentForInstance(int* n) {
    196           return fruit::createComponent()
    197             .install(getComponentForInstanceHelper, n)
    198             .bindInstance<intAnnot, int>(*n);
    199         }
    200 
    201         int main() {
    202           int n = 5;
    203           fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
    204           if (injector.get<intPtrAnnot>() != &n)
    205             abort();
    206         }
    207         '''
    208     expect_success(
    209         COMMON_DEFINITIONS,
    210         source,
    211         locals())
    212 
    213 @pytest.mark.parametrize('intAnnot,intPtrAnnot', [
    214     ('int', 'int*'),
    215     ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
    216 ])
    217 def test_bind_instance_multiple_equivalent_bindings_different_constness_success(intAnnot, intPtrAnnot):
    218     source = '''
    219         fruit::Component<> getComponentForInstanceHelper(const int* n) {
    220           return fruit::createComponent()
    221             .bindInstance<intAnnot, int>(*n);
    222         }
    223         
    224         fruit::Component<intAnnot> getComponentForInstance(int* n) {
    225           return fruit::createComponent()
    226             .install(getComponentForInstanceHelper, n)
    227             .bindInstance<intAnnot, int>(*n);
    228         }
    229 
    230         int main() {
    231           int n = 5;
    232           fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
    233           if (injector.get<intPtrAnnot>() != &n)
    234             abort();
    235         }
    236         '''
    237     expect_success(
    238         COMMON_DEFINITIONS,
    239         source,
    240         locals())
    241 
    242 @pytest.mark.parametrize('intAnnot,intPtrAnnot', [
    243     ('int', 'int*'),
    244     ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
    245 ])
    246 def test_bind_instance_multiple_equivalent_bindings_different_constness_other_order_success(intAnnot, intPtrAnnot):
    247     source = '''
    248         fruit::Component<> getComponentForInstanceHelper(const int* n) {
    249           return fruit::createComponent()
    250             .bindInstance<intAnnot, int>(*n);
    251         }
    252         
    253         fruit::Component<intAnnot> getComponentForInstance(int* n) {
    254           return fruit::createComponent()
    255             .bindInstance<intAnnot, int>(*n)
    256             .install(getComponentForInstanceHelper, n);
    257         }
    258 
    259         int main() {
    260           int n = 5;
    261           fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
    262           if (injector.get<intPtrAnnot>() != &n)
    263             abort();
    264         }
    265         '''
    266     expect_success(
    267         COMMON_DEFINITIONS,
    268         source,
    269         locals())
    270 
    271 @pytest.mark.parametrize('XVariant', [
    272     'X*',
    273     'const X*',
    274     'std::shared_ptr<X>',
    275 ])
    276 def test_bind_instance_non_normalized_type_error(XVariant):
    277     if XVariant.endswith('&'):
    278         XVariantRegexp = escape_regex(XVariant[:-1])
    279     else:
    280         XVariantRegexp = escape_regex(XVariant)
    281     source = '''
    282         struct X {};
    283 
    284         fruit::Component<> getComponent(XVariant x) {
    285           return fruit::createComponent()
    286             .bindInstance(x);
    287         }
    288         '''
    289     expect_compile_error(
    290         'NonClassTypeError<XVariantRegexp,X>',
    291         'A non-class type T was specified. Use C instead.',
    292         COMMON_DEFINITIONS,
    293         source,
    294         locals())
    295 
    296 @pytest.mark.parametrize('XVariant,XVariantRegexp', [
    297     ('const X', 'const X'),
    298     ('X*', 'X\*'),
    299     ('const X*', 'const X\*'),
    300     ('X&', 'X&'),
    301     ('const X&', 'const X&'),
    302     ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
    303 ])
    304 def test_bind_instance_non_normalized_type_error_with_annotation(XVariant, XVariantRegexp):
    305     source = '''
    306         struct X {};
    307 
    308         fruit::Component<> getComponent(XVariant x) {
    309           return fruit::createComponent()
    310             .bindInstance<fruit::Annotated<Annotation1, XVariant>>(x);
    311         }
    312         '''
    313     expect_compile_error(
    314         'NonClassTypeError<XVariantRegexp,X>',
    315         'A non-class type T was specified. Use C instead.',
    316         COMMON_DEFINITIONS,
    317         source,
    318         locals())
    319 
    320 @pytest.mark.parametrize('XAnnotVariant,XVariant', [
    321     ('const X', 'const X'),
    322     ('X*', 'X*'),
    323     ('const X*', 'const X*'),
    324     ('X&', 'X&'),
    325     ('const X&', 'const X&'),
    326     ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
    327 
    328     ('fruit::Annotated<Annotation1, const X>', 'const X'),
    329     ('fruit::Annotated<Annotation1, X*>', 'X*'),
    330     ('fruit::Annotated<Annotation1, const X*>', 'const X*'),
    331     ('fruit::Annotated<Annotation1, X&>', 'X&'),
    332     ('fruit::Annotated<Annotation1, const X&>', 'const X&'),
    333     ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'),
    334 
    335     ('fruit::Annotated<Annotation1, X>', 'const X'),
    336     ('fruit::Annotated<Annotation1, X>', 'X*'),
    337     ('fruit::Annotated<Annotation1, X>', 'const X*'),
    338     ('fruit::Annotated<Annotation1, X>', 'X&'),
    339     ('fruit::Annotated<Annotation1, X>', 'const X&'),
    340     ('fruit::Annotated<Annotation1, X>', 'std::shared_ptr<X>'),
    341 ])
    342 def test_bind_instance_non_normalized_type_error_two_explicit_type_arguments(XAnnotVariant, XVariant):
    343     XVariantRegexp = escape_regex(XVariant)
    344     source = '''
    345         struct X {};
    346 
    347         fruit::Component<> getComponent(XVariant x) {
    348           return fruit::createComponent()
    349             .bindInstance<XAnnotVariant, XVariant>(x);
    350         }
    351         '''
    352     expect_compile_error(
    353         'NonClassTypeError<XVariantRegexp,X>',
    354         'A non-class type T was specified. Use C instead.',
    355         COMMON_DEFINITIONS,
    356         source,
    357         locals())
    358 
    359 @pytest.mark.parametrize('XVariant,XVariantRegex', [
    360     ('X*', 'X\*'),
    361     ('const X*', 'const X\*'),
    362     ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
    363 ])
    364 def test_register_instance_error_must_be_reference(XVariant, XVariantRegex):
    365     source = '''
    366         struct X {};
    367 
    368         fruit::Component<> getComponentForInstance(XVariant x) {
    369           return fruit::createComponent()
    370               .bindInstance(x);
    371         }
    372         '''
    373     expect_compile_error(
    374         'NonClassTypeError<XVariantRegex,X>',
    375         'A non-class type T was specified. Use C instead.',
    376         COMMON_DEFINITIONS,
    377         source,
    378         locals())
    379 
    380 @pytest.mark.parametrize('XVariant,XVariantRegex', [
    381     ('X*', 'X\*'),
    382     ('const X*', 'const X\*'),
    383     ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
    384 ])
    385 def test_register_instance_error_must_be_reference_with_annotation(XVariant, XVariantRegex):
    386     source = '''
    387         struct X {};
    388 
    389         fruit::Component<> getComponentForInstance(XVariant x) {
    390           return fruit::createComponent()
    391               .bindInstance<fruit::Annotated<Annotation1, X>>(x);
    392         }
    393         '''
    394     expect_compile_error(
    395         'NonClassTypeError<XVariantRegex,X>',
    396         'A non-class type T was specified. Use C instead.',
    397         COMMON_DEFINITIONS,
    398         source,
    399         locals())
    400 
    401 @pytest.mark.parametrize('XAnnot', [
    402     'X',
    403     'fruit::Annotated<Annotation1, X>',
    404 ])
    405 def test_bind_instance_mismatched_type_arguments(XAnnot):
    406     source = '''
    407         struct X {};
    408 
    409         fruit::Component<> getComponent(int* n) {
    410           return fruit::createComponent()
    411             .bindInstance<XAnnot, int>(*n);
    412         }
    413         '''
    414     expect_compile_error(
    415         'TypeMismatchInBindInstanceError<X,int>',
    416         'A type parameter was specified in bindInstance.. but it doesn.t match the value type',
    417         COMMON_DEFINITIONS,
    418         source,
    419         locals())
    420 
    421 @pytest.mark.parametrize('BaseAnnot,BasePtrAnnot', [
    422     ('Base', 'Base*'),
    423     ('fruit::Annotated<Annotation1, Base>', 'fruit::Annotated<Annotation1, Base*>'),
    424 ])
    425 def test_bind_instance_to_subclass(BaseAnnot, BasePtrAnnot):
    426     source = '''
    427         struct Base {
    428           virtual void f() = 0;
    429           virtual ~Base() {
    430           }
    431         };
    432 
    433         struct Derived : public Base {
    434           void f() override {
    435           }
    436         };
    437 
    438         fruit::Component<BaseAnnot> getComponent(Derived* derived) {
    439           return fruit::createComponent()
    440             .bindInstance<BaseAnnot, Base>(*derived);
    441         }
    442 
    443         int main() {
    444           Derived derived;
    445           fruit::Injector<BaseAnnot> injector(getComponent, &derived);
    446           Base* base = injector.get<BasePtrAnnot>();
    447           base->f();
    448         }
    449         '''
    450     expect_success(COMMON_DEFINITIONS, source, locals())
    451 
    452 @pytest.mark.parametrize('XVariant,XVariantRegex', [
    453     ('X**', r'X\*\*'),
    454     ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
    455     ('X*&', r'X\*&'),
    456     ('fruit::Annotated<Annotation1, X**>', r'X\*\*'),
    457 ])
    458 def test_bind_instance_type_not_normalized(XVariant, XVariantRegex):
    459     source = '''
    460         struct X {};
    461 
    462         using XVariantT = XVariant;
    463         fruit::Component<> getComponent(XVariantT x) {
    464           return fruit::createComponent()
    465             .bindInstance<XVariant, XVariant>(x);
    466         }
    467         '''
    468     expect_compile_error(
    469         'NonClassTypeError<XVariantRegex,X>',
    470         'A non-class type T was specified.',
    471         COMMON_DEFINITIONS,
    472         source,
    473         locals())
    474 
    475 @pytest.mark.parametrize('XVariant,XVariantRegex', [
    476     ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
    477 ])
    478 def test_bind_instance_type_not_injectable_error(XVariant, XVariantRegex):
    479     source = '''
    480         struct X {};
    481         
    482         using XVariantT = XVariant;
    483         fruit::Component<> getComponent(XVariantT x) {
    484           return fruit::createComponent()
    485             .bindInstance<XVariant, XVariant>(x);
    486         }
    487         '''
    488     expect_compile_error(
    489         'NonInjectableTypeError<XVariantRegex>',
    490         'The type T is not injectable.',
    491         COMMON_DEFINITIONS,
    492         source,
    493         locals())
    494 
    495 if __name__== '__main__':
    496     main(__file__)
    497