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 XAnnot1 = fruit::Annotated<Annotation1, X>;
26
27    struct Annotation2 {};
28    using XAnnot2 = fruit::Annotated<Annotation2, X>;
29    '''
30
31class TestNormalizedComponent(parameterized.TestCase):
32    @parameterized.parameters([
33        ('X', 'X', 'Y'),
34        ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, X)', 'fruit::Annotated<Annotation2, Y>'),
35    ])
36    def test_success_normalized_component_provides_unused(self, XAnnot, X_ANNOT, YAnnot):
37        source = '''
38            struct X {};
39
40            struct Y {
41              INJECT(Y(X_ANNOT)) {};
42            };
43
44            fruit::Component<fruit::Required<XAnnot>, YAnnot> getComponent() {
45              return fruit::createComponent();
46            }
47
48            fruit::Component<XAnnot> getXComponent(X* x) {
49              return fruit::createComponent()
50                .bindInstance<XAnnot, X>(*x);
51            }
52
53            int main() {
54              fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent(getComponent);
55
56              X x{};
57
58              fruit::Injector<XAnnot> injector(normalizedComponent, getXComponent, &x);
59              injector.get<XAnnot>();
60            }
61            '''
62        expect_success(
63            COMMON_DEFINITIONS,
64            source,
65            locals())
66
67    @parameterized.parameters([
68        ('X', 'X', 'Y'),
69        ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, X)', 'fruit::Annotated<Annotation2, Y>'),
70    ])
71    def test_success(self, XAnnot, X_ANNOT, YAnnot):
72        source = '''
73            struct X {};
74
75            struct Y {
76              INJECT(Y(X_ANNOT)) {};
77            };
78
79            fruit::Component<fruit::Required<XAnnot>, YAnnot> getComponent() {
80              return fruit::createComponent();
81            }
82
83            fruit::Component<XAnnot> getXComponent(X* x) {
84              return fruit::createComponent()
85                .bindInstance<XAnnot, X>(*x);
86            }
87
88            int main() {
89              fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent(getComponent);
90
91              X x{};
92
93              fruit::Injector<YAnnot> injector(normalizedComponent, getXComponent, &x);
94              injector.get<YAnnot>();
95            }
96            '''
97        expect_success(
98            COMMON_DEFINITIONS,
99            source,
100            locals())
101
102    @parameterized.parameters([
103        ('X', 'X', 'Y'),
104        ('fruit::Annotated<Annotation1, X>', 'ANNOTATED(Annotation1, X)', 'fruit::Annotated<Annotation2, Y>'),
105    ])
106    def test_success_inline_component(self, XAnnot, X_ANNOT, YAnnot):
107        source = '''
108            struct X {};
109
110            struct Y {
111              INJECT(Y(X_ANNOT)) {};
112            };
113
114            fruit::Component<fruit::Required<XAnnot>, YAnnot> getComponent() {
115              return fruit::createComponent();
116            }
117
118            fruit::Component<XAnnot> getAdditionalComponent(X* x) {
119              return fruit::createComponent()
120                .bindInstance<XAnnot, X>(*x);
121            }
122
123            int main() {
124              fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent(getComponent);
125
126              X x{};
127
128              fruit::Injector<YAnnot> injector(normalizedComponent, getAdditionalComponent, &x);
129              injector.get<YAnnot>();
130            }
131            '''
132        expect_success(
133            COMMON_DEFINITIONS,
134            source,
135            locals())
136
137    @parameterized.parameters([
138        'X',
139        'fruit::Annotated<Annotation1, X>',
140    ])
141    def test_injector_from_normalized_component_unsatisfied_requirements(self, XAnnot):
142        source = '''
143            struct X {};
144
145            fruit::Component<fruit::Required<XAnnot>> getComponent();
146            fruit::Component<> getEmptyComponent();
147
148            int main() {
149              fruit::NormalizedComponent<fruit::Required<XAnnot>> normalizedComponent(getComponent);
150              fruit::Injector<> injector(normalizedComponent, getEmptyComponent);
151            }
152            '''
153        expect_compile_error(
154            'UnsatisfiedRequirementsInNormalizedComponentError<XAnnot>',
155            'The requirements in UnsatisfiedRequirements are required by the NormalizedComponent but are not provided by the Component',
156            COMMON_DEFINITIONS,
157            source,
158            locals())
159
160    @parameterized.parameters([
161        ('X', 'const X'),
162        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
163    ])
164    def test_normalized_component_providing_nonconst_from_component_providing_const_error(self, XAnnot, ConstXAnnot):
165        source = '''
166            struct X {};
167
168            fruit::Component<XAnnot> getComponent();
169
170            int main() {
171              fruit::NormalizedComponent<ConstXAnnot> normalizedComponent(getComponent);
172              (void) normalizedComponent;
173            }
174            '''
175        expect_generic_compile_error(
176            r'no matching function for call to .fruit::NormalizedComponent<ConstXAnnot>::NormalizedComponent\(fruit::Component<XAnnot> \(&\)\(\)\).'
177            r'|no matching constructor for initialization of .fruit::NormalizedComponent<ConstXAnnot>.'
178            r'|.fruit::NormalizedComponent<ConstXAnnot>::NormalizedComponent.: none of the .* overloads could convert all the argument types',
179            COMMON_DEFINITIONS,
180            source,
181            locals())
182
183    # TODO: we should probably return a more specific error here.
184    @parameterized.parameters([
185        ('X', 'Y'),
186        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'),
187    ])
188    def test_injector_from_normalized_component_nonconst_requirements_provided_as_const_error(self, XAnnot, YAnnot):
189        source = '''
190            struct X {};
191            struct Y {};
192
193            fruit::Component<const XAnnot> getXComponent();
194
195            void f(fruit::NormalizedComponent<fruit::Required<XAnnot>, YAnnot> normalizedComponent) {
196              fruit::Injector<YAnnot> injector(normalizedComponent, getXComponent);
197            }
198            '''
199        expect_compile_error(
200            'NonConstBindingRequiredButConstBindingProvidedError<XAnnot>',
201            'The type T was provided as constant, however one of the constructors/providers/factories in this component',
202            COMMON_DEFINITIONS,
203            source,
204            locals())
205
206if __name__ == '__main__':
207    absltest.main()
208