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 TestComponent(parameterized.TestCase):
32    @parameterized.parameters([
33        'X',
34        'fruit::Annotated<Annotation1, X>',
35    ])
36    def test_move(self, XAnnot):
37        source = '''
38            struct X {
39              using Inject = X();
40            };
41
42            fruit::Component<XAnnot> getComponent() {
43              fruit::Component<XAnnot> c = fruit::createComponent();
44              fruit::Component<XAnnot> c2 = std::move(c);
45              return fruit::Component<XAnnot>(std::move(c2));
46            }
47
48            int main() {
49              fruit::Injector<XAnnot> injector(getComponent);
50              injector.get<XAnnot>();
51            }
52            '''
53        expect_success(
54            COMMON_DEFINITIONS,
55            source,
56            locals())
57
58    @parameterized.parameters([
59        'X',
60        'fruit::Annotated<Annotation1, X>',
61    ])
62    def test_move_partial_component(self, XAnnot):
63        source = '''
64            struct X {
65              using Inject = X();
66            };
67
68            fruit::Component<XAnnot> getComponent() {
69              auto c = fruit::createComponent();
70              auto c1 = std::move(c);
71              return std::move(c1);
72            }
73
74            int main() {
75              fruit::Injector<XAnnot> injector(getComponent);
76              injector.get<XAnnot>();
77            }
78            '''
79        expect_generic_compile_error(
80            r'error: use of deleted function .fruit::PartialComponent<Bindings>::PartialComponent\(fruit::PartialComponent<Bindings>&&\).'
81            r'|error: call to deleted constructor of .fruit::PartialComponent<>.'
82            # MSVC 2017
83            r'|error C2280: .fruit::PartialComponent<>::PartialComponent\(fruit::PartialComponent<> &&\).: attempting to reference a deleted function'
84            # MSVC 2015
85            r'|error C2248: .fruit::PartialComponent<>::PartialComponent.: cannot access private member declared in class .fruit::PartialComponent<>.',
86            COMMON_DEFINITIONS,
87            source,
88            locals())
89
90    @parameterized.parameters([
91        ('X', 'X'),
92        ('X', 'const X'),
93        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
94        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
95    ])
96    def test_error_no_binding_found(self, XAnnot, ConstXAnnot):
97        source = '''
98            struct X {};
99
100            fruit::Component<ConstXAnnot> getComponent() {
101              return fruit::createComponent();
102            }
103            '''
104        expect_compile_error(
105            'NoBindingFoundError<XAnnot>',
106            'No explicit binding nor C::Inject definition was found for T.',
107            COMMON_DEFINITIONS,
108            source,
109            locals())
110
111    @parameterized.parameters([
112        ('X', 'X'),
113        ('X', 'const X'),
114        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'),
115        ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'),
116    ])
117    def test_error_no_binding_found_abstract_class(self, XAnnot, ConstXAnnot):
118        source = '''
119            struct X {
120              virtual void f() = 0;
121            };
122
123            fruit::Component<ConstXAnnot> getComponent() {
124              return fruit::createComponent();
125            }
126            '''
127        expect_compile_error(
128            'NoBindingFoundForAbstractClassError<XAnnot,X>',
129            'No explicit binding was found for T, and note that C is an abstract class',
130            COMMON_DEFINITIONS,
131            source,
132            locals())
133
134    @parameterized.parameters([
135        '',
136        'const',
137    ])
138    def test_error_no_factory_binding_found(self, MaybeConst):
139        source = '''
140            struct X {};
141
142            fruit::Component<MaybeConst std::function<std::unique_ptr<X>()>> getComponent() {
143              return fruit::createComponent();
144            }
145            '''
146        expect_compile_error(
147            r'NoBindingFoundError<std::function<std::unique_ptr<X(,std::default_delete<X>)?>\((void)?\)>',
148            'No explicit binding nor C::Inject definition was found for T.',
149            COMMON_DEFINITIONS,
150            source,
151            locals())
152
153    @parameterized.parameters([
154        '',
155        'const',
156    ])
157    def test_error_no_factory_binding_found_with_annotation(self, MaybeConst):
158        source = '''
159            struct X {};
160
161            fruit::Component<fruit::Annotated<Annotation1, MaybeConst std::function<std::unique_ptr<X>()>>> getComponent() {
162              return fruit::createComponent();
163            }
164            '''
165        expect_compile_error(
166            r'NoBindingFoundError<fruit::Annotated<Annotation1,std::function<std::unique_ptr<X(,std::default_delete<X>)?>\((void)?\)>>',
167            'No explicit binding nor C::Inject definition was found for T.',
168            COMMON_DEFINITIONS,
169            source,
170            locals())
171
172if __name__ == '__main__':
173    absltest.main()
174