• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
24    struct Annotation1 {};
25    using XAnnot = fruit::Annotated<Annotation1, X>;
26
27    struct Annotation2 {};
28
29    struct Annotation3 {};
30
31    template <typename T>
32    using WithNoAnnotation = T;
33
34    template <typename T>
35    using WithAnnotation1 = fruit::Annotated<Annotation1, T>;
36    '''
37
38class TestRegisterConstructor(parameterized.TestCase):
39
40    def test_register_constructor_success_copyable_and_movable(self):
41        source = '''
42            struct X {
43              INJECT(X()) = default;
44              X(X&&) = default;
45              X(const X&) = default;
46            };
47
48            fruit::Component<X> getComponent() {
49              return fruit::createComponent();
50            }
51
52            int main() {
53              fruit::Injector<X> injector(getComponent);
54              injector.get<X*>();
55            }
56            '''
57        expect_success(
58            COMMON_DEFINITIONS,
59            source)
60
61    def test_register_constructor_success_movable_only(self):
62        source = '''
63            struct X {
64              INJECT(X()) = default;
65              X(X&&) = default;
66              X(const X&) = delete;
67            };
68
69            fruit::Component<X> getComponent() {
70              return fruit::createComponent();
71            }
72
73            int main() {
74              fruit::Injector<X> injector(getComponent);
75              injector.get<X*>();
76            }
77            '''
78        expect_success(
79            COMMON_DEFINITIONS,
80            source)
81
82    def test_register_constructor_success_not_movable(self):
83        source = '''
84            struct X {
85              INJECT(X()) = default;
86              X(X&&) = delete;
87              X(const X&) = delete;
88            };
89
90            fruit::Component<X> getComponent() {
91              return fruit::createComponent();
92            }
93
94            int main() {
95              fruit::Injector<X> injector(getComponent);
96              injector.get<X*>();
97            }
98            '''
99        expect_success(
100            COMMON_DEFINITIONS,
101            source)
102
103    # TODO: consider moving to test_normalized_component.py
104    @parameterized.parameters([
105        ('X', 'Y', 'Y', 'Z'),
106        ('X', 'Y', 'const Y', 'Z'),
107        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation2, const Y>', 'fruit::Annotated<Annotation3, Z>'),
108    ])
109    def test_autoinject_with_annotation_success(self, XAnnot, YAnnot, MaybeConstYAnnot, ZAnnot):
110        source = '''
111            struct X {
112              using Inject = X();
113            };
114
115            struct Y : public ConstructionTracker<Y> {
116              using Inject = Y();
117            };
118
119            struct Z {
120              using Inject = Z();
121            };
122
123            fruit::Component<ZAnnot, MaybeConstYAnnot, XAnnot> getComponent() {
124              return fruit::createComponent();
125            }
126
127            fruit::Component<> getEmptyComponent() {
128              return fruit::createComponent();
129            }
130
131            int main() {
132              fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
133              fruit::Injector<MaybeConstYAnnot> injector(normalizedComponent, getComponent);
134
135              Assert(Y::num_objects_constructed == 0);
136              injector.get<YAnnot>();
137              Assert(Y::num_objects_constructed == 1);
138            }
139            '''
140        expect_success(
141            COMMON_DEFINITIONS,
142            source,
143            locals())
144
145    def test_autoinject_annotation_in_signature_return_type(self):
146        source = '''
147            struct X {
148              using Inject = XAnnot();
149            };
150
151            fruit::Component<XAnnot> getComponent() {
152              return fruit::createComponent();
153            }
154            '''
155        expect_compile_error(
156            'InjectTypedefWithAnnotationError<X>',
157            'C::Inject is a signature that returns an annotated type',
158            COMMON_DEFINITIONS,
159            source)
160
161    def test_autoinject_wrong_class_in_typedef(self):
162        source = '''
163            struct X {
164              using Inject = X();
165            };
166
167            struct Y : public X {
168            };
169
170            fruit::Component<Y> getComponent() {
171              return fruit::createComponent();
172            }
173            '''
174        expect_compile_error(
175            'InjectTypedefForWrongClassError<Y,X>',
176            'C::Inject is a signature, but does not return a C. Maybe the class C has no Inject typedef and',
177            COMMON_DEFINITIONS,
178            source)
179
180    def test_register_constructor_error_abstract_class(self):
181        source = '''
182            struct X {
183              X(int*) {}
184
185              virtual void foo() = 0;
186            };
187
188            fruit::Component<X> getComponent() {
189              return fruit::createComponent()
190                .registerConstructor<fruit::Annotated<Annotation1, X>(int*)>();
191            }
192            '''
193        if re.search('GNU|MSVC', CXX_COMPILER_NAME) is not None:
194            expect_generic_compile_error(
195                'invalid abstract return type'
196                '|.X.: cannot instantiate abstract class',
197                COMMON_DEFINITIONS,
198                source)
199        else:
200            expect_compile_error(
201                'CannotConstructAbstractClassError<X>',
202                'The specified class can.t be constructed because it.s an abstract class',
203                COMMON_DEFINITIONS,
204                source)
205
206    def test_register_constructor_error_malformed_signature(self):
207        source = '''
208            struct X {
209              X(int) {}
210            };
211
212            fruit::Component<X> getComponent() {
213              return fruit::createComponent()
214                .registerConstructor<X[]>();
215            }
216            '''
217        expect_compile_error(
218            r'NotASignatureError<X\[\]>',
219            r'CandidateSignature was specified as parameter, but it.s not a signature. Signatures are of the form',
220            COMMON_DEFINITIONS,
221            source)
222
223    def test_register_constructor_error_malformed_signature_autoinject(self):
224        source = '''
225            struct X {
226              using Inject = X[];
227              X(int) {}
228            };
229
230            fruit::Component<X> getComponent() {
231              return fruit::createComponent();
232            }
233            '''
234        expect_compile_error(
235            r'InjectTypedefNotASignatureError<X,X\[\]>',
236            r'C::Inject should be a typedef to a signature',
237            COMMON_DEFINITIONS,
238            source)
239
240    @parameterized.parameters([
241        'char*',
242        'fruit::Annotated<Annotation1, char*>',
243    ])
244    def test_register_constructor_does_not_exist_error(self, charPtrAnnot):
245        source = '''
246            struct X {
247              X(int*) {}
248            };
249
250            fruit::Component<X> getComponent() {
251              return fruit::createComponent()
252                .registerConstructor<X(charPtrAnnot)>();
253            }
254            '''
255        expect_compile_error(
256            r'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>',
257            r'contains an Inject typedef but it.s not constructible with the specified types',
258            COMMON_DEFINITIONS,
259            source,
260            locals())
261
262    @parameterized.parameters([
263        'char*',
264        'fruit::Annotated<Annotation1, char*>',
265    ])
266    def test_autoinject_constructor_does_not_exist_error(self, charPtrAnnot):
267        source = '''
268            struct X {
269              using Inject = X(charPtrAnnot);
270              X(int*) {}
271            };
272
273            fruit::Component<X> getComponent() {
274              return fruit::createComponent();
275            }
276            '''
277        expect_compile_error(
278            r'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>',
279            r'contains an Inject typedef but it.s not constructible with the specified types',
280            COMMON_DEFINITIONS,
281            source,
282            locals())
283
284    def test_autoinject_abstract_class_error(self):
285        source = '''
286            struct X {
287              using Inject = fruit::Annotated<Annotation1, X>();
288
289              virtual void scale() = 0;
290              // Note: here we "forgot" to implement scale() (on purpose, for this test) so X is an abstract class.
291            };
292
293            fruit::Component<fruit::Annotated<Annotation1, X>> getComponent() {
294              return fruit::createComponent();
295            }
296            '''
297        expect_compile_error(
298            'CannotConstructAbstractClassError<X>',
299            'The specified class can.t be constructed because it.s an abstract class.',
300            COMMON_DEFINITIONS,
301            source)
302
303    @multiple_parameters([
304        'WithNoAnnotation',
305        'WithAnnotation1',
306    ], [
307        'Y',
308        'const Y',
309        'Y*',
310        'const Y*',
311        'Y&',
312        'const Y&',
313        'std::shared_ptr<Y>',
314        'fruit::Provider<Y>',
315        'fruit::Provider<const Y>',
316    ])
317    def test_register_constructor_with_param_success(self, WithAnnotation, YVariant):
318        source = '''
319            struct Y {};
320            struct X {
321              X(YVariant) {
322              }
323            };
324
325            fruit::Component<WithAnnotation<Y>> getYComponent() {
326              return fruit::createComponent()
327                .registerConstructor<WithAnnotation<Y>()>();
328            }
329
330            fruit::Component<X> getComponent() {
331              return fruit::createComponent()
332                .install(getYComponent)
333                .registerConstructor<X(WithAnnotation<YVariant>)>();
334            }
335
336            int main() {
337              fruit::Injector<X> injector(getComponent);
338              injector.get<X>();
339            }
340            '''
341        expect_success(
342            COMMON_DEFINITIONS,
343            source,
344            locals())
345
346    @multiple_parameters([
347        'WithNoAnnotation',
348        'WithAnnotation1',
349    ], [
350        'Y',
351        'const Y',
352        'const Y*',
353        'const Y&',
354        'fruit::Provider<const Y>',
355    ])
356    def test_register_constructor_with_param_const_binding_success(self, WithAnnotation, YVariant):
357        source = '''
358            struct Y {};
359            struct X {
360              X(YVariant) {
361              }
362            };
363
364            const Y y{};
365
366            fruit::Component<WithAnnotation<const Y>> getYComponent() {
367              return fruit::createComponent()
368                .bindInstance<WithAnnotation<Y>, Y>(y);
369            }
370
371            fruit::Component<X> getComponent() {
372              return fruit::createComponent()
373                .install(getYComponent)
374                .registerConstructor<X(WithAnnotation<YVariant>)>();
375            }
376
377            int main() {
378              fruit::Injector<X> injector(getComponent);
379              injector.get<X>();
380            }
381            '''
382        expect_success(
383            COMMON_DEFINITIONS,
384            source,
385            locals())
386
387    @multiple_parameters([
388        ('WithNoAnnotation', 'Y'),
389        ('WithAnnotation1', 'fruit::Annotated<Annotation1,Y>'),
390    ], [
391        'Y*',
392        'Y&',
393        'std::shared_ptr<Y>',
394        'fruit::Provider<Y>',
395    ])
396    def test_register_constructor_with_param_error_nonconst_param_required(self, WithAnnotation, YAnnotRegex, YVariant):
397        source = '''
398            struct Y {};
399            struct X {
400              X(YVariant);
401            };
402
403            fruit::Component<WithAnnotation<const Y>> getYComponent();
404
405            fruit::Component<> getComponent() {
406              return fruit::createComponent()
407                .install(getYComponent)
408                .registerConstructor<X(WithAnnotation<YVariant>)>();
409            }
410            '''
411        expect_compile_error(
412            'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
413            'The type T was provided as constant, however one of the constructors/providers/factories in this component',
414            COMMON_DEFINITIONS,
415            source,
416            locals())
417
418    @multiple_parameters([
419        ('WithNoAnnotation', 'Y'),
420        ('WithAnnotation1', 'fruit::Annotated<Annotation1, Y>'),
421    ], [
422        'Y*',
423        'Y&',
424        'std::shared_ptr<Y>',
425        'fruit::Provider<Y>',
426    ])
427    def test_register_constructor_with_param_error_nonconst_param_required_install_after(self, WithAnnotation, YAnnotRegex, YVariant):
428        source = '''
429            struct Y {};
430            struct X {
431              X(YVariant);
432            };
433
434            fruit::Component<WithAnnotation<const Y>> getYComponent();
435
436            fruit::Component<> getComponent() {
437              return fruit::createComponent()
438                .registerConstructor<X(WithAnnotation<YVariant>)>()
439                .install(getYComponent);
440            }
441            '''
442        expect_compile_error(
443            'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>',
444            'The type T was provided as constant, however one of the constructors/providers/factories in this component',
445            COMMON_DEFINITIONS,
446            source,
447            locals())
448
449    def test_register_constructor_requiring_nonconst_then_requiring_const_ok(self):
450        source = '''
451            struct X {};
452
453            struct Y {
454              Y(X&) {}
455            };
456
457            struct Z {
458              Z(const X&) {}
459            };
460
461            fruit::Component<Y, Z> getRootComponent() {
462              return fruit::createComponent()
463                .registerConstructor<Y(X&)>()
464                .registerConstructor<Z(const X&)>()
465                .registerConstructor<X()>();
466            }
467
468            int main() {
469              fruit::Injector<Y, Z> injector(getRootComponent);
470              injector.get<Y>();
471              injector.get<Z>();
472            }
473            '''
474        expect_success(
475            COMMON_DEFINITIONS,
476            source,
477            locals())
478
479    def test_register_constructor_requiring_nonconst_then_requiring_const_declaring_const_requirement_error(self):
480        source = '''
481            struct X {};
482
483            struct Y {
484              Y(X&) {}
485            };
486
487            struct Z {
488              Z(const X&) {}
489            };
490
491            fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
492              return fruit::createComponent()
493                .registerConstructor<Y(X&)>()
494                .registerConstructor<Z(const X&)>();
495            }
496            '''
497        expect_compile_error(
498            'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
499            'The type T was declared as a const Required type in the returned Component, however',
500            COMMON_DEFINITIONS,
501            source,
502            locals())
503
504    def test_register_constructor_requiring_const_then_requiring_nonconst_ok(self):
505        source = '''
506            struct X {};
507
508            struct Y {
509              Y(const X&) {}
510            };
511
512            struct Z {
513              Z(X&) {}
514            };
515
516            fruit::Component<Y, Z> getRootComponent() {
517              return fruit::createComponent()
518                .registerConstructor<Y(const X&)>()
519                .registerConstructor<Z(X&)>()
520                .registerConstructor<X()>();
521            }
522
523            int main() {
524              fruit::Injector<Y, Z> injector(getRootComponent);
525              injector.get<Y>();
526              injector.get<Z>();
527            }
528            '''
529        expect_success(
530            COMMON_DEFINITIONS,
531            source,
532            locals())
533
534    def test_register_constructor_requiring_const_then_requiring_nonconst_declaring_const_requirement_error(self):
535        source = '''
536            struct X {};
537
538            struct Y {
539              Y(const X&) {}
540            };
541
542            struct Z {
543              Z(X&) {}
544            };
545
546            fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() {
547              return fruit::createComponent()
548                .registerConstructor<Y(const X&)>()
549                .registerConstructor<Z(X&)>();
550            }
551            '''
552        expect_compile_error(
553            'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>',
554            'The type T was declared as a const Required type in the returned Component, however',
555            COMMON_DEFINITIONS,
556            source,
557            locals())
558
559    @parameterized.parameters([
560        ('Y**', r'Y\*\*'),
561        ('std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'),
562        ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
563        ('Y*&', r'Y\*&'),
564        ('Y(*)()', r'Y(\((__cdecl)?\*\))?\((void)?\)'),
565        ('fruit::Annotated<Annotation1, Y**>', r'Y\*\*'),
566    ])
567    def test_register_constructor_with_param_error_type_not_injectable(self, YVariant, YVariantRegex):
568        source = '''
569            struct Y {};
570            struct X {
571              X(YVariant);
572            };
573
574            fruit::Component<> getComponent() {
575              return fruit::createComponent()
576                .registerConstructor<X(YVariant)>();
577            }
578            '''
579        expect_compile_error(
580            'NonInjectableTypeError<YVariantRegex>',
581            'The type T is not injectable.',
582            COMMON_DEFINITIONS,
583            source,
584            locals())
585
586if __name__ == '__main__':
587    absltest.main()
588