xref: /aosp_15_r20/external/google-fruit/tests/test_bind_interface.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
26class TestBindInstance(parameterized.TestCase):
27    @parameterized.parameters([
28        ('X', 'X', 'const X&', 'Y'),
29        ('X', 'const X', 'const X&', 'Y'),
30        ('X', 'X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
31        ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
32        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
33        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
34        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
35        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
36        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
37        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
38    ])
39    def test_bind_interface(self, XAnnot, MaybeConstXAnnot, XConstRefAnnot, YAnnot):
40        source = '''
41            struct X {
42              virtual void f() const = 0;
43            };
44
45            struct Y : public X {
46              INJECT(Y()) = default;
47
48              void f() const override {
49              }
50            };
51
52            fruit::Component<MaybeConstXAnnot> getComponent() {
53              return fruit::createComponent()
54                .bind<XAnnot, YAnnot>();
55            }
56
57            int main() {
58              fruit::Injector<MaybeConstXAnnot> injector(getComponent);
59              const X& x = injector.get<XConstRefAnnot>();
60              x.f();
61            }
62        '''
63        expect_success(
64            COMMON_DEFINITIONS,
65            source,
66            locals())
67
68    @parameterized.parameters([
69        ('X', 'const X', 'const X&', 'Y'),
70        ('X', 'const X', 'const X&', 'fruit::Annotated<Annotation1, Y>'),
71        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'Y'),
72        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation1, Y>'),
73        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>', 'fruit::Annotated<Annotation2, Y>'),
74    ])
75    def test_bind_interface_to_constant(self, XAnnot, ConstXAnnot, XConstRefAnnot, YAnnot):
76        source = '''
77            struct X {
78              virtual void f() const = 0;
79            };
80
81            struct Y : public X {
82              void f() const override {
83              }
84            };
85
86            const Y y{};
87
88            fruit::Component<ConstXAnnot> getComponent() {
89              return fruit::createComponent()
90                .bindInstance<YAnnot, Y>(y)
91                .bind<XAnnot, YAnnot>();
92            }
93
94            int main() {
95              fruit::Injector<ConstXAnnot> injector(getComponent);
96              const X& x = injector.get<XConstRefAnnot>();
97              x.f();
98            }
99        '''
100        expect_success(
101            COMMON_DEFINITIONS,
102            source,
103            locals())
104
105    @parameterized.parameters([
106        ('X', 'X&', 'Y'),
107        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
108    ])
109    def test_bind_interface_target_bound_in_other_component(self, XAnnot, XRefAnnot, YAnnot):
110        source = '''
111            struct X {
112              virtual void f() = 0;
113            };
114
115            struct Y : public X {
116              void f() override {
117              }
118            };
119
120            fruit::Component<fruit::Required<YAnnot>, XAnnot> getComponent() {
121              return fruit::createComponent()
122                .bind<XAnnot, YAnnot>();
123            }
124
125            fruit::Component<XAnnot> getRootComponent() {
126              return fruit::createComponent()
127                .registerConstructor<YAnnot()>()
128                .install(getComponent);
129            }
130
131            int main() {
132              fruit::Injector<XAnnot> injector(getRootComponent);
133              X& x = injector.get<XRefAnnot>();
134              x.f();
135            }
136        '''
137        expect_success(
138            COMMON_DEFINITIONS,
139            source,
140            locals())
141
142    @parameterized.parameters([
143        ('X', 'X&', 'Y'),
144        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
145    ])
146    def test_bind_nonconst_interface_requires_nonconst_target(self, XAnnot, XRefAnnot, YAnnot):
147        source = '''
148            struct X {
149              virtual void f() = 0;
150            };
151
152            struct Y : public X {
153              void f() override {
154              }
155            };
156
157            fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() {
158              return fruit::createComponent()
159                .bind<XAnnot, YAnnot>();
160            }
161        '''
162        expect_compile_error(
163            'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>',
164            'The type T was declared as a const Required type in the returned Component, however a non-const binding',
165            COMMON_DEFINITIONS,
166            source,
167            locals())
168
169    @parameterized.parameters([
170        ('X', 'Y'),
171        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
172    ])
173    def test_bind_interface_to_constant_nonconst_required_const_bound_error(self, XAnnot, YAnnot):
174        source = '''
175            struct X {
176              virtual void f() const = 0;
177            };
178
179            struct Y : public X {
180              void f() const override {
181              }
182            };
183
184            const Y y{};
185
186            fruit::Component<XAnnot> getComponent() {
187              return fruit::createComponent()
188                .bindInstance<YAnnot, Y>(y)
189                .bind<XAnnot, YAnnot>();
190            }
191        '''
192        expect_compile_error(
193            'NonConstBindingRequiredButConstBindingProvidedError<YAnnot>',
194            'The type T was provided as constant, however one of the constructors/providers/factories in this component',
195            COMMON_DEFINITIONS,
196            source,
197            locals())
198
199    @parameterized.parameters([
200        ('X', 'X&', 'Y'),
201        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation2, Y>'),
202    ])
203    def test_bind_nonconst_interface_requires_nonconst_target_abstract(self, XAnnot, XRefAnnot, YAnnot):
204        source = '''
205            struct X {
206              virtual void f() = 0;
207            };
208
209            struct Y : public X {};
210
211            fruit::Component<fruit::Required<const YAnnot>, XAnnot> getComponent() {
212              return fruit::createComponent()
213                .bind<XAnnot, YAnnot>();
214            }
215        '''
216        expect_compile_error(
217            'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<YAnnot>',
218            'The type T was declared as a const Required type in the returned Component, however a non-const binding',
219            COMMON_DEFINITIONS,
220            source,
221            locals())
222
223    @parameterized.parameters([
224        ('X', 'int'),
225        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, int>'),
226    ])
227    def test_error_not_base(self, XAnnot, intAnnot):
228        source = '''
229            struct X {};
230
231            fruit::Component<intAnnot> getComponent() {
232              return fruit::createComponent()
233                .bind<XAnnot, intAnnot>();
234            }
235            '''
236        expect_compile_error(
237            'NotABaseClassOfError<X,int>',
238            'I is not a base class of C.',
239            COMMON_DEFINITIONS,
240            source,
241            locals())
242
243    # TODO: maybe the error should include the annotation here.
244    @parameterized.parameters([
245        'X',
246        'fruit::Annotated<Annotation1, X>',
247    ])
248    def test_error_bound_to_itself(self, XAnnot):
249        source = '''
250            struct X {};
251
252            fruit::Component<X> getComponent() {
253              return fruit::createComponent()
254                .bind<XAnnot, XAnnot>();
255            }
256            '''
257        expect_compile_error(
258            'InterfaceBindingToSelfError<X>',
259            'The type C was bound to itself.',
260            COMMON_DEFINITIONS,
261            source,
262            locals())
263
264    def test_bound_to_itself_with_annotation_error(self):
265        source = '''
266            struct X {};
267
268            fruit::Component<> getComponent() {
269              return fruit::createComponent()
270                .registerConstructor<X()>()
271                .bind<fruit::Annotated<Annotation1, X>, X>();
272            }
273            '''
274        expect_compile_error(
275            'InterfaceBindingToSelfError<X>',
276            'The type C was bound to itself.',
277            COMMON_DEFINITIONS,
278            source)
279
280    def test_bound_chain_ok(self):
281        source = '''
282            struct X {
283              virtual void f() = 0;
284            };
285
286            struct Y : public X {};
287
288            struct Z : public Y {
289              INJECT(Z()) = default;
290              void f() override {
291              }
292            };
293
294            fruit::Component<X> getComponent() {
295              return fruit::createComponent()
296                .bind<X, Y>()
297                .bind<Y, Z>();
298            }
299
300            int main() {
301              fruit::Injector<X> injector(getComponent);
302              X& x = injector.get<X&>();
303              x.f();
304            }
305        '''
306        expect_success(COMMON_DEFINITIONS, source)
307
308    def test_bind_non_normalized_types_error(self):
309        source = '''
310            struct X {};
311
312            struct Y : public std::shared_ptr<X> {};
313
314            fruit::Component<> getComponent() {
315              return fruit::createComponent()
316                .bind<std::shared_ptr<X>, Y>();
317            }
318            '''
319        expect_compile_error(
320            'NonClassTypeError<std::shared_ptr<X>,X>',
321            'A non-class type T was specified. Use C instead',
322            COMMON_DEFINITIONS,
323            source)
324
325if __name__ == '__main__':
326    absltest.main()
327