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