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