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 struct Annotation3 {}; 30 31 template <typename T> 32 using WithNoAnnotation = T; 33 34 template <typename T> 35 using WithAnnotation1 = fruit::Annotated<Annotation1, T>; 36 ''' 37 38def test_register_constructor_success_copyable_and_movable(): 39 source = ''' 40 struct X { 41 INJECT(X()) = default; 42 X(X&&) = default; 43 X(const X&) = default; 44 }; 45 46 fruit::Component<X> getComponent() { 47 return fruit::createComponent(); 48 } 49 50 int main() { 51 fruit::Injector<X> injector(getComponent); 52 injector.get<X*>(); 53 } 54 ''' 55 expect_success( 56 COMMON_DEFINITIONS, 57 source) 58 59def test_register_constructor_success_movable_only(): 60 source = ''' 61 struct X { 62 INJECT(X()) = default; 63 X(X&&) = default; 64 X(const X&) = delete; 65 }; 66 67 fruit::Component<X> getComponent() { 68 return fruit::createComponent(); 69 } 70 71 int main() { 72 fruit::Injector<X> injector(getComponent); 73 injector.get<X*>(); 74 } 75 ''' 76 expect_success( 77 COMMON_DEFINITIONS, 78 source) 79 80def test_register_constructor_success_not_movable(): 81 source = ''' 82 struct X { 83 INJECT(X()) = default; 84 X(X&&) = delete; 85 X(const X&) = delete; 86 }; 87 88 fruit::Component<X> getComponent() { 89 return fruit::createComponent(); 90 } 91 92 int main() { 93 fruit::Injector<X> injector(getComponent); 94 injector.get<X*>(); 95 } 96 ''' 97 expect_success( 98 COMMON_DEFINITIONS, 99 source) 100 101# TODO: consider moving to test_normalized_component.py 102@pytest.mark.parametrize('XAnnot,YAnnot,MaybeConstYAnnot,ZAnnot', [ 103 ('X', 'Y', 'Y', 'Z'), 104 ('X', 'Y', 'const Y', 'Z'), 105 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation2, Y>', 'fruit::Annotated<Annotation2, const Y>', 'fruit::Annotated<Annotation3, Z>'), 106]) 107def test_autoinject_with_annotation_success(XAnnot, YAnnot, MaybeConstYAnnot, ZAnnot): 108 source = ''' 109 struct X { 110 using Inject = X(); 111 }; 112 113 struct Y : public ConstructionTracker<Y> { 114 using Inject = Y(); 115 }; 116 117 struct Z { 118 using Inject = Z(); 119 }; 120 121 fruit::Component<ZAnnot, MaybeConstYAnnot, XAnnot> getComponent() { 122 return fruit::createComponent(); 123 } 124 125 fruit::Component<> getEmptyComponent() { 126 return fruit::createComponent(); 127 } 128 129 int main() { 130 fruit::NormalizedComponent<> normalizedComponent(getEmptyComponent); 131 fruit::Injector<MaybeConstYAnnot> injector(normalizedComponent, getComponent); 132 133 Assert(Y::num_objects_constructed == 0); 134 injector.get<YAnnot>(); 135 Assert(Y::num_objects_constructed == 1); 136 } 137 ''' 138 expect_success( 139 COMMON_DEFINITIONS, 140 source, 141 locals()) 142 143def test_autoinject_annotation_in_signature_return_type(): 144 source = ''' 145 struct X { 146 using Inject = XAnnot(); 147 }; 148 149 fruit::Component<XAnnot> getComponent() { 150 return fruit::createComponent(); 151 } 152 ''' 153 expect_compile_error( 154 'InjectTypedefWithAnnotationError<X>', 155 'C::Inject is a signature that returns an annotated type', 156 COMMON_DEFINITIONS, 157 source) 158 159def test_autoinject_wrong_class_in_typedef(): 160 source = ''' 161 struct X { 162 using Inject = X(); 163 }; 164 165 struct Y : public X { 166 }; 167 168 fruit::Component<Y> getComponent() { 169 return fruit::createComponent(); 170 } 171 ''' 172 expect_compile_error( 173 'InjectTypedefForWrongClassError<Y,X>', 174 'C::Inject is a signature, but does not return a C. Maybe the class C has no Inject typedef and', 175 COMMON_DEFINITIONS, 176 source) 177 178def test_register_constructor_error_abstract_class(): 179 source = ''' 180 struct X { 181 X(int*) {} 182 183 virtual void foo() = 0; 184 }; 185 186 fruit::Component<X> getComponent() { 187 return fruit::createComponent() 188 .registerConstructor<fruit::Annotated<Annotation1, X>(int*)>(); 189 } 190 ''' 191 if re.search('GNU|MSVC', CXX_COMPILER_NAME) is not None: 192 expect_generic_compile_error( 193 'invalid abstract return type' 194 '|.X.: cannot instantiate abstract class', 195 COMMON_DEFINITIONS, 196 source) 197 else: 198 expect_compile_error( 199 'CannotConstructAbstractClassError<X>', 200 'The specified class can.t be constructed because it.s an abstract class', 201 COMMON_DEFINITIONS, 202 source) 203 204def test_register_constructor_error_malformed_signature(): 205 source = ''' 206 struct X { 207 X(int) {} 208 }; 209 210 fruit::Component<X> getComponent() { 211 return fruit::createComponent() 212 .registerConstructor<X[]>(); 213 } 214 ''' 215 expect_compile_error( 216 'NotASignatureError<X\[\]>', 217 'CandidateSignature was specified as parameter, but it.s not a signature. Signatures are of the form', 218 COMMON_DEFINITIONS, 219 source) 220 221def test_register_constructor_error_malformed_signature_autoinject(): 222 source = ''' 223 struct X { 224 using Inject = X[]; 225 X(int) {} 226 }; 227 228 fruit::Component<X> getComponent() { 229 return fruit::createComponent(); 230 } 231 ''' 232 expect_compile_error( 233 'InjectTypedefNotASignatureError<X,X\[\]>', 234 'C::Inject should be a typedef to a signature', 235 COMMON_DEFINITIONS, 236 source) 237 238@pytest.mark.parametrize('charPtrAnnot', [ 239 'char*', 240 'fruit::Annotated<Annotation1, char*>', 241]) 242def test_register_constructor_does_not_exist_error(charPtrAnnot): 243 source = ''' 244 struct X { 245 X(int*) {} 246 }; 247 248 fruit::Component<X> getComponent() { 249 return fruit::createComponent() 250 .registerConstructor<X(charPtrAnnot)>(); 251 } 252 ''' 253 expect_compile_error( 254 'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>', 255 'contains an Inject typedef but it.s not constructible with the specified types', 256 COMMON_DEFINITIONS, 257 source, 258 locals()) 259 260@pytest.mark.parametrize('charPtrAnnot', [ 261 'char*', 262 'fruit::Annotated<Annotation1, char*>', 263]) 264def test_autoinject_constructor_does_not_exist_error(charPtrAnnot): 265 source = ''' 266 struct X { 267 using Inject = X(charPtrAnnot); 268 X(int*) {} 269 }; 270 271 fruit::Component<X> getComponent() { 272 return fruit::createComponent(); 273 } 274 ''' 275 expect_compile_error( 276 'NoConstructorMatchingInjectSignatureError<X,X\(char\*\)>', 277 'contains an Inject typedef but it.s not constructible with the specified types', 278 COMMON_DEFINITIONS, 279 source, 280 locals()) 281 282def test_autoinject_abstract_class_error(): 283 source = ''' 284 struct X { 285 using Inject = fruit::Annotated<Annotation1, X>(); 286 287 virtual void scale() = 0; 288 // Note: here we "forgot" to implement scale() (on purpose, for this test) so X is an abstract class. 289 }; 290 291 fruit::Component<fruit::Annotated<Annotation1, X>> getComponent() { 292 return fruit::createComponent(); 293 } 294 ''' 295 expect_compile_error( 296 'CannotConstructAbstractClassError<X>', 297 'The specified class can.t be constructed because it.s an abstract class.', 298 COMMON_DEFINITIONS, 299 source) 300 301@pytest.mark.parametrize('WithAnnotation', [ 302 'WithNoAnnotation', 303 'WithAnnotation1', 304]) 305@pytest.mark.parametrize('YVariant', [ 306 'Y', 307 'const Y', 308 'Y*', 309 'const Y*', 310 'Y&', 311 'const Y&', 312 'std::shared_ptr<Y>', 313 'fruit::Provider<Y>', 314 'fruit::Provider<const Y>', 315]) 316def test_register_constructor_with_param_success(WithAnnotation, YVariant): 317 source = ''' 318 struct Y {}; 319 struct X { 320 X(YVariant) { 321 } 322 }; 323 324 fruit::Component<WithAnnotation<Y>> getYComponent() { 325 return fruit::createComponent() 326 .registerConstructor<WithAnnotation<Y>()>(); 327 } 328 329 fruit::Component<X> getComponent() { 330 return fruit::createComponent() 331 .install(getYComponent) 332 .registerConstructor<X(WithAnnotation<YVariant>)>(); 333 } 334 335 int main() { 336 fruit::Injector<X> injector(getComponent); 337 injector.get<X>(); 338 } 339 ''' 340 expect_success( 341 COMMON_DEFINITIONS, 342 source, 343 locals()) 344 345@pytest.mark.parametrize('WithAnnotation', [ 346 'WithNoAnnotation', 347 'WithAnnotation1', 348]) 349@pytest.mark.parametrize('YVariant', [ 350 'Y', 351 'const Y', 352 'const Y*', 353 'const Y&', 354 'fruit::Provider<const Y>', 355]) 356def test_register_constructor_with_param_const_binding_success(WithAnnotation, YVariant): 357 source = ''' 358 struct Y {}; 359 struct X { 360 X(YVariant) { 361 } 362 }; 363 364 const Y y{}; 365 366 fruit::Component<WithAnnotation<const Y>> getYComponent() { 367 return fruit::createComponent() 368 .bindInstance<WithAnnotation<Y>, Y>(y); 369 } 370 371 fruit::Component<X> getComponent() { 372 return fruit::createComponent() 373 .install(getYComponent) 374 .registerConstructor<X(WithAnnotation<YVariant>)>(); 375 } 376 377 int main() { 378 fruit::Injector<X> injector(getComponent); 379 injector.get<X>(); 380 } 381 ''' 382 expect_success( 383 COMMON_DEFINITIONS, 384 source, 385 locals()) 386 387@pytest.mark.parametrize('WithAnnotation,YAnnotRegex', [ 388 ('WithNoAnnotation', 'Y'), 389 ('WithAnnotation1', 'fruit::Annotated<Annotation1,Y>'), 390]) 391@pytest.mark.parametrize('YVariant', [ 392 'Y*', 393 'Y&', 394 'std::shared_ptr<Y>', 395 'fruit::Provider<Y>', 396]) 397def test_register_constructor_with_param_error_nonconst_param_required(WithAnnotation, YAnnotRegex, YVariant): 398 source = ''' 399 struct Y {}; 400 struct X { 401 X(YVariant); 402 }; 403 404 fruit::Component<WithAnnotation<const Y>> getYComponent(); 405 406 fruit::Component<> getComponent() { 407 return fruit::createComponent() 408 .install(getYComponent) 409 .registerConstructor<X(WithAnnotation<YVariant>)>(); 410 } 411 ''' 412 expect_compile_error( 413 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>', 414 'The type T was provided as constant, however one of the constructors/providers/factories in this component', 415 COMMON_DEFINITIONS, 416 source, 417 locals()) 418 419@pytest.mark.parametrize('WithAnnotation,YAnnotRegex', [ 420 ('WithNoAnnotation', 'Y'), 421 ('WithAnnotation1', 'fruit::Annotated<Annotation1, Y>'), 422]) 423@pytest.mark.parametrize('YVariant', [ 424 'Y*', 425 'Y&', 426 'std::shared_ptr<Y>', 427 'fruit::Provider<Y>', 428]) 429def test_register_constructor_with_param_error_nonconst_param_required_install_after(WithAnnotation, YAnnotRegex, YVariant): 430 source = ''' 431 struct Y {}; 432 struct X { 433 X(YVariant); 434 }; 435 436 fruit::Component<WithAnnotation<const Y>> getYComponent(); 437 438 fruit::Component<> getComponent() { 439 return fruit::createComponent() 440 .registerConstructor<X(WithAnnotation<YVariant>)>() 441 .install(getYComponent); 442 } 443 ''' 444 expect_compile_error( 445 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>', 446 'The type T was provided as constant, however one of the constructors/providers/factories in this component', 447 COMMON_DEFINITIONS, 448 source, 449 locals()) 450 451def test_register_constructor_requiring_nonconst_then_requiring_const_ok(): 452 source = ''' 453 struct X {}; 454 455 struct Y { 456 Y(X&) {} 457 }; 458 459 struct Z { 460 Z(const X&) {} 461 }; 462 463 fruit::Component<Y, Z> getRootComponent() { 464 return fruit::createComponent() 465 .registerConstructor<Y(X&)>() 466 .registerConstructor<Z(const X&)>() 467 .registerConstructor<X()>(); 468 } 469 470 int main() { 471 fruit::Injector<Y, Z> injector(getRootComponent); 472 injector.get<Y>(); 473 injector.get<Z>(); 474 } 475 ''' 476 expect_success( 477 COMMON_DEFINITIONS, 478 source, 479 locals()) 480 481def test_register_constructor_requiring_nonconst_then_requiring_const_declaring_const_requirement_error(): 482 source = ''' 483 struct X {}; 484 485 struct Y { 486 Y(X&) {} 487 }; 488 489 struct Z { 490 Z(const X&) {} 491 }; 492 493 fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() { 494 return fruit::createComponent() 495 .registerConstructor<Y(X&)>() 496 .registerConstructor<Z(const X&)>(); 497 } 498 ''' 499 expect_compile_error( 500 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>', 501 'The type T was declared as a const Required type in the returned Component, however', 502 COMMON_DEFINITIONS, 503 source, 504 locals()) 505 506def test_register_constructor_requiring_const_then_requiring_nonconst_ok(): 507 source = ''' 508 struct X {}; 509 510 struct Y { 511 Y(const X&) {} 512 }; 513 514 struct Z { 515 Z(X&) {} 516 }; 517 518 fruit::Component<Y, Z> getRootComponent() { 519 return fruit::createComponent() 520 .registerConstructor<Y(const X&)>() 521 .registerConstructor<Z(X&)>() 522 .registerConstructor<X()>(); 523 } 524 525 int main() { 526 fruit::Injector<Y, Z> injector(getRootComponent); 527 injector.get<Y>(); 528 injector.get<Z>(); 529 } 530 ''' 531 expect_success( 532 COMMON_DEFINITIONS, 533 source, 534 locals()) 535 536def test_register_constructor_requiring_const_then_requiring_nonconst_declaring_const_requirement_error(): 537 source = ''' 538 struct X {}; 539 540 struct Y { 541 Y(const X&) {} 542 }; 543 544 struct Z { 545 Z(X&) {} 546 }; 547 548 fruit::Component<fruit::Required<const X>, Y, Z> getRootComponent() { 549 return fruit::createComponent() 550 .registerConstructor<Y(const X&)>() 551 .registerConstructor<Z(X&)>(); 552 } 553 ''' 554 expect_compile_error( 555 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>', 556 'The type T was declared as a const Required type in the returned Component, however', 557 COMMON_DEFINITIONS, 558 source, 559 locals()) 560 561@pytest.mark.parametrize('YVariant,YVariantRegex', [ 562 ('Y**', r'Y\*\*'), 563 ('std::shared_ptr<Y>*', r'std::shared_ptr<Y>\*'), 564 ('std::nullptr_t', r'(std::)?nullptr(_t)?'), 565 ('Y*&', r'Y\*&'), 566 ('Y(*)()', r'Y(\((__cdecl)?\*\))?\((void)?\)'), 567 ('fruit::Annotated<Annotation1, Y**>', r'Y\*\*'), 568]) 569def test_register_constructor_with_param_error_type_not_injectable(YVariant, YVariantRegex): 570 source = ''' 571 struct Y {}; 572 struct X { 573 X(YVariant); 574 }; 575 576 fruit::Component<> getComponent() { 577 return fruit::createComponent() 578 .registerConstructor<X(YVariant)>(); 579 } 580 ''' 581 expect_compile_error( 582 'NonInjectableTypeError<YVariantRegex>', 583 'The type T is not injectable.', 584 COMMON_DEFINITIONS, 585 source, 586 locals()) 587 588 589if __name__== '__main__': 590 main(__file__) 591