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