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
16import pytest
17
18from fruit_test_common import *
19
20COMMON_DEFINITIONS = '''
21    #include "test_common.h"
22
23    struct Annotation1 {};
24
25    template <typename T>
26    using WithNoAnnot = T;
27
28    template <typename T>
29    using WithAnnot1 = fruit::Annotated<Annotation1, T>;
30    '''
31
32@pytest.mark.parametrize('WithAnnot', [
33    'WithNoAnnot',
34    'WithAnnot1',
35])
36@pytest.mark.parametrize('ConstructX,XPtr', [
37   ('X()', 'X'),
38   ('new X()', 'X*'),
39])
40def test_register_provider_success(WithAnnot,ConstructX, XPtr):
41    source = '''
42        struct X : public ConstructionTracker<X> {
43          int value = 5;
44        };
45
46        fruit::Component<WithAnnot<X>> getComponent() {
47          return fruit::createComponent()
48            .registerProvider<WithAnnot<XPtr>()>([](){return ConstructX;});
49        }
50
51        int main() {
52          fruit::Injector<WithAnnot<X>> injector(getComponent);
53
54          Assert((injector.get<WithAnnot<X                 >>(). value == 5));
55          Assert((injector.get<WithAnnot<X*                >>()->value == 5));
56          Assert((injector.get<WithAnnot<X&                >>(). value == 5));
57          Assert((injector.get<WithAnnot<const X           >>(). value == 5));
58          Assert((injector.get<WithAnnot<const X*          >>()->value == 5));
59          Assert((injector.get<WithAnnot<const X&          >>(). value == 5));
60          Assert((injector.get<WithAnnot<std::shared_ptr<X>>>()->value == 5));
61
62          Assert(X::num_objects_constructed == 1);
63        }
64        '''
65    expect_success(
66        COMMON_DEFINITIONS,
67        source,
68        locals())
69
70@pytest.mark.parametrize('WithAnnot', [
71    'WithNoAnnot',
72    'WithAnnot1',
73])
74def test_register_provider_abstract_class_ok(WithAnnot):
75    source = '''
76        struct I {
77          virtual int foo() = 0;
78          virtual ~I() = default;
79        };
80
81        struct X : public I {
82          int foo() override {
83            return 5;
84          }
85        };
86
87        fruit::Component<WithAnnot<I>> getComponent() {
88          return fruit::createComponent()
89            .registerProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
90        }
91
92        int main() {
93          fruit::Injector<WithAnnot<I>> injector(getComponent);
94
95          Assert(injector.get<WithAnnot<I*>>()->foo() == 5);
96        }
97        '''
98    expect_success(
99        COMMON_DEFINITIONS,
100        source,
101        locals())
102
103@pytest.mark.parametrize('WithAnnot', [
104    'WithNoAnnot',
105    'WithAnnot1',
106])
107def test_register_provider_abstract_class_with_no_virtual_destructor_error(WithAnnot):
108    source = '''
109        struct I {
110          virtual int foo() = 0;
111        };
112
113        struct X : public I {
114          int foo() override {
115            return 5;
116          }
117        };
118
119        fruit::Component<WithAnnot<I>> getComponent() {
120          return fruit::createComponent()
121            .registerProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());});
122        }
123        '''
124    expect_compile_error(
125        'ProviderReturningPointerToAbstractClassWithNoVirtualDestructorError<I>',
126        'registerProvider\(\) was called with a lambda that returns a pointer to T, but T is an abstract class',
127        COMMON_DEFINITIONS,
128        source,
129        locals())
130
131@pytest.mark.parametrize('ConstructX', [
132    'X()',
133    'new X()',
134])
135def test_register_provider_not_copyable_success(ConstructX):
136    source = '''
137        struct X {
138          X() = default;
139          X(X&&) = default;
140          X(const X&) = delete;
141        };
142
143        fruit::Component<X> getComponent() {
144          return fruit::createComponent()
145            .registerProvider([](){return ConstructX;});
146        }
147
148        int main() {
149          fruit::Injector<X> injector(getComponent);
150          injector.get<X*>();
151        }
152        '''
153    expect_success(
154        COMMON_DEFINITIONS,
155        source,
156        locals())
157
158def test_register_provider_not_movable_returning_pointer_success():
159    source = '''
160        struct X {
161          X() = default;
162          X(X&&) = delete;
163          X(const X&) = delete;
164        };
165
166        fruit::Component<X> getComponent() {
167          return fruit::createComponent()
168            .registerProvider([](){return new X();});
169        }
170
171        int main() {
172          fruit::Injector<X> injector(getComponent);
173          injector.get<X*>();
174        }
175        '''
176    expect_success(
177        COMMON_DEFINITIONS,
178        source)
179
180@pytest.mark.parametrize('XAnnot', [
181    'X',
182    'fruit::Annotated<Annotation1, X>',
183])
184def test_register_provider_error_not_function(XAnnot):
185    source = '''
186        struct X {
187          X(int) {}
188        };
189
190        fruit::Component<XAnnot> getComponent() {
191          int n = 3;
192          return fruit::createComponent()
193            .registerProvider<XAnnot()>([=]{return X(n);});
194        }
195        '''
196    expect_compile_error(
197        'FunctorUsedAsProviderError<.*>',
198        'A stateful lambda or a non-lambda functor was used as provider',
199        COMMON_DEFINITIONS,
200        source,
201        locals())
202
203@pytest.mark.parametrize('intAnnot', [
204    'int',
205    'fruit::Annotated<Annotation1, int>',
206])
207def test_register_provider_error_malformed_signature(intAnnot):
208    source = '''
209        fruit::Component<intAnnot> getComponent() {
210          return fruit::createComponent()
211            .registerProvider<intAnnot>([](){return 42;});
212        }
213        '''
214    expect_compile_error(
215        'NotASignatureError<intAnnot>',
216        'CandidateSignature was specified as parameter, but it.s not a signature. Signatures are of the form',
217        COMMON_DEFINITIONS,
218        source,
219        locals())
220
221@pytest.mark.parametrize('XAnnot,XPtrAnnot,XAnnotRegex', [
222    ('X', 'X*', '(struct )?X'),
223    ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
224])
225def test_register_provider_error_returned_nullptr(XAnnot, XPtrAnnot, XAnnotRegex):
226    source = '''
227        struct X {};
228
229        fruit::Component<XAnnot> getComponent() {
230          return fruit::createComponent()
231              .registerProvider<XPtrAnnot()>([](){return (X*)nullptr;});
232        }
233
234        int main() {
235          fruit::Injector<XAnnot> injector(getComponent);
236          injector.get<XAnnot>();
237        }
238        '''
239    expect_runtime_error(
240        'Fatal injection error: attempting to get an instance for the type XAnnotRegex but the provider returned nullptr',
241        COMMON_DEFINITIONS,
242        source,
243        locals())
244
245@pytest.mark.parametrize('ConstructX,XPtr', [
246    ('X()', 'X'),
247    ('new X()', 'X*'),
248])
249@pytest.mark.parametrize('WithAnnot', [
250    'WithNoAnnot',
251    'WithAnnot1',
252])
253@pytest.mark.parametrize('YVariant', [
254    'Y',
255    'const Y',
256    'Y*',
257    'const Y*',
258    'Y&',
259    'const Y&',
260    'std::shared_ptr<Y>',
261    'fruit::Provider<Y>',
262    'fruit::Provider<const Y>',
263])
264def test_register_provider_with_param_success(ConstructX, XPtr, WithAnnot, YVariant):
265    source = '''
266        struct Y {};
267        struct X {};
268
269        fruit::Component<WithAnnot<Y>> getYComponent() {
270          return fruit::createComponent()
271            .registerConstructor<WithAnnot<Y>()>();
272        }
273
274        fruit::Component<X> getComponent() {
275          return fruit::createComponent()
276            .install(getYComponent)
277            .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
278        }
279
280        int main() {
281          fruit::Injector<X> injector(getComponent);
282          injector.get<X>();
283        }
284        '''
285    expect_success(
286        COMMON_DEFINITIONS,
287        source,
288        locals())
289
290@pytest.mark.parametrize('ConstructX,XPtr', [
291    ('X()', 'X'),
292    ('new X()', 'X*'),
293])
294@pytest.mark.parametrize('WithAnnot', [
295    'WithNoAnnot',
296    'WithAnnot1',
297])
298@pytest.mark.parametrize('YVariant', [
299    'Y',
300    'const Y',
301    'const Y*',
302    'const Y&',
303    'fruit::Provider<const Y>',
304])
305def test_register_provider_with_param_const_binding_success(ConstructX, XPtr, WithAnnot, YVariant):
306    source = '''
307        struct Y {};
308        struct X {};
309
310        const Y y{};
311
312        fruit::Component<WithAnnot<const Y>> getYComponent() {
313          return fruit::createComponent()
314            .bindInstance<WithAnnot<Y>, Y>(y);
315        }
316
317        fruit::Component<X> getComponent() {
318          return fruit::createComponent()
319            .install(getYComponent)
320            .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
321        }
322
323        int main() {
324          fruit::Injector<X> injector(getComponent);
325          injector.get<X>();
326        }
327        '''
328    expect_success(
329        COMMON_DEFINITIONS,
330        source,
331        locals())
332
333@pytest.mark.parametrize('ConstructX,XPtr', [
334    ('X()', 'X'),
335    ('new X()', 'X*'),
336])
337@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
338    ('WithNoAnnot', 'Y'),
339    ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
340])
341@pytest.mark.parametrize('YVariant', [
342    'Y*',
343    'Y&',
344    'std::shared_ptr<Y>',
345    'fruit::Provider<Y>',
346])
347def test_register_provider_with_param_error_nonconst_param_required(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
348    source = '''
349        struct Y {};
350        struct X {};
351
352        fruit::Component<WithAnnot<const Y>> getYComponent();
353
354        fruit::Component<> getComponent() {
355          return fruit::createComponent()
356            .install(getYComponent)
357            .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; });
358        }
359        '''
360    expect_compile_error(
361        'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
362        'The type T was provided as constant, however one of the constructors/providers/factories in this component',
363        COMMON_DEFINITIONS,
364        source,
365        locals())
366
367@pytest.mark.parametrize('ConstructX,XPtr', [
368    ('X()', 'X'),
369    ('new X()', 'X*'),
370])
371@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [
372    ('WithNoAnnot', 'Y'),
373    ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'),
374])
375@pytest.mark.parametrize('YVariant', [
376    'Y*',
377    'Y&',
378    'std::shared_ptr<Y>',
379    'fruit::Provider<Y>',
380])
381def test_register_provider_with_param_error_nonconst_param_required_install_after(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant):
382    source = '''
383        struct Y {};
384        struct X {};
385
386        fruit::Component<WithAnnot<const Y>> getYComponent();
387
388        fruit::Component<> getComponent() {
389          return fruit::createComponent()
390            .registerProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; })
391            .install(getYComponent);
392        }
393        '''
394    expect_compile_error(
395        'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
396        'The type T was provided as constant, however one of the constructors/providers/factories in this component',
397        COMMON_DEFINITIONS,
398        source,
399        locals())
400
401def test_register_provider_requiring_nonconst_then_requiring_const_ok():
402    source = '''
403        struct X {};
404        struct Y {};
405        struct Z {};
406
407        fruit::Component<Y, Z> getRootComponent() {
408          return fruit::createComponent()
409            .registerProvider([](X&) { return Y();})
410            .registerProvider([](const X&) { return Z();})
411            .registerConstructor<X()>();
412        }
413
414        int main() {
415          fruit::Injector<Y, Z> injector(getRootComponent);
416          injector.get<Y>();
417          injector.get<Z>();
418        }
419        '''
420    expect_success(
421        COMMON_DEFINITIONS,
422        source,
423        locals())
424
425def test_register_provider_requiring_nonconst_then_requiring_const_declaring_const_requirement_error():
426    source = '''
427        struct X {};
428        struct Y {};
429        struct Z {};
430
431        fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
432          return fruit::createComponent()
433            .registerProvider([](X&) { return Y();})
434            .registerProvider([](const X&) { return Z();});
435        }
436        '''
437    expect_compile_error(
438        'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
439        'The type T was declared as a const Required type in the returned Component, however',
440        COMMON_DEFINITIONS,
441        source,
442        locals())
443
444def test_register_provider_requiring_const_then_requiring_nonconst_ok():
445    source = '''
446        struct X {};
447        struct Y {};
448        struct Z {};
449
450        fruit::Component<Y, Z> getRootComponent() {
451          return fruit::createComponent()
452            .registerProvider([](const X&) { return Y();})
453            .registerProvider([](X&) { return Z();})
454            .registerConstructor<X()>();
455        }
456
457        int main() {
458          fruit::Injector<Y, Z> injector(getRootComponent);
459          injector.get<Y>();
460          injector.get<Z>();
461        }
462        '''
463    expect_success(
464        COMMON_DEFINITIONS,
465        source,
466        locals())
467
468def test_register_provider_requiring_const_then_requiring_nonconst_declaring_const_requirement_error():
469    source = '''
470        struct X {};
471        struct Y {};
472        struct Z {};
473
474        fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
475          return fruit::createComponent()
476            .registerProvider([](const X&) { return Y();})
477            .registerProvider([](X&) { return Z();});
478        }
479        '''
480    expect_compile_error(
481        'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
482        'The type T was declared as a const Required type in the returned Component, however',
483        COMMON_DEFINITIONS,
484        source,
485        locals())
486
487@pytest.mark.parametrize('ConstructX,XPtr', [
488    ('X()', 'X'),
489    ('new X()', 'X*'),
490])
491@pytest.mark.parametrize('YVariant,YVariantRegex', [
492    ('Y**', r'Y\*\*'),
493    ('std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
494    ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
495    ('Y*&', r'Y\*&'),
496    ('Y(*)()', r'Y(\((__cdecl)?\*\))?\((void)?\)'),
497])
498def test_register_provider_with_param_error_type_not_injectable(ConstructX, XPtr, YVariant, YVariantRegex):
499    source = '''
500        struct Y {};
501        struct X {};
502
503        fruit::Component<> getComponent() {
504          return fruit::createComponent()
505            .registerProvider<XPtr(YVariant)>([](YVariant){ return ConstructX; });
506        }
507        '''
508    expect_compile_error(
509        'NonInjectableTypeError<YVariantRegex>',
510        'The type T is not injectable.',
511        COMMON_DEFINITIONS,
512        source,
513        locals())
514
515
516if __name__== '__main__':
517    main(__file__)
518