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     struct Annotation2 {};
     24     '''
     25 
     26 @pytest.mark.parametrize('XAnnot,MaybeConstXAnnot,XConstRefAnnot,YAnnot', [
     27     ('X', 'X', 'const X&', 'Y'),
     28     ('X', 'const X', 'const X&', 'Y'),
     29     ('X', 'X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
     30     ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
     31     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
     32     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
     33     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
     34     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
     35     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
     36     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
     37 ])
     38 def test_bind_interface(XAnnot, MaybeConstXAnnot, XConstRefAnnot, YAnnot):
     39     source = '''
     40         struct X {
     41           virtual void f() const = 0;
     42         };
     43 
     44         struct Y : public X {
     45           INJECT(Y()) = default;
     46           
     47           void f() const override {
     48           }
     49         };
     50 
     51         fruit::Component<MaybeConstXAnnot> getComponent() {
     52           return fruit::createComponent()
     53             .bind<XAnnot, YAnnot>();
     54         }
     55 
     56         int main() {
     57           fruit::Injector<MaybeConstXAnnot> injector(getComponent);
     58           const X& x = injector.get<XConstRefAnnot>();
     59           x.f();
     60         }
     61     '''
     62     expect_success(
     63         COMMON_DEFINITIONS,
     64         source,
     65         locals())
     66 
     67 @pytest.mark.parametrize('XAnnot,ConstXAnnot,XConstRefAnnot,YAnnot', [
     68     ('X', 'const X', 'const X&', 'Y'),
     69     ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
     70     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
     71     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
     72     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
     73 ])
     74 def test_bind_interface_to_constant(XAnnot, ConstXAnnot, XConstRefAnnot, YAnnot):
     75     source = '''
     76         struct X {
     77           virtual void f() const = 0;
     78         };
     79 
     80         struct Y : public X {
     81           void f() const override {
     82           }
     83         };
     84         
     85         const Y y{};
     86 
     87         fruit::Component<ConstXAnnot> getComponent() {
     88           return fruit::createComponent()
     89             .bindInstance<YAnnot, Y>(y)
     90             .bind<XAnnot, YAnnot>();
     91         }
     92 
     93         int main() {
     94           fruit::Injector<ConstXAnnot> injector(getComponent);
     95           const X& x = injector.get<XConstRefAnnot>();
     96           x.f();
     97         }
     98     '''
     99     expect_success(
    100         COMMON_DEFINITIONS,
    101         source,
    102         locals())
    103 
    104 @pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [
    105     ('X', 'X&', 'Y'),
    106     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
    107 ])
    108 def test_bind_interface_target_bound_in_other_component(XAnnot, XRefAnnot, YAnnot):
    109     source = '''
    110         struct X {
    111           virtual void f() = 0;
    112         };
    113 
    114         struct Y : public X {
    115           void f() override {
    116           }
    117         };
    118 
    119         fruit::Component<fruit::Required<YAnnot>, XAnnot> getComponent() {
    120           return fruit::createComponent()
    121             .bind<XAnnot, YAnnot>();
    122         }
    123 
    124         fruit::Component<XAnnot> getRootComponent() {
    125           return fruit::createComponent()
    126             .registerConstructor<YAnnot()>()
    127             .install(getComponent);
    128         }
    129 
    130         int main() {
    131           fruit::Injector<XAnnot> injector(getRootComponent);
    132           X& x = injector.get<XRefAnnot>();
    133           x.f();
    134         }
    135     '''
    136     expect_success(
    137         COMMON_DEFINITIONS,
    138         source,
    139         locals())
    140 
    141 @pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [
    142     ('X', 'X&', 'Y'),
    143     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
    144 ])
    145 def test_bind_nonconst_interface_requires_nonconst_target(XAnnot, XRefAnnot, YAnnot):
    146     source = '''
    147         struct X {
    148           virtual void f() = 0;
    149         };
    150 
    151         struct Y : public X {
    152           void f() override {
    153           }
    154         };
    155 
    156         fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() {
    157           return fruit::createComponent()
    158             .bind<XAnnot, YAnnot>();
    159         }
    160     '''
    161     expect_compile_error(
    162         'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>',
    163         'The type T was declared as a const Required type in the returned Component, however a non-const binding',
    164         COMMON_DEFINITIONS,
    165         source,
    166         locals())
    167 
    168 @pytest.mark.parametrize('XAnnot,YAnnot', [
    169     ('X', 'Y'),
    170     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
    171 ])
    172 def test_bind_interface_to_constant_nonconst_required_const_bound_error(XAnnot, YAnnot):
    173     source = '''
    174         struct X {
    175           virtual void f() const = 0;
    176         };
    177 
    178         struct Y : public X {
    179           void f() const override {
    180           }
    181         };
    182         
    183         const Y y{};
    184 
    185         fruit::Component<XAnnot> getComponent() {
    186           return fruit::createComponent()
    187             .bindInstance<YAnnot, Y>(y)
    188             .bind<XAnnot, YAnnot>();
    189         }
    190     '''
    191     expect_compile_error(
    192         'NonConstBindingRequiredButConstBindingProvidedError<YAnnot>',
    193         'The type T was provided as constant, however one of the constructors/providers/factories in this component',
    194         COMMON_DEFINITIONS,
    195         source,
    196         locals())
    197 
    198 @pytest.mark.parametrize('XAnnot,XRefAnnot,YAnnot', [
    199     ('X', 'X&', 'Y'),
    200     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
    201 ])
    202 def test_bind_nonconst_interface_requires_nonconst_target_abstract(XAnnot, XRefAnnot, YAnnot):
    203     source = '''
    204         struct X {
    205           virtual void f() = 0;
    206         };
    207 
    208         struct Y : public X {};
    209 
    210         fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() {
    211           return fruit::createComponent()
    212             .bind<XAnnot, YAnnot>();
    213         }
    214     '''
    215     expect_compile_error(
    216         'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>',
    217         'The type T was declared as a const Required type in the returned Component, however a non-const binding',
    218         COMMON_DEFINITIONS,
    219         source,
    220         locals())
    221 
    222 @pytest.mark.parametrize('XAnnot,intAnnot', [
    223     ('X', 'int'),
    224     ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, int>'),
    225 ])
    226 def test_error_not_base(XAnnot, intAnnot):
    227     source = '''
    228         struct X {};
    229 
    230         fruit::Component<intAnnot> getComponent() {
    231           return fruit::createComponent()
    232             .bind<XAnnot, intAnnot>();
    233         }
    234         '''
    235     expect_compile_error(
    236         'NotABaseClassOfError<X,int>',
    237         'I is not a base class of C.',
    238         COMMON_DEFINITIONS,
    239         source,
    240         locals())
    241 
    242 # TODO: maybe the error should include the annotation here.
    243 @pytest.mark.parametrize('XAnnot', [
    244     'X',
    245     'fruit::Annotated<Annotation1, X>',
    246 ])
    247 def test_error_bound_to_itself(XAnnot):
    248     source = '''
    249         struct X {};
    250 
    251         fruit::Component<X> getComponent() {
    252           return fruit::createComponent()
    253             .bind<XAnnot, XAnnot>();
    254         }
    255         '''
    256     expect_compile_error(
    257         'InterfaceBindingToSelfError<X>',
    258         'The type C was bound to itself.',
    259         COMMON_DEFINITIONS,
    260         source,
    261         locals())
    262 
    263 def test_bound_to_itself_with_annotation_error():
    264     source = '''
    265         struct X {};
    266 
    267         fruit::Component<> getComponent() {
    268           return fruit::createComponent()
    269             .registerConstructor<X()>()
    270             .bind<fruit::Annotated<Annotation1, X>, X>();
    271         }
    272         '''
    273     expect_compile_error(
    274         'InterfaceBindingToSelfError<X>',
    275         'The type C was bound to itself.',
    276         COMMON_DEFINITIONS,
    277         source)
    278 
    279 def test_bound_chain_ok():
    280     source = '''
    281         struct X {
    282           virtual void f() = 0;
    283         };
    284 
    285         struct Y : public X {};
    286 
    287         struct Z : public Y {
    288           INJECT(Z()) = default;
    289           void f() override {
    290           }
    291         };
    292 
    293         fruit::Component<X> getComponent() {
    294           return fruit::createComponent()
    295             .bind<X, Y>()
    296             .bind<Y, Z>();
    297         }
    298 
    299         int main() {
    300           fruit::Injector<X> injector(getComponent);
    301           X& x = injector.get<X&>();
    302           x.f();
    303         }
    304     '''
    305     expect_success(COMMON_DEFINITIONS, source)
    306 
    307 def test_bind_non_normalized_types_error():
    308     source = '''
    309         struct X {};
    310 
    311         struct Y : public std::shared_ptr<X> {};
    312 
    313         fruit::Component<> getComponent() {
    314           return fruit::createComponent()
    315             .bind<std::shared_ptr<X>, Y>();
    316         }
    317         '''
    318     expect_compile_error(
    319         'NonClassTypeError<std::shared_ptr<X>,X>',
    320         'A non-class type T was specified. Use C instead',
    321         COMMON_DEFINITIONS,
    322         source)
    323 
    324 if __name__== '__main__':
    325     main(__file__)
    326