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.
15import pytest
16
17from fruit_test_common import *
18from fruit_test_config import CXX_COMPILER_NAME
19import re
20
21COMMON_DEFINITIONS = '''
22    #include "test_common.h"
23
24    struct Annotation {};
25    struct Annotation1 {};
26    struct Annotation2 {};
27    '''
28
29@pytest.mark.parametrize('XAnnot,XImplAnnot', [
30    ('X', 'XImpl'),
31    ('X', 'fruit::Annotated<Annotation2, XImpl>'),
32    ('fruit::Annotated<Annotation1, X>', 'XImpl'),
33    ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, XImpl>'),
34])
35def test_add_interface_multibinding_success(XAnnot, XImplAnnot):
36    source = '''
37        struct X {
38          virtual int foo() = 0;
39        };
40
41        struct XImpl : public X {
42          INJECT(XImpl()) = default;
43
44          int foo() override {
45            return 5;
46          }
47        };
48
49        fruit::Component<> getComponent() {
50          return fruit::createComponent()
51            .addMultibinding<XAnnot, XImplAnnot>();
52        }
53
54        int main() {
55          fruit::Injector<> injector(getComponent);
56
57          std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
58          Assert(multibindings.size() == 1);
59          Assert(multibindings[0]->foo() == 5);
60        }
61        '''
62    expect_success(
63        COMMON_DEFINITIONS,
64        source,
65        locals())
66
67@pytest.mark.parametrize('XAnnot,XImplAnnot,ConstXImplAnnot', [
68    ('X', 'XImpl', 'const XImpl'),
69    ('X', 'fruit::Annotated<Annotation2, XImpl>', 'fruit::Annotated<Annotation2, const XImpl>'),
70])
71def test_add_interface_multibinding_const_target_error_install_first(XAnnot, XImplAnnot, ConstXImplAnnot):
72    source = '''
73        struct X {
74          virtual int foo() = 0;
75        };
76
77        struct XImpl : public X {
78          int foo() override {
79            return 5;
80          }
81        };
82
83        fruit::Component<ConstXImplAnnot> getXImplComponent();
84
85        fruit::Component<> getComponent() {
86          return fruit::createComponent()
87            .install(getXImplComponent)
88            .addMultibinding<XAnnot, XImplAnnot>();
89        }
90        '''
91    expect_compile_error(
92        'NonConstBindingRequiredButConstBindingProvidedError<XImplAnnot>',
93        'The type T was provided as constant, however one of the constructors/providers/factories in this component',
94        COMMON_DEFINITIONS,
95        source,
96        locals())
97
98@pytest.mark.parametrize('XAnnot,XImplAnnot,ConstXImplAnnot', [
99    ('X', 'XImpl', 'const XImpl'),
100    ('X', 'fruit::Annotated<Annotation2, XImpl>', 'fruit::Annotated<Annotation2, const XImpl>'),
101])
102def test_add_interface_multibinding_const_target_error_binding_first(XAnnot, XImplAnnot, ConstXImplAnnot):
103    source = '''
104        struct X {
105          virtual int foo() = 0;
106        };
107
108        struct XImpl : public X {
109          int foo() override {
110            return 5;
111          }
112        };
113
114        fruit::Component<ConstXImplAnnot> getXImplComponent();
115
116        fruit::Component<> getComponent() {
117          return fruit::createComponent()
118            .addMultibinding<XAnnot, XImplAnnot>()
119            .install(getXImplComponent);
120        }
121        '''
122    expect_compile_error(
123        'NonConstBindingRequiredButConstBindingProvidedError<XImplAnnot>',
124        'The type T was provided as constant, however one of the constructors/providers/factories in this component',
125        COMMON_DEFINITIONS,
126        source,
127        locals())
128
129@pytest.mark.parametrize('XAnnot,intAnnot', [
130    ('X', 'int'),
131    ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, int>'),
132])
133def test_error_not_base(XAnnot, intAnnot):
134    source = '''
135        struct X {};
136
137        fruit::Component<> getComponent() {
138          return fruit::createComponent()
139            .addMultibinding<XAnnot, intAnnot>();
140        }
141        '''
142    expect_compile_error(
143        'NotABaseClassOfError<X,int>',
144        'I is not a base class of C.',
145        COMMON_DEFINITIONS,
146        source,
147        locals())
148
149@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot', [
150    ('Scaler', 'ScalerImpl'),
151    ('fruit::Annotated<Annotation1, Scaler>', 'fruit::Annotated<Annotation2, ScalerImpl>'),
152])
153def test_error_abstract_class(ScalerAnnot, ScalerImplAnnot):
154    source = '''
155        struct Scaler {
156          virtual double scale(double x) = 0;
157        };
158
159        struct ScalerImpl : public Scaler {
160          // Note: here we "forgot" to implement scale() (on purpose, for this test) so ScalerImpl is an abstract class.
161        };
162
163        fruit::Component<> getComponent() {
164          return fruit::createComponent()
165            .addMultibinding<ScalerAnnot, ScalerImplAnnot>();
166        }
167        '''
168    expect_compile_error(
169        'NoBindingFoundForAbstractClassError<ScalerImplAnnot,ScalerImpl>',
170        'No explicit binding was found for T, and note that C is an abstract class',
171        COMMON_DEFINITIONS,
172        source,
173        locals())
174
175@pytest.mark.parametrize('ScalerAnnot,ScalerImplAnnot', [
176    ('Scaler', 'ScalerImpl'),
177    ('fruit::Annotated<Annotation1, Scaler>', 'fruit::Annotated<Annotation2, ScalerImpl>'),
178])
179@pytest.mark.skipif(
180    re.search('Clang', CXX_COMPILER_NAME) is None,
181    reason = 'This is Clang-only because GCC >=4.9 refuses to even mention the type C() when C is an abstract class, '
182             'while Clang allows to mention the type (but of course there can be no functions with this type)')
183def test_error_abstract_class_clang(ScalerAnnot, ScalerImplAnnot):
184    source = '''
185        struct Scaler {
186          virtual double scale(double x) = 0;
187        };
188
189        struct ScalerImpl : public Scaler {
190          INJECT(ScalerImpl()) = default;
191
192          // Note: here we "forgot" to implement scale() (on purpose, for this test) so ScalerImpl is an abstract class.
193        };
194
195        fruit::Component<> getComponent() {
196          return fruit::createComponent()
197            .addMultibinding<ScalerAnnot, ScalerImplAnnot>();
198        }
199        '''
200    expect_compile_error(
201        'CannotConstructAbstractClassError<ScalerImpl>',
202        'The specified class can.t be constructed because it.s an abstract class.',
203        COMMON_DEFINITIONS,
204        source,
205        locals())
206
207if __name__== '__main__':
208    main(__file__)
209