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
25@pytest.mark.parametrize('XAnnot', [
26    'X',
27    'fruit::Annotated<Annotation1, X>',
28])
29def test_multibindings_bind_instance_ok(XAnnot):
30    source = '''
31        struct X {};
32
33        X x;
34
35        fruit::Component<> getComponent() {
36          return fruit::createComponent()
37            .addInstanceMultibinding<XAnnot, X>(x);
38        }
39
40        int main() {
41          fruit::Injector<> injector(getComponent);
42
43          std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
44          Assert(multibindings.size() == 1);
45          Assert(multibindings[0] == &x);
46        }
47        '''
48    expect_success(
49        COMMON_DEFINITIONS,
50        source,
51        locals())
52
53@pytest.mark.parametrize('XAnnot', [
54    'X',
55    'fruit::Annotated<Annotation1, X>',
56])
57def test_multibindings_bind_const_instance_error(XAnnot):
58    source = '''
59        struct X {};
60
61        const X x{};
62
63        fruit::Component<> getComponent() {
64          return fruit::createComponent()
65            .addInstanceMultibinding<XAnnot, X>(x);
66        }
67        '''
68    expect_generic_compile_error(
69        'candidate function not viable: 1st argument \(.const X.\) would lose const qualifier'
70        '|no matching function for call to .fruit::PartialComponent<.*>::addInstanceMultibinding(<XAnnot,X>)?\(const X&\).'
71        '|error: no matching member function for call to .addInstanceMultibinding.'
72        '|cannot convert argument 1 from .const X. to .X &.',
73        COMMON_DEFINITIONS,
74        source,
75        locals())
76
77@pytest.mark.parametrize('XAnnot', [
78    'X',
79    'fruit::Annotated<Annotation1, X>',
80])
81def test_multibindings_bind_instance_vector(XAnnot):
82    source = '''
83        struct X {};
84
85        std::vector<X> values = {X(), X()};
86
87        fruit::Component<> getComponent() {
88          return fruit::createComponent()
89            .addInstanceMultibindings<XAnnot, X>(values);
90        }
91
92        int main() {
93          fruit::Injector<> injector(getComponent);
94
95          std::vector<X*> multibindings = injector.getMultibindings<XAnnot>();
96          Assert(multibindings.size() == 2);
97          Assert(multibindings[0] == &(values[0]));
98          Assert(multibindings[1] == &(values[1]));
99        }
100        '''
101    expect_success(
102        COMMON_DEFINITIONS,
103        source,
104        locals())
105
106@pytest.mark.parametrize('XAnnot', [
107    'X',
108    'fruit::Annotated<Annotation1, X>',
109])
110def test_multibindings_bind_const_instance_vector_error(XAnnot):
111    source = '''
112        struct X {};
113
114        const std::vector<X> values{};
115
116        fruit::Component<> getComponent() {
117          return fruit::createComponent()
118            .addInstanceMultibindings<XAnnot, X>(values);
119        }
120        '''
121    expect_generic_compile_error(
122        'candidate function not viable: 1st argument \(.const std::vector<X>.\) would lose const qualifier'
123        '|cannot convert .values. \(type .const std::(__debug::)?vector<X>.\) to type .std::(__debug::)?vector<X>&.'
124        '|no matching member function for call to .addInstanceMultibindings.'
125        '|cannot convert argument 1 from .const std::vector<X,std::allocator<.*>>. to .std::vector<X,std::allocator<.*>> &.',
126        COMMON_DEFINITIONS,
127        source,
128        locals())
129
130@pytest.mark.parametrize('XAnnot', [
131    'X',
132    'fruit::Annotated<Annotation1, X>',
133])
134def test_multibindings_bind_instance_vector_of_consts_error(XAnnot):
135    source = '''
136        struct X {};
137
138        std::vector<const X> values;
139
140        fruit::Component<> getComponent() {
141          return fruit::createComponent()
142            .addInstanceMultibindings<XAnnot, X>(values);
143        }
144        '''
145    expect_generic_compile_error(
146        '.*',
147        COMMON_DEFINITIONS,
148        source,
149        locals())
150
151@pytest.mark.parametrize('XVariant,XVariantRegex', [
152    ('X**', r'X\*\*'),
153    ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'),
154    ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'),
155    ('X* const', r'X\* const'),
156    ('const X* const', r'const X\* const'),
157    ('X*&', r'X\*&'),
158    ('fruit::Annotated<Annotation1, X**>', r'X\*\*'),
159])
160def test_multibindings_bind_instance_non_class_type_error(XVariant, XVariantRegex):
161    source = '''
162        struct X {};
163
164        using XVariantT = XVariant;
165        fruit::Component<> getComponent(XVariantT x) {
166          return fruit::createComponent()
167            .addInstanceMultibinding<XVariant, XVariant>(x);
168        }
169        '''
170    expect_compile_error(
171        'NonClassTypeError<XVariantRegex,X>',
172        'A non-class type T was specified.',
173        COMMON_DEFINITIONS,
174        source,
175        locals())
176
177@pytest.mark.parametrize('XVariant,XVariantRegex', [
178    ('std::nullptr_t', r'(std::)?nullptr(_t)?'),
179    ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'),
180])
181def test_multibindings_bind_instance_non_injectable_type_error(XVariant, XVariantRegex):
182    source = '''
183        struct X {};
184
185        using XVariantT = XVariant;
186        fruit::Component<> getComponent(XVariantT x) {
187          return fruit::createComponent()
188            .addInstanceMultibinding<XVariant, XVariant>(x);
189        }
190        '''
191    expect_compile_error(
192        'NonInjectableTypeError<XVariantRegex>',
193        'The type T is not injectable.',
194        COMMON_DEFINITIONS,
195        source,
196        locals())
197
198if __name__== '__main__':
199    main(__file__)
200