xref: /aosp_15_r20/external/google-fruit/tests/test_binding_clash.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 X;
23    struct Y;
24    struct Z;
25
26    struct Annotation1 {};
27    using XAnnot1 = fruit::Annotated<Annotation1, X>;
28    using YAnnot1 = fruit::Annotated<Annotation1, Y>;
29    using ZAnnot1 = fruit::Annotated<Annotation1, Z>;
30    using ConstXAnnot1 = fruit::Annotated<Annotation1, const X>;
31    using ConstYAnnot1 = fruit::Annotated<Annotation1, const Y>;
32    using ConstZAnnot1 = fruit::Annotated<Annotation1, const Z>;
33
34    struct Annotation2 {};
35    using XAnnot2 = fruit::Annotated<Annotation2, X>;
36    using YAnnot2 = fruit::Annotated<Annotation2, Y>;
37    using ZAnnot2 = fruit::Annotated<Annotation2, Z>;
38    using ConstXAnnot2 = fruit::Annotated<Annotation2, const X>;
39    using ConstYAnnot2 = fruit::Annotated<Annotation2, const Y>;
40    using ConstZAnnot2 = fruit::Annotated<Annotation2, const Z>;
41
42    struct Annotation3 {};
43    '''
44
45CONSTRUCTOR_BINDING = (
46    '',
47    '.registerConstructor<XAnnot()>()')
48INTERFACE_BINDING = (
49    '''
50        struct Y : public X {};
51    ''',
52    '''
53        .bind<XAnnot, YAnnot>()
54        .registerConstructor<YAnnot()>()
55    ''')
56INTERFACE_BINDING2 = (
57    '''
58        struct Y2 : public X {};
59    ''',
60    '''
61        .bind<XAnnot, Y2Annot>()
62        .registerConstructor<Y2Annot()>()
63    ''')
64INSTALL = (
65    '''
66        fruit::Component<XAnnot> getParentComponent() {
67          return fruit::createComponent()
68            .registerConstructor<XAnnot()>();
69        }
70    ''',
71    '.install(getParentComponent)')
72INSTALL2 = (
73    '''
74        fruit::Component<XAnnot> getParentComponent2() {
75          return fruit::createComponent()
76            .registerConstructor<XAnnot()>();
77        }
78    ''',
79    '.install(getParentComponent2)')
80CONST_BINDING_FROM_INSTALL = (
81    '''
82        fruit::Component<const XAnnot> getParentComponent() {
83          return fruit::createComponent()
84            .registerConstructor<XAnnot()>();
85        }
86    ''',
87    '.install(getParentComponent)')
88CONST_BINDING_FROM_INSTALL2 = (
89    '''
90        fruit::Component<const XAnnot> getParentComponent2() {
91          return fruit::createComponent()
92            .registerConstructor<XAnnot()>();
93        }
94    ''',
95    '.install(getParentComponent2)')
96CONST_BINDING = (
97    '''
98        const X x{};
99    ''',
100    '.bindInstance<XAnnot, X>(x)')
101CONST_BINDING2 = (
102    '''
103        const X x2{};
104    ''',
105    '.bindInstance<XAnnot, X>(x2)')
106
107class TestBindingClash(parameterized.TestCase):
108    @multiple_named_parameters([
109        ('CONSTRUCTOR_BINDING + INSTALL',) + CONSTRUCTOR_BINDING + INSTALL,
110        ('INTERFACE_BINDING + INSTALL',) + INTERFACE_BINDING + INSTALL,
111        ('INSTALL + INSTALL2',) + INSTALL + INSTALL2,
112        ('CONSTRUCTOR_BINDING + CONST_BINDING_FROM_INSTALL',) + CONSTRUCTOR_BINDING + CONST_BINDING_FROM_INSTALL,
113        ('INTERFACE_BINDING + CONST_BINDING_FROM_INSTALL',) + INTERFACE_BINDING + CONST_BINDING_FROM_INSTALL,
114        ('INSTALL2 + CONST_BINDING_FROM_INSTALL',) + INSTALL2 + CONST_BINDING_FROM_INSTALL,
115        ('CONST_BINDING_FROM_INSTALL + INSTALL2',) + CONST_BINDING_FROM_INSTALL + INSTALL2,
116        ('CONST_BINDING + INSTALL2',) + CONST_BINDING + INSTALL2,
117        ('CONST_BINDING_FROM_INSTALL + CONST_BINDING_FROM_INSTALL2',) + CONST_BINDING_FROM_INSTALL + CONST_BINDING_FROM_INSTALL2,
118        ('CONST_BINDING + CONST_BINDING_FROM_INSTALL',) + CONST_BINDING + CONST_BINDING_FROM_INSTALL,
119    ], [
120        ('No annotation', 'X', 'Y', 'Y2'),
121        ('With annotation', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation3, Y2>'),
122    ])
123    def test_clash_with_install(self,
124            binding1_preparation, binding1, binding2_preparation, binding2, XAnnot, YAnnot, Y2Annot):
125        source = '''
126            struct X{};
127
128            %s
129            %s
130
131            fruit::Component<XAnnot> getComponent() {
132              return fruit::createComponent()
133                  %s
134                  %s;
135            }
136            ''' % (binding1_preparation, binding2_preparation, binding1, binding2)
137        expect_compile_error(
138            'DuplicateTypesInComponentError<XAnnot>',
139            'The installed component provides some types that are already provided by the current component.',
140            COMMON_DEFINITIONS,
141            source,
142            locals())
143
144    @multiple_named_parameters([
145        ('CONSTRUCTOR_BINDING + CONSTRUCTOR_BINDING',) + CONSTRUCTOR_BINDING + CONSTRUCTOR_BINDING,
146        ('CONSTRUCTOR_BINDING + INTERFACE_BINDING',) + CONSTRUCTOR_BINDING + INTERFACE_BINDING,
147        ('INTERFACE_BINDING + CONSTRUCTOR_BINDING',) + INTERFACE_BINDING + CONSTRUCTOR_BINDING,
148        ('INTERFACE_BINDING + INTERFACE_BINDING2',) + INTERFACE_BINDING + INTERFACE_BINDING2,
149        ('INSTALL + CONSTRUCTOR_BINDING',) + INSTALL + CONSTRUCTOR_BINDING,
150        ('INSTALL + INTERFACE_BINDING',) + INSTALL + INTERFACE_BINDING,
151        ('CONST_BINDING_FROM_INSTALL + CONSTRUCTOR_BINDING',) + CONST_BINDING_FROM_INSTALL + CONSTRUCTOR_BINDING,
152        ('CONST_BINDING_FROM_INSTALL + INTERFACE_BINDING',) + CONST_BINDING_FROM_INSTALL + INTERFACE_BINDING,
153        ('CONST_BINDING + CONSTRUCTOR_BINDING',) + CONST_BINDING + CONSTRUCTOR_BINDING,
154        ('CONST_BINDING + INTERFACE_BINDING',) + CONST_BINDING + INTERFACE_BINDING,
155        ('CONSTRUCTOR_BINDING + CONST_BINDING',) + CONSTRUCTOR_BINDING + CONST_BINDING,
156        ('INTERFACE_BINDING + CONST_BINDING',) + INTERFACE_BINDING + CONST_BINDING,
157        ('INSTALL2 + CONST_BINDING',) + INSTALL2 + CONST_BINDING,
158        ('CONST_BINDING_FROM_INSTALL + CONST_BINDING',) + CONST_BINDING_FROM_INSTALL + CONST_BINDING,
159        ('CONST_BINDING + CONST_BINDING2',) + CONST_BINDING + CONST_BINDING2,
160    ], [
161        ('No annotation', 'X', 'Y', 'Y2'),
162        ('With annotation', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation3, Y2>'),
163    ])
164    def test_clash_with_binding(self, binding1_preparation, binding1, binding2_preparation, binding2, XAnnot, YAnnot, Y2Annot):
165        source = '''
166            struct X{};
167
168            %s
169            %s
170
171            fruit::Component<XAnnot> getComponent() {
172              return fruit::createComponent()
173                  %s
174                  %s;
175            }
176
177            ''' % (binding1_preparation, binding2_preparation, binding1, binding2)
178        expect_compile_error(
179            'TypeAlreadyBoundError<XAnnot>',
180            'Trying to bind C but it is already bound.',
181            COMMON_DEFINITIONS,
182            source,
183            locals())
184
185    CONSTRUCTOR_BINDING_ANNOT1 = (
186        '',
187        '.registerConstructor<XAnnot1()>()')
188    CONSTRUCTOR_BINDING_ANNOT2 = (
189        '',
190        '.registerConstructor<XAnnot2()>()')
191    INTERFACE_BINDING_ANNOT1 = (
192        '''
193            struct Y : public X {};
194        ''',
195        '''
196            .bind<XAnnot1, YAnnot1>()
197            .registerConstructor<YAnnot1()>()
198        ''')
199    INTERFACE_BINDING_ANNOT2 = (
200        '''
201            struct Z : public X {};
202        ''',
203        '''
204            .bind<XAnnot2, ZAnnot2>()
205            .registerConstructor<ZAnnot2()>()
206        ''')
207    INSTALL_ANNOT1 = (
208        '''
209            fruit::Component<XAnnot1> getParentComponent1() {
210              return fruit::createComponent()
211                .registerConstructor<XAnnot1()>();
212            }
213        ''',
214        '.install(getParentComponent1)')
215    INSTALL_ANNOT2 = (
216        '''
217            fruit::Component<XAnnot2> getParentComponent2() {
218              return fruit::createComponent()
219                .registerConstructor<XAnnot2()>();
220            }
221        ''',
222        '.install(getParentComponent2)')
223    CONST_BINDING_FROM_INSTALL_ANNOT1 = (
224        '''
225            fruit::Component<ConstXAnnot1> getParentComponent1() {
226              return fruit::createComponent()
227                .registerConstructor<XAnnot1()>();
228            }
229        ''',
230        '.install(getParentComponent1)')
231    CONST_BINDING_FROM_INSTALL_ANNOT2 = (
232        '''
233            fruit::Component<ConstXAnnot2> getParentComponent2() {
234              return fruit::createComponent()
235                .registerConstructor<XAnnot2()>();
236            }
237        ''',
238        '.install(getParentComponent2)')
239    CONST_BINDING_ANNOT1 = (
240        '''
241            const X x1{};
242        ''',
243        '.bindInstance<XAnnot1, X>(x1)')
244    CONST_BINDING_ANNOT2 = (
245        '''
246            const X x2{};
247        ''',
248        '.bindInstance<XAnnot2, X>(x2)')
249
250    @parameterized.named_parameters([
251        ('CONSTRUCTOR_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',) + CONSTRUCTOR_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
252        ('CONSTRUCTOR_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2',) + CONSTRUCTOR_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2,
253        ('INTERFACE_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',) + INTERFACE_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
254        ('INTERFACE_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2',) + INTERFACE_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2,
255        ('INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',) + INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
256        ('INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2',) + INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2,
257        ('CONST_BINDING_FROM_INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',) + CONST_BINDING_FROM_INSTALL_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
258        ('CONST_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2',) + CONST_BINDING_ANNOT1 + CONSTRUCTOR_BINDING_ANNOT2,
259        ('CONST_BINDING_FROM_INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2',) + CONST_BINDING_FROM_INSTALL_ANNOT1 + INTERFACE_BINDING_ANNOT2,
260        ('CONST_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2',) + CONST_BINDING_ANNOT1 + INTERFACE_BINDING_ANNOT2,
261        ('CONSTRUCTOR_BINDING_ANNOT1 + INSTALL_ANNOT2',) + CONSTRUCTOR_BINDING_ANNOT1 + INSTALL_ANNOT2,
262        ('INTERFACE_BINDING_ANNOT1 + INSTALL_ANNOT2',) + INTERFACE_BINDING_ANNOT1 + INSTALL_ANNOT2,
263        ('CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',) + CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
264        ('CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_ANNOT2',) + CONSTRUCTOR_BINDING_ANNOT1 + CONST_BINDING_ANNOT2,
265        ('INTERFACE_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',) + INTERFACE_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
266        ('INTERFACE_BINDING_ANNOT1 + CONST_BINDING_ANNOT2',) + INTERFACE_BINDING_ANNOT1 + CONST_BINDING_ANNOT2,
267        ('INSTALL_ANNOT1 + INSTALL_ANNOT2',) + INSTALL_ANNOT1 + INSTALL_ANNOT2,
268        ('CONST_BINDING_FROM_INSTALL_ANNOT1 + INSTALL_ANNOT2',) + CONST_BINDING_FROM_INSTALL_ANNOT1 + INSTALL_ANNOT2,
269        ('CONST_BINDING_ANNOT1 + INSTALL_ANNOT2',) + CONST_BINDING_ANNOT1 + INSTALL_ANNOT2,
270        ('INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',) + INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
271        ('INSTALL_ANNOT1 + CONST_BINDING_ANNOT2',) + INSTALL_ANNOT1 + CONST_BINDING_ANNOT2,
272        ('CONST_BINDING_FROM_INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',) + CONST_BINDING_FROM_INSTALL_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
273        ('CONST_BINDING_ANNOT1 + CONST_BINDING_ANNOT2',) + CONST_BINDING_ANNOT1 + CONST_BINDING_ANNOT2,
274        ('CONST_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2',) + CONST_BINDING_ANNOT1 + CONST_BINDING_FROM_INSTALL_ANNOT2,
275    ])
276    def test_no_clash_with_different_annotations(self, binding1_preparation, binding1, binding2_preparation, binding2):
277        source = '''
278            struct X {};
279
280            %s
281            %s
282
283            fruit::Component<const XAnnot1, const XAnnot2> getComponent() {
284              return fruit::createComponent()
285                  %s
286                  %s;
287            }
288
289            int main() {
290                fruit::Injector<const XAnnot1, const XAnnot2> injector(getComponent);
291                injector.get<XAnnot1>();
292                injector.get<XAnnot2>();
293            }
294            ''' % (binding1_preparation, binding2_preparation, binding1, binding2)
295        expect_success(
296            COMMON_DEFINITIONS,
297            source)
298
299    @parameterized.parameters([
300        ('X', 'X', 'X'),
301        ('const X', 'X', 'X'),
302        ('X', 'const X', 'X'),
303        ('const X', 'const X', 'X'),
304        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
305        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
306        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
307        ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'),
308    ])
309    def test_during_component_merge(self, NormalizedComponentXAnnot, ComponentXAnnot, XAnnot):
310        source = '''
311            struct X {};
312
313            fruit::Component<NormalizedComponentXAnnot> getComponent1() {
314              return fruit::createComponent()
315                .registerConstructor<XAnnot()>();
316            }
317
318            fruit::Component<ComponentXAnnot> getComponent2() {
319              return fruit::createComponent()
320                .registerConstructor<XAnnot()>();
321            }
322
323            void f() {
324              fruit::NormalizedComponent<NormalizedComponentXAnnot> nc(getComponent1);
325              fruit::Injector<> injector(nc, getComponent2);
326              (void) injector;
327            }
328            '''
329        expect_compile_error(
330            'DuplicateTypesInComponentError<XAnnot>',
331            'The installed component provides some types that are already provided',
332            COMMON_DEFINITIONS,
333            source,
334            locals())
335
336    def test_during_component_merge_with_different_annotation_ok(self):
337        source = '''
338            struct X {};
339
340            fruit::Component<XAnnot1> getComponent1() {
341              return fruit::createComponent()
342                .registerConstructor<XAnnot1()>();
343            }
344
345            fruit::Component<XAnnot2> getComponent2() {
346              return fruit::createComponent()
347                .registerConstructor<XAnnot2()>();
348            }
349
350            int main() {
351              fruit::NormalizedComponent<XAnnot1> nc(getComponent1);
352              fruit::Injector<XAnnot1, XAnnot2> injector(nc, getComponent2);
353              injector.get<XAnnot1>();
354              injector.get<XAnnot2>();
355            }
356            '''
357        expect_success(
358            COMMON_DEFINITIONS,
359            source)
360
361    @parameterized.parameters([
362        ('X', '(struct )?X'),
363        ('fruit::Annotated<Annotation1, X>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
364    ])
365    def test_bind_instance_and_bind_instance_runtime(self, XAnnot, XAnnotRegex):
366        source = '''
367            struct X {};
368
369            fruit::Component<> getComponentForInstanceHelper() {
370              // Note: don't do this in real code, leaks memory.
371              return fruit::createComponent()
372                .bindInstance<XAnnot, X>(*(new X()));
373            }
374
375            fruit::Component<XAnnot> getComponentForInstance() {
376              // Note: don't do this in real code, leaks memory.
377              return fruit::createComponent()
378                .install(getComponentForInstanceHelper)
379                .bindInstance<XAnnot, X>(*(new X()));
380            }
381
382            int main() {
383              fruit::Injector<XAnnot> injector(getComponentForInstance);
384              injector.get<XAnnot>();
385            }
386            '''
387        expect_runtime_error(
388            'Fatal injection error: the type XAnnotRegex was provided more than once, with different bindings.',
389            COMMON_DEFINITIONS,
390            source,
391            locals())
392
393    @parameterized.parameters([
394        ('X', '(struct )?X'),
395        ('fruit::Annotated<Annotation1, X>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'),
396    ])
397    def test_bind_instance_and_binding_runtime(self, XAnnot, XAnnotRegex):
398        source = '''
399            struct X {};
400
401            fruit::Component<> getComponentForInstanceHelper(X* x) {
402              return fruit::createComponent()
403                .bindInstance<XAnnot, X>(*x);
404            }
405
406            fruit::Component<XAnnot> getComponentForInstance(X* x) {
407              return fruit::createComponent()
408                .install(getComponentForInstanceHelper, x)
409                .registerConstructor<XAnnot()>();
410            }
411
412            int main() {
413              X x;
414              fruit::Injector<XAnnot> injector(getComponentForInstance, &x);
415              injector.get<XAnnot>();
416            }
417            '''
418        expect_runtime_error(
419            'Fatal injection error: the type XAnnotRegex was provided more than once, with different bindings.',
420            COMMON_DEFINITIONS,
421            source,
422            locals())
423
424    @parameterized.parameters([
425        'X',
426        'fruit::Annotated<Annotation1, X>',
427    ])
428    def test_during_component_merge_consistent_ok(self, XAnnot):
429        source = '''
430            struct X : public ConstructionTracker<X> {
431              using Inject = X();
432            };
433
434            fruit::Component<XAnnot> getComponent() {
435              return fruit::createComponent();
436            }
437
438            fruit::Component<> getRootComponent() {
439              return fruit::createComponent()
440                  .install(getComponent);
441            }
442
443            int main() {
444              fruit::NormalizedComponent<> normalizedComponent(getRootComponent);
445              fruit::Injector<XAnnot> injector(normalizedComponent, getComponent);
446
447              Assert(X::num_objects_constructed == 0);
448              injector.get<XAnnot>();
449              Assert(X::num_objects_constructed == 1);
450            }
451            '''
452        expect_success(
453            COMMON_DEFINITIONS,
454            source,
455            locals())
456
457if __name__ == '__main__':
458    absltest.main()
459