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