xref: /aosp_15_r20/external/google-fruit/tests/test_register_instance.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    '''
24
25def escape_regex(regex):
26    # We un-escape the space because we strip the spaces in fruit_test_common, and this would otherwise leave a
27    # stray backslash.
28    return re.escape(regex).replace('\\ ', ' ')
29
30class TestRegisterInstance(parameterized.TestCase):
31    def test_bind_instance_success(self):
32        source = '''
33            struct X {
34              int n;
35
36              X(int n)
37                : n(n) {
38              }
39            };
40
41            fruit::Component<X> getComponent(X* x) {
42              return fruit::createComponent()
43                .bindInstance(*x);
44            }
45
46            int main() {
47              X x(34);
48              fruit::Injector<X> injector(getComponent, &x);
49              X& x1 = injector.get<X&>();
50              Assert(&x == &x1);
51            }
52            '''
53        expect_success(COMMON_DEFINITIONS, source)
54
55    def test_bind_instance_annotated_success(self):
56        source = '''
57            struct X {
58              int n;
59
60              X(int n)
61                : n(n) {
62              }
63            };
64
65            fruit::Component<fruit::Annotated<Annotation1, X>> getComponent(X* x) {
66              return fruit::createComponent()
67                .bindInstance<fruit::Annotated<Annotation1, X>>(*x);
68            }
69
70            int main() {
71              X x(34);
72              fruit::Injector<fruit::Annotated<Annotation1, X>> injector(getComponent, &x);
73              X& x1 = injector.get<fruit::Annotated<Annotation1, X&>>();
74              Assert(&x == &x1);
75            }
76            '''
77        expect_success(COMMON_DEFINITIONS, source)
78
79    def test_bind_const_instance_success(self):
80        source = '''
81            struct X {
82              int n;
83
84              X(int n)
85                : n(n) {
86              }
87            };
88
89            fruit::Component<const X> getComponent(const X* x) {
90              return fruit::createComponent()
91                .bindInstance(*x);
92            }
93
94            const X x(34);
95
96            int main() {
97              fruit::Injector<const X> injector(getComponent, &x);
98              const X& x1 = injector.get<const X&>();
99              Assert(&x == &x1);
100            }
101            '''
102        expect_success(COMMON_DEFINITIONS, source)
103
104    def test_bind_const_instance_annotated_success(self):
105        source = '''
106            struct X {
107              int n;
108
109              X(int n)
110                : n(n) {
111              }
112            };
113
114            fruit::Component<fruit::Annotated<Annotation1, const X>> getComponent(const X* x) {
115              return fruit::createComponent()
116                .bindInstance<fruit::Annotated<Annotation1, X>>(*x);
117            }
118
119            const X x(34);
120
121            int main() {
122              fruit::Injector<fruit::Annotated<Annotation1, const X>> injector(getComponent, &x);
123              const X& x1 = injector.get<fruit::Annotated<Annotation1, const X&>>();
124              Assert(&x == &x1);
125            }
126            '''
127        expect_success(COMMON_DEFINITIONS, source)
128
129    @parameterized.parameters([
130        ('X', 'X', 'X*', 'X*'),
131        ('X', 'const X', 'const X*', 'const X*'),
132        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'X*', 'fruit::Annotated<Annotation1, X*>'),
133        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'const X*', 'fruit::Annotated<Annotation1, const X*>'),
134    ])
135    def test_bind_instance_two_explicit_type_arguments_success(self, XAnnot, MaybeConstXAnnot, XPtr, XPtrAnnot):
136        source = '''
137            struct X {
138              int n;
139
140              X(int n)
141                : n(n) {
142              }
143            };
144
145            fruit::Component<MaybeConstXAnnot> getComponent(XPtr x) {
146              return fruit::createComponent()
147                .bindInstance<XAnnot, X>(*x);
148            }
149
150            int main() {
151              X x(34);
152              fruit::Injector<MaybeConstXAnnot> injector(getComponent, &x);
153              XPtr x1 = injector.get<XPtrAnnot>();
154              Assert(&x == x1);
155            }
156            '''
157        expect_success(COMMON_DEFINITIONS, source, locals())
158
159    @parameterized.parameters([
160        'X',
161        'fruit::Annotated<Annotation1, X>',
162    ])
163    def test_bind_instance_abstract_class_ok(self, XAnnot):
164        source = '''
165            struct X {
166              virtual void foo() = 0;
167            };
168
169            fruit::Component<> getComponentForInstanceHelper(X* x) {
170              return fruit::createComponent()
171                .bindInstance<XAnnot, X>(*x);
172            }
173
174            fruit::Component<XAnnot> getComponentForInstance(X* x) {
175              return fruit::createComponent()
176                .install(getComponentForInstanceHelper, x)
177                .bindInstance<XAnnot, X>(*x);
178            }
179            '''
180        expect_success(
181            COMMON_DEFINITIONS,
182            source,
183            locals())
184
185    @parameterized.parameters([
186        ('int', 'int*'),
187        ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
188    ])
189    def test_bind_instance_multiple_equivalent_bindings_success(self, intAnnot, intPtrAnnot):
190        source = '''
191            fruit::Component<> getComponentForInstanceHelper(int* n) {
192              return fruit::createComponent()
193                .bindInstance<intAnnot, int>(*n);
194            }
195
196            fruit::Component<intAnnot> getComponentForInstance(int* n) {
197              return fruit::createComponent()
198                .install(getComponentForInstanceHelper, n)
199                .bindInstance<intAnnot, int>(*n);
200            }
201
202            int main() {
203              int n = 5;
204              fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
205              if (injector.get<intPtrAnnot>() != &n)
206                abort();
207            }
208            '''
209        expect_success(
210            COMMON_DEFINITIONS,
211            source,
212            locals())
213
214    @parameterized.parameters([
215        ('int', 'int*'),
216        ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
217    ])
218    def test_bind_instance_multiple_equivalent_bindings_different_constness_success(self, intAnnot, intPtrAnnot):
219        source = '''
220            fruit::Component<> getComponentForInstanceHelper(const int* n) {
221              return fruit::createComponent()
222                .bindInstance<intAnnot, int>(*n);
223            }
224
225            fruit::Component<intAnnot> getComponentForInstance(int* n) {
226              return fruit::createComponent()
227                .install(getComponentForInstanceHelper, n)
228                .bindInstance<intAnnot, int>(*n);
229            }
230
231            int main() {
232              int n = 5;
233              fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
234              if (injector.get<intPtrAnnot>() != &n)
235                abort();
236            }
237            '''
238        expect_success(
239            COMMON_DEFINITIONS,
240            source,
241            locals())
242
243    @parameterized.parameters([
244        ('int', 'int*'),
245        ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'),
246    ])
247    def test_bind_instance_multiple_equivalent_bindings_different_constness_other_order_success(self, intAnnot, intPtrAnnot):
248        source = '''
249            fruit::Component<> getComponentForInstanceHelper(const int* n) {
250              return fruit::createComponent()
251                .bindInstance<intAnnot, int>(*n);
252            }
253
254            fruit::Component<intAnnot> getComponentForInstance(int* n) {
255              return fruit::createComponent()
256                .bindInstance<intAnnot, int>(*n)
257                .install(getComponentForInstanceHelper, n);
258            }
259
260            int main() {
261              int n = 5;
262              fruit::Injector<intAnnot> injector(getComponentForInstance, &n);
263              if (injector.get<intPtrAnnot>() != &n)
264                abort();
265            }
266            '''
267        expect_success(
268            COMMON_DEFINITIONS,
269            source,
270            locals())
271
272    @parameterized.parameters([
273        'X*',
274        'const X*',
275        'std::shared_ptr<X>',
276    ])
277    def test_bind_instance_non_normalized_type_error(self, XVariant):
278        if XVariant.endswith('&'):
279            XVariantRegexp = escape_regex(XVariant[:-1])
280        else:
281            XVariantRegexp = escape_regex(XVariant)
282        source = '''
283            struct X {};
284
285            fruit::Component<> getComponent(XVariant x) {
286              return fruit::createComponent()
287                .bindInstance(x);
288            }
289            '''
290        expect_compile_error(
291            'NonClassTypeError<XVariantRegexp,X>',
292            'A non-class type T was specified. Use C instead.',
293            COMMON_DEFINITIONS,
294            source,
295            locals())
296
297    @parameterized.parameters([
298        ('const X', r'const X'),
299        ('X*', r'X\*'),
300        ('const X*', r'const X\*'),
301        ('X&', r'X&'),
302        ('const X&', r'const X&'),
303        ('std::shared_ptr<X>', r'std::shared_ptr<X>'),
304    ])
305    def test_bind_instance_non_normalized_type_error_with_annotation(self, XVariant, XVariantRegexp):
306        source = '''
307            struct X {};
308
309            fruit::Component<> getComponent(XVariant x) {
310              return fruit::createComponent()
311                .bindInstance<fruit::Annotated<Annotation1, XVariant>>(x);
312            }
313            '''
314        expect_compile_error(
315            'NonClassTypeError<XVariantRegexp,X>',
316            'A non-class type T was specified. Use C instead.',
317            COMMON_DEFINITIONS,
318            source,
319            locals())
320
321    @parameterized.parameters([
322        ('const X', 'const X'),
323        ('X*', 'X*'),
324        ('const X*', 'const X*'),
325        ('X&', 'X&'),
326        ('const X&', 'const X&'),
327        ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
328
329        ('fruit::Annotated<Annotation1, const X>', 'const X'),
330        ('fruit::Annotated<Annotation1, X*>', 'X*'),
331        ('fruit::Annotated<Annotation1, const X*>', 'const X*'),
332        ('fruit::Annotated<Annotation1, X&>', 'X&'),
333        ('fruit::Annotated<Annotation1, const X&>', 'const X&'),
334        ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'),
335
336        ('fruit::Annotated<Annotation1, X>', 'const X'),
337        ('fruit::Annotated<Annotation1, X>', 'X*'),
338        ('fruit::Annotated<Annotation1, X>', 'const X*'),
339        ('fruit::Annotated<Annotation1, X>', 'X&'),
340        ('fruit::Annotated<Annotation1, X>', 'const X&'),
341        ('fruit::Annotated<Annotation1, X>', 'std::shared_ptr<X>'),
342    ])
343    def test_bind_instance_non_normalized_type_error_two_explicit_type_arguments(self, XAnnotVariant, XVariant):
344        XVariantRegexp = escape_regex(XVariant)
345        source = '''
346            struct X {};
347
348            fruit::Component<> getComponent(XVariant x) {
349              return fruit::createComponent()
350                .bindInstance<XAnnotVariant, XVariant>(x);
351            }
352            '''
353        expect_compile_error(
354            'NonClassTypeError<XVariantRegexp,X>',
355            'A non-class type T was specified. Use C instead.',
356            COMMON_DEFINITIONS,
357            source,
358            locals())
359
360    @parameterized.parameters([
361        ('X*', r'X\*'),
362        ('const X*', r'const X\*'),
363        ('std::shared_ptr<X>', r'std::shared_ptr<X>'),
364    ])
365    def test_register_instance_error_must_be_reference(self, XVariant, XVariantRegex):
366        source = '''
367            struct X {};
368
369            fruit::Component<> getComponentForInstance(XVariant x) {
370              return fruit::createComponent()
371                  .bindInstance(x);
372            }
373            '''
374        expect_compile_error(
375            'NonClassTypeError<XVariantRegex,X>',
376            'A non-class type T was specified. Use C instead.',
377            COMMON_DEFINITIONS,
378            source,
379            locals())
380
381    @parameterized.parameters([
382        ('X*', r'X\*'),
383        ('const X*', r'const X\*'),
384        ('std::shared_ptr<X>', r'std::shared_ptr<X>'),
385    ])
386    def test_register_instance_error_must_be_reference_with_annotation(self, XVariant, XVariantRegex):
387        source = '''
388            struct X {};
389
390            fruit::Component<> getComponentForInstance(XVariant x) {
391              return fruit::createComponent()
392                  .bindInstance<fruit::Annotated<Annotation1, X>>(x);
393            }
394            '''
395        expect_compile_error(
396            'NonClassTypeError<XVariantRegex,X>',
397            'A non-class type T was specified. Use C instead.',
398            COMMON_DEFINITIONS,
399            source,
400            locals())
401
402    @parameterized.parameters([
403        'X',
404        'fruit::Annotated<Annotation1, X>',
405    ])
406    def test_bind_instance_mismatched_type_arguments(self, XAnnot):
407        source = '''
408            struct X {};
409
410            fruit::Component<> getComponent(int* n) {
411              return fruit::createComponent()
412                .bindInstance<XAnnot, int>(*n);
413            }
414            '''
415        expect_compile_error(
416            'TypeMismatchInBindInstanceError<X,int>',
417            'A type parameter was specified in bindInstance.. but it doesn.t match the value type',
418            COMMON_DEFINITIONS,
419            source,
420            locals())
421
422    @parameterized.parameters([
423        ('Base', 'Base*'),
424        ('fruit::Annotated<Annotation1, Base>', 'fruit::Annotated<Annotation1, Base*>'),
425    ])
426    def test_bind_instance_to_subclass(self, BaseAnnot, BasePtrAnnot):
427        source = '''
428            struct Base {
429              virtual void f() = 0;
430              virtual ~Base() {
431              }
432            };
433
434            struct Derived : public Base {
435              void f() override {
436              }
437            };
438
439            fruit::Component<BaseAnnot> getComponent(Derived* derived) {
440              return fruit::createComponent()
441                .bindInstance<BaseAnnot, Base>(*derived);
442            }
443
444            int main() {
445              Derived derived;
446              fruit::Injector<BaseAnnot> injector(getComponent, &derived);
447              Base* base = injector.get<BasePtrAnnot>();
448              base->f();
449            }
450            '''
451        expect_success(COMMON_DEFINITIONS, source, locals())
452
453    @parameterized.parameters([
454        ('X**', r'X\*\*'),
455        ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
456        ('X*&', r'X\*&'),
457        ('fruit::Annotated<Annotation1, X**>', r'X\*\*'),
458    ])
459    def test_bind_instance_type_not_normalized(self, XVariant, XVariantRegex):
460        source = '''
461            struct X {};
462
463            using XVariantT = XVariant;
464            fruit::Component<> getComponent(XVariantT x) {
465              return fruit::createComponent()
466                .bindInstance<XVariant, XVariant>(x);
467            }
468            '''
469        expect_compile_error(
470            'NonClassTypeError<XVariantRegex,X>',
471            'A non-class type T was specified.',
472            COMMON_DEFINITIONS,
473            source,
474            locals())
475
476    @parameterized.parameters([
477        ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
478    ])
479    def test_bind_instance_type_not_injectable_error(self, XVariant, XVariantRegex):
480        source = '''
481            struct X {};
482
483            using XVariantT = XVariant;
484            fruit::Component<> getComponent(XVariantT x) {
485              return fruit::createComponent()
486                .bindInstance<XVariant, XVariant>(x);
487            }
488            '''
489        expect_compile_error(
490            'NonInjectableTypeError<XVariantRegex>',
491            'The type T is not injectable.',
492            COMMON_DEFINITIONS,
493            source,
494            locals())
495
496if __name__ == '__main__':
497    absltest.main()
498