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 import pytest 16 17 from fruit_test_common import * 18 19 COMMON_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 31 def test_empty_injector(): 32 source = ''' 33 fruit::Component<> getComponent() { 34 return fruit::createComponent(); 35 } 36 37 int main() { 38 fruit::Injector<> injector(getComponent); 39 } 40 ''' 41 expect_success( 42 COMMON_DEFINITIONS, 43 source) 44 45 @pytest.mark.parametrize('XAnnot', [ 46 'X', 47 'fruit::Annotated<Annotation1, X>', 48 ]) 49 def test_error_component_with_requirements(XAnnot): 50 source = ''' 51 struct X {}; 52 53 fruit::Component<fruit::Required<XAnnot>> getComponent(); 54 55 void f(fruit::NormalizedComponent<XAnnot> normalizedComponent) { 56 fruit::Injector<XAnnot> injector(normalizedComponent, getComponent); 57 } 58 ''' 59 expect_compile_error( 60 'ComponentWithRequirementsInInjectorError<XAnnot>', 61 'When using the two-argument constructor of Injector, the component used as second parameter must not have requirements', 62 COMMON_DEFINITIONS, 63 source, 64 locals()) 65 66 @pytest.mark.parametrize('XAnnot', [ 67 'X', 68 'fruit::Annotated<Annotation1, X>', 69 ]) 70 def test_error_declared_types_not_provided(XAnnot): 71 source = ''' 72 struct X { 73 using Inject = X(); 74 }; 75 76 fruit::Component<> getEmptyComponent() { 77 return fruit::createComponent(); 78 } 79 80 int main() { 81 fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent); 82 fruit::Injector<XAnnot> injector(normalizedComponent, getEmptyComponent); 83 } 84 ''' 85 expect_compile_error( 86 'TypesInInjectorNotProvidedError<XAnnot>', 87 'The types in TypesNotProvided are declared as provided by the injector, but none of the two components passed to the Injector constructor provides them.', 88 COMMON_DEFINITIONS, 89 source, 90 locals()) 91 92 @pytest.mark.parametrize('XAnnot,ConstXAnnot', [ 93 ('X', 'const X'), 94 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'), 95 ]) 96 def test_error_declared_nonconst_types_provided_as_const(XAnnot, ConstXAnnot): 97 source = ''' 98 struct X { 99 using Inject = X(); 100 }; 101 102 fruit::Component<ConstXAnnot> getComponent(); 103 104 int main() { 105 fruit::Injector<XAnnot> injector(getComponent); 106 } 107 ''' 108 expect_generic_compile_error( 109 'no matching constructor for initialization of .fruit::Injector<XAnnot>.' 110 '|no matching function for call to .fruit::Injector<XAnnot>::Injector\(fruit::Component<ConstXAnnot> \(&\)\(\)\).' 111 # MSVC 112 '|.fruit::Injector<XAnnot>::Injector.: none of the 2 overloads could convert all the argument types', 113 COMMON_DEFINITIONS, 114 source, 115 locals()) 116 117 @pytest.mark.parametrize('XAnnot,ConstXAnnot', [ 118 ('X', 'const X'), 119 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>'), 120 ]) 121 def test_error_declared_nonconst_types_provided_as_const_with_normalized_component(XAnnot, ConstXAnnot): 122 source = ''' 123 struct X {}; 124 125 fruit::Component<> getEmptyComponent(); 126 127 void f(fruit::NormalizedComponent<ConstXAnnot> normalizedComponent) { 128 fruit::Injector<XAnnot> injector(normalizedComponent, getEmptyComponent); 129 } 130 ''' 131 expect_compile_error( 132 'TypesInInjectorProvidedAsConstOnlyError<XAnnot>', 133 'The types in TypesProvidedAsConstOnly are declared as non-const provided types by the injector', 134 COMMON_DEFINITIONS, 135 source, 136 locals()) 137 138 @pytest.mark.parametrize('XAnnot,YAnnot', [ 139 ('X', 'Y'), 140 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>'), 141 ]) 142 def test_injector_get_error_type_not_provided(XAnnot, YAnnot): 143 source = ''' 144 struct X { 145 using Inject = X(); 146 }; 147 148 struct Y {}; 149 150 fruit::Component<XAnnot> getComponent() { 151 return fruit::createComponent(); 152 } 153 154 int main() { 155 fruit::Injector<XAnnot> injector(getComponent); 156 injector.get<YAnnot>(); 157 } 158 ''' 159 expect_compile_error( 160 'TypeNotProvidedError<YAnnot>', 161 'Trying to get an instance of T, but it is not provided by this Provider/Injector.', 162 COMMON_DEFINITIONS, 163 source, 164 locals()) 165 166 @pytest.mark.parametrize('ConstXAnnot,XInjectorGetParam,XInjectorGetParamRegex', [ 167 ('const X', 'X&', 'X&'), 168 ('const X', 'X*', 'X\*'), 169 ('const X', 'std::shared_ptr<X>', 'std::shared_ptr<X>'), 170 ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X&>', 'fruit::Annotated<Annotation1, X&>'), 171 ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X*>', 'fruit::Annotated<Annotation1, X\*>'), 172 ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>'), 173 ]) 174 def test_injector_const_provided_type_does_not_allow_injecting_nonconst_variants(ConstXAnnot, XInjectorGetParam, XInjectorGetParamRegex): 175 source = ''' 176 void f(fruit::Injector<ConstXAnnot> injector) { 177 injector.get<XInjectorGetParam>(); 178 } 179 ''' 180 expect_compile_error( 181 'TypeProvidedAsConstOnlyError<XInjectorGetParamRegex>', 182 'Trying to get an instance of T, but it is only provided as a constant by this Provider/Injector', 183 COMMON_DEFINITIONS, 184 source, 185 locals()) 186 187 @pytest.mark.parametrize('XBindingInInjector,XInjectorGetParam', [ 188 ('X', 'X'), 189 ('X', 'const X&'), 190 ('X', 'const X*'), 191 ('X', 'X&'), 192 ('X', 'X*'), 193 ('X', 'std::shared_ptr<X>'), 194 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'), 195 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X&>'), 196 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X*>'), 197 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X&>'), 198 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'), 199 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, std::shared_ptr<X>>'), 200 ]) 201 def test_injector_get_ok(XBindingInInjector, XInjectorGetParam): 202 source = ''' 203 struct X { 204 using Inject = X(); 205 }; 206 207 fruit::Component<XBindingInInjector> getComponent() { 208 return fruit::createComponent(); 209 } 210 211 int main() { 212 fruit::Injector<XBindingInInjector> injector(getComponent); 213 214 auto x = injector.get<XInjectorGetParam>(); 215 (void)x; 216 } 217 ''' 218 expect_success( 219 COMMON_DEFINITIONS, 220 source, 221 locals()) 222 223 @pytest.mark.parametrize('XBindingInInjector,XInjectorGetParam', [ 224 ('const X', 'X'), 225 ('const X', 'const X&'), 226 ('const X', 'const X*'), 227 ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, X>'), 228 ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X&>'), 229 ('fruit::Annotated<Annotation1, const X>', 'fruit::Annotated<Annotation1, const X*>'), 230 ]) 231 def test_injector_get_const_binding_ok(XBindingInInjector, XInjectorGetParam): 232 XBindingInInjectorWithoutConst = XBindingInInjector.replace('const ', '') 233 source = ''' 234 struct X {}; 235 236 const X x{}; 237 238 fruit::Component<XBindingInInjector> getComponent() { 239 return fruit::createComponent() 240 .bindInstance<XBindingInInjectorWithoutConst, X>(x); 241 } 242 243 int main() { 244 fruit::Injector<XBindingInInjector> injector(getComponent); 245 246 auto x = injector.get<XInjectorGetParam>(); 247 (void)x; 248 } 249 ''' 250 expect_success( 251 COMMON_DEFINITIONS, 252 source, 253 locals()) 254 255 @pytest.mark.parametrize('XVariant,XVariantRegex', [ 256 ('X**', r'X\*\*'), 257 ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'), 258 ('const std::shared_ptr<X>', r'const std::shared_ptr<X>'), 259 ('X* const', r'X\* const'), 260 ('const X* const', r'const X\* const'), 261 ('std::nullptr_t', r'(std::)?nullptr(_t)?'), 262 ('X*&', r'X\*&'), 263 ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'), 264 ('void', r'void'), 265 ('fruit::Annotated<Annotation1, X**>', r'X\*\*'), 266 ]) 267 def test_injector_get_error_type_not_injectable(XVariant,XVariantRegex): 268 source = ''' 269 struct X {}; 270 271 void f(fruit::Injector<X> injector) { 272 injector.get<XVariant>(); 273 } 274 ''' 275 expect_compile_error( 276 'NonInjectableTypeError<XVariantRegex>', 277 'The type T is not injectable.', 278 COMMON_DEFINITIONS, 279 source, 280 locals()) 281 282 @pytest.mark.parametrize('XVariant,XVariantRegex', [ 283 ('X[]', r'X\[\]'), 284 ]) 285 def test_injector_get_error_array_type(XVariant,XVariantRegex): 286 source = ''' 287 struct X {}; 288 289 void f(fruit::Injector<X> injector) { 290 injector.get<XVariant>(); 291 } 292 ''' 293 expect_generic_compile_error( 294 'function cannot return array type' 295 '|function returning an array' 296 # MSVC 297 '|.fruit::Injector<X>::get.: no matching overloaded function found', 298 COMMON_DEFINITIONS, 299 source, 300 locals()) 301 302 if __name__== '__main__': 303 main(__file__) 304