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
30@pytest.mark.parametrize('XVariant,XVariantRegexp', [
31    ('X*', 'X\*'),
32    ('const X*', 'const X\*'),
33    ('X&', 'X&'),
34    ('const X&', 'const X&'),
35    ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
36])
37def test_error_non_class_type_parameter(XVariant, XVariantRegexp):
38    source = '''
39        struct X {};
40
41        fruit::Provider<XVariant> provider;
42        '''
43    expect_compile_error(
44        'NonClassTypeError<XVariantRegexp,X>',
45        'A non-class type T was specified. Use C instead',
46        COMMON_DEFINITIONS,
47        source,
48        locals())
49
50def test_error_annotated_type_parameter():
51    source = '''
52        struct X {};
53
54        fruit::Provider<XAnnot> provider;
55        '''
56    expect_compile_error(
57        'AnnotatedTypeError<fruit::Annotated<Annotation1,X>,X>',
58        'An annotated type was specified where a non-annotated type was expected.',
59        COMMON_DEFINITIONS,
60        source)
61
62@pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [
63    ('X', 'fruit::Provider<X>', 'X', 'X'),
64    ('X', 'fruit::Provider<X>', 'X', 'const X&'),
65    ('X', 'fruit::Provider<X>', 'X', 'const X*'),
66    ('X', 'fruit::Provider<X>', 'X', 'X&'),
67    ('X', 'fruit::Provider<X>', 'X', 'X*'),
68    ('X', 'fruit::Provider<X>', 'X', 'std::shared_ptr<X>'),
69    ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<X>'),
70    ('X', 'fruit::Provider<X>', 'X', 'fruit::Provider<const X>'),
71    ('X', 'fruit::Provider<const X>', 'const X', 'const X&'),
72    ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<X>>', 'X', 'const X&'),
73    ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'),
74])
75def test_provider_get_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam):
76    source = '''
77        struct X {
78          using Inject = X();
79        };
80
81        fruit::Component<XBindingInInjector> getComponent() {
82          return fruit::createComponent();
83        }
84
85        int main() {
86          fruit::Injector<XBindingInInjector> injector(getComponent);
87          fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>();
88
89          XProviderGetParam x = provider.get<XProviderGetParam>();
90          (void)x;
91        }
92        '''
93    expect_success(
94        COMMON_DEFINITIONS,
95        source,
96        locals())
97
98@pytest.mark.parametrize('XBindingInInjector,XProviderAnnot,XParamInProvider,XProviderGetParam', [
99    ('const X', 'fruit::Provider<const X>', 'const X', 'X'),
100    ('const X', 'fruit::Provider<const X>', 'const X', 'const X&'),
101    ('const X', 'fruit::Provider<const X>', 'const X', 'const X*'),
102    ('const X', 'fruit::Provider<const X>', 'const X', 'fruit::Provider<const X>'),
103    ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, fruit::Provider<const X>>', 'const X', 'const X&'),
104])
105def test_provider_get_const_binding_ok(XBindingInInjector, XProviderAnnot, XParamInProvider, XProviderGetParam):
106    XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '')
107    source = '''
108        struct X {};
109
110        const X x{};
111
112        fruit::Component<XBindingInInjector> getComponent() {
113          return fruit::createComponent()
114              .bindInstance<XBindingInInjectorWithoutConst, X>(x);
115        }
116
117        int main() {
118          fruit::Injector<XBindingInInjector> injector(getComponent);
119          fruit::Provider<XParamInProvider> provider = injector.get<XProviderAnnot>();
120
121          XProviderGetParam x = provider.get<XProviderGetParam>();
122          (void)x;
123        }
124        '''
125    expect_success(
126        COMMON_DEFINITIONS,
127        source,
128        locals())
129
130def test_provider_get_during_injection_ok():
131    source = '''
132        struct X {
133          INJECT(X()) = default;
134          void foo() {
135          }
136        };
137
138        struct Y {
139          X x;
140          INJECT(Y(fruit::Provider<X> xProvider))
141            : x(xProvider.get<X>()) {
142          }
143
144          void foo() {
145            x.foo();
146          }
147        };
148
149        struct Z {
150          Y y;
151          INJECT(Z(fruit::Provider<Y> yProvider))
152              : y(yProvider.get<Y>()) {
153          }
154
155          void foo() {
156            y.foo();
157          }
158        };
159
160        fruit::Component<Z> getZComponent() {
161          return fruit::createComponent();
162        }
163
164        int main() {
165          fruit::Injector<Z> injector(getZComponent);
166          fruit::Provider<Z> provider(injector);
167          // During provider.get<Z>(), yProvider.get() is called, and during that xProvider.get()
168          // is called.
169          Z z = provider.get<Z>();
170          z.foo();
171        }
172        '''
173    expect_success(
174        COMMON_DEFINITIONS,
175        source)
176
177def test_provider_get_error_type_not_provided():
178    source = '''
179        struct X {};
180        struct Y {};
181
182        void f(fruit::Provider<X> provider) {
183          provider.get<Y>();
184        }
185        '''
186    expect_compile_error(
187        'TypeNotProvidedError<Y>',
188        'Trying to get an instance of T, but it is not provided by this Provider/Injector.',
189        COMMON_DEFINITIONS,
190        source)
191
192@pytest.mark.parametrize('XVariant,XVariantRegex', [
193    ('X**', r'X\*\*'),
194    ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
195    ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'),
196    ('X* const', r'X\* const'),
197    ('const X* const', r'const X\* const'),
198    ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
199    ('X*&', r'X\*&'),
200    ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
201    ('void', r'void'),
202    ('fruit::Annotated<Annotation1, fruit::Annotated<Annotation1, X>>', r'fruit::Annotated<Annotation1, X>'),
203])
204def test_provider_get_error_type_not_injectable(XVariant,XVariantRegex):
205    source = '''
206        struct X {};
207
208        void f(fruit::Provider<X> provider) {
209          provider.get<XVariant>();
210        }
211        '''
212    expect_compile_error(
213        'NonInjectableTypeError<XVariantRegex>',
214        'The type T is not injectable',
215        COMMON_DEFINITIONS,
216        source,
217        locals())
218
219@pytest.mark.parametrize('XProviderGetParam,XProviderGetParamRegex', [
220    ('X&', 'X&'),
221    ('X*', 'X\*'),
222    ('std::shared_ptr<X>', 'std::shared_ptr<X>'),
223    ('fruit::Provider<X>', 'fruit::Provider<X>'),
224])
225def test_const_provider_get_does_not_allow_injecting_nonconst_variants(XProviderGetParam, XProviderGetParamRegex):
226    source = '''
227        void f(fruit::Provider<const X> provider) {
228          provider.get<XProviderGetParam>();
229        }
230        '''
231    expect_compile_error(
232        'TypeProvidedAsConstOnlyError<XProviderGetParamRegex>',
233        'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector',
234        COMMON_DEFINITIONS,
235        source,
236        locals())
237
238@pytest.mark.parametrize('Y_PROVIDER_ANNOT', [
239    ('fruit::Provider<Y>'),
240    ('ANNOTATED(Annotation1, fruit::Provider<Y>)'),
241])
242def test_lazy_injection_with_annotations(Y_PROVIDER_ANNOT):
243    source = '''
244        struct Y : public ConstructionTracker<Y> {
245          using Inject = Y();
246        };
247
248        struct X : public ConstructionTracker<X> {
249          INJECT(X(Y_PROVIDER_ANNOT provider)) : provider(provider) {
250          }
251
252          void run() {
253            Y* y(provider);
254            (void) y;
255          }
256
257          fruit::Provider<Y> provider;
258        };
259
260        fruit::Component<X> getComponent() {
261          return fruit::createComponent();
262        }
263
264        fruit::Component<> getEmptyComponent() {
265          return fruit::createComponent();
266        }
267
268        int main() {
269          fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent);
270          fruit::Injector<X> injector(normalizedComponent, getComponent);
271
272          Assert(X::num_objects_constructed == 0);
273          Assert(Y::num_objects_constructed == 0);
274
275          X* x(injector);
276
277          Assert(X::num_objects_constructed == 1);
278          Assert(Y::num_objects_constructed == 0);
279
280          x->run();
281
282          Assert(X::num_objects_constructed == 1);
283          Assert(Y::num_objects_constructed == 1);
284        }
285        '''
286    expect_success(
287        COMMON_DEFINITIONS,
288        source,
289        locals())
290
291if __name__== '__main__':
292    main(__file__)
293