• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
16from absl.testing import parameterized
17from fruit_test_common import *
18
19COMMON_DEFINITIONS = '''
20    #include "test_common.h"
21
22    struct X;
23
24    struct Annotation1 {};
25    using XAnnot1 = fruit::Annotated<Annotation1, X>;
26
27    struct Annotation2 {};
28    using XAnnot2 = fruit::Annotated<Annotation2, X>;
29    '''
30
31class TestInjector(parameterized.TestCase):
32    def test_empty_injector(self):
33        source = '''
34            fruit::Component<> getComponent() {
35              return fruit::createComponent();
36            }
37
38            int main() {
39              fruit::Injector<> injector(getComponent);
40            }
41            '''
42        expect_success(
43            COMMON_DEFINITIONS,
44            source)
45
46    @parameterized.parameters([
47        'X',
48        'fruit::Annotated<Annotation1, X>',
49    ])
50    def test_error_component_with_requirements(self, XAnnot):
51        source = '''
52            struct X {};
53
54            fruit::Component<fruit::Required<XAnnot>> getComponent();
55
56            void f(fruit::NormalizedComponent<XAnnot> normalizedComponent) {
57              fruit::Injector<XAnnot> injector(normalizedComponent, getComponent);
58            }
59            '''
60        expect_compile_error(
61            'ComponentWithRequirementsInInjectorError<XAnnot>',
62            'When using the two-argument constructor of Injector, the component used as second parameter must not have requirements',
63            COMMON_DEFINITIONS,
64            source,
65            locals())
66
67    @parameterized.parameters([
68        'X',
69        'fruit::Annotated<Annotation1, X>',
70    ])
71    def test_error_declared_types_not_provided(self, XAnnot):
72        source = '''
73            struct X {
74              using Inject = X();
75            };
76
77            fruit::Component<> getEmptyComponent() {
78              return fruit::createComponent();
79            }
80
81            int main() {
82              fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
83              fruit::Injector<XAnnot> injector(normalizedComponent, getEmptyComponent);
84            }
85            '''
86        expect_compile_error(
87            'TypesInInjectorNotProvidedError<XAnnot>',
88            'The types in TypesNotProvided are declared as provided by the injector, but none of the two components passed to the Injector constructor provides them.',
89            COMMON_DEFINITIONS,
90            source,
91            locals())
92
93    @parameterized.parameters([
94        ('X', 'const X'),
95        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
96    ])
97    def test_error_declared_nonconst_types_provided_as_const(self, XAnnot, ConstXAnnot):
98        source = '''
99            struct X {
100              using Inject = X();
101            };
102
103            fruit::Component<ConstXAnnot> getComponent();
104
105            int main() {
106              fruit::Injector<XAnnot> injector(getComponent);
107            }
108            '''
109        expect_generic_compile_error(
110            r'no matching constructor for initialization of .fruit::Injector<XAnnot>.'
111            r'|no matching function for call to .fruit::Injector<XAnnot>::Injector\(fruit::Component<ConstXAnnot> \(&\)\(\)\).'
112            # MSVC
113            r'|.fruit::Injector<XAnnot>::Injector.: none of the 2 overloads could convert all the argument types',
114            COMMON_DEFINITIONS,
115            source,
116            locals())
117
118    @parameterized.parameters([
119        ('X', 'const X'),
120        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
121    ])
122    def test_error_declared_nonconst_types_provided_as_const_with_normalized_component(self, XAnnot, ConstXAnnot):
123        source = '''
124            struct X {};
125
126            fruit::Component<> getEmptyComponent();
127
128            void f(fruit::NormalizedComponent<ConstXAnnot> normalizedComponent) {
129              fruit::Injector<XAnnot> injector(normalizedComponent, getEmptyComponent);
130            }
131            '''
132        expect_compile_error(
133            'TypesInInjectorProvidedAsConstOnlyError<XAnnot>',
134            'The types in TypesProvidedAsConstOnly are declared as non-const provided types by the injector',
135            COMMON_DEFINITIONS,
136            source,
137            locals())
138
139    @parameterized.parameters([
140        ('X', 'Y'),
141        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
142    ])
143    def test_injector_get_error_type_not_provided(self, XAnnot, YAnnot):
144        source = '''
145            struct X {
146              using Inject = X();
147            };
148
149            struct Y {};
150
151            fruit::Component<XAnnot> getComponent() {
152              return fruit::createComponent();
153            }
154
155            int main() {
156              fruit::Injector<XAnnot> injector(getComponent);
157              injector.get<YAnnot>();
158            }
159            '''
160        expect_compile_error(
161            'TypeNotProvidedError<YAnnot>',
162            'Trying to get an instance of T, but it is not provided by this Provider/Injector.',
163            COMMON_DEFINITIONS,
164            source,
165            locals())
166
167    @parameterized.parameters([
168        ('const X', 'X&', r'X&'),
169        ('const X', 'X*', r'X\*'),
170        ('const X', 'std::shared_ptr<X>', r'std::shared_ptr<X>'),
171        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X&>', r'fruit::Annotated<Annotation1, X&>'),
172        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X*>', r'fruit::Annotated<Annotation1, X\*>'),
173        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>', r'fruit::Annotated<Annotation1, std::shared_ptr<X>>'),
174    ])
175    def test_injector_const_provided_type_does_not_allow_injecting_nonconst_variants(self, ConstXAnnot, XInjectorGetParam, XInjectorGetParamRegex):
176        source = '''
177            void f(fruit::Injector<ConstXAnnot> injector) {
178              injector.get<XInjectorGetParam>();
179            }
180            '''
181        expect_compile_error(
182            'TypeProvidedAsConstOnlyError<XInjectorGetParamRegex>',
183            'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector',
184            COMMON_DEFINITIONS,
185            source,
186            locals())
187
188    @parameterized.parameters([
189        ('X', 'X'),
190        ('X', 'const X&'),
191        ('X', 'const X*'),
192        ('X', 'X&'),
193        ('X', 'X*'),
194        ('X', 'std::shared_ptr<X>'),
195        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
196        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>'),
197        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X*>'),
198        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>'),
199        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'),
200        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>'),
201    ])
202    def test_injector_get_ok(self, XBindingInInjector, XInjectorGetParam):
203        source = '''
204            struct X {
205              using Inject = X();
206            };
207
208            fruit::Component<XBindingInInjector> getComponent() {
209              return fruit::createComponent();
210            }
211
212            int main() {
213              fruit::Injector<XBindingInInjector> injector(getComponent);
214
215              auto x = injector.get<XInjectorGetParam>();
216              (void)x;
217            }
218            '''
219        expect_success(
220            COMMON_DEFINITIONS,
221            source,
222            locals())
223
224    @parameterized.parameters([
225        ('const X', 'X'),
226        ('const X', 'const X&'),
227        ('const X', 'const X*'),
228        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
229        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>'),
230        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X*>'),
231    ])
232    def test_injector_get_const_binding_ok(self, XBindingInInjector, XInjectorGetParam):
233        XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '')
234        source = '''
235            struct X {};
236
237            const X x{};
238
239            fruit::Component<XBindingInInjector> getComponent() {
240              return fruit::createComponent()
241                  .bindInstance<XBindingInInjectorWithoutConst, X>(x);
242            }
243
244            int main() {
245              fruit::Injector<XBindingInInjector> injector(getComponent);
246
247              auto x = injector.get<XInjectorGetParam>();
248              (void)x;
249            }
250            '''
251        expect_success(
252            COMMON_DEFINITIONS,
253            source,
254            locals())
255
256    @parameterized.parameters([
257        ('X**', r'X\*\*'),
258        ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
259        ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'),
260        ('X* const', r'X\* const'),
261        ('const X* const', r'const X\* const'),
262        ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
263        ('X*&', r'X\*&'),
264        ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
265        ('void', r'void'),
266        ('fruit::Annotated<Annotation1, X**>', r'X\*\*'),
267    ])
268    def test_injector_get_error_type_not_injectable(self, XVariant, XVariantRegex):
269        source = '''
270            struct X {};
271
272            void f(fruit::Injector<X> injector) {
273              injector.get<XVariant>();
274            }
275            '''
276        expect_compile_error(
277            'NonInjectableTypeError<XVariantRegex>',
278            'The type T is not injectable.',
279            COMMON_DEFINITIONS,
280            source,
281            locals())
282
283    @parameterized.parameters([
284        ('X[]', r'X\[\]'),
285    ])
286    def test_injector_get_error_array_type(self, XVariant, XVariantRegex):
287        source = '''
288            struct X {};
289
290            void f(fruit::Injector<X> injector) {
291              injector.get<XVariant>();
292            }
293            '''
294        expect_generic_compile_error(
295            'function cannot return array type'
296            '|function returning an array'
297            # MSVC
298            '|.fruit::Injector<X>::get.: no matching overloaded function found',
299            COMMON_DEFINITIONS,
300            source,
301            locals())
302
303if __name__ == '__main__':
304    absltest.main()
305