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 struct Annotation2 {}; 24 25 template <typename T> 26 using WithNoAnnot = T; 27 28 template <typename T> 29 using WithAnnot1 = fruit::Annotated<Annotation1, T>; 30 ''' 31 32@pytest.mark.parametrize('ConstructX', [ 33 'X()', 34 'new X()', 35]) 36def test_bind_multibinding_provider_success(ConstructX): 37 source = ''' 38 struct X : public ConstructionTracker<X> { 39 INJECT(X()) = default; 40 }; 41 42 fruit::Component<> getComponent() { 43 return fruit::createComponent() 44 .addMultibindingProvider([](){return ConstructX;}); 45 } 46 47 int main() { 48 fruit::Injector<> injector(getComponent); 49 50 Assert(X::num_objects_constructed == 0); 51 Assert(injector.getMultibindings<X>().size() == 1); 52 Assert(X::num_objects_constructed == 1); 53 } 54 ''' 55 expect_success( 56 COMMON_DEFINITIONS, 57 source, 58 locals()) 59 60@pytest.mark.parametrize('WithAnnot', [ 61 'WithNoAnnot', 62 'WithAnnot1', 63]) 64def test_bind_multibinding_provider_abstract_class_success(WithAnnot): 65 source = ''' 66 struct I { 67 virtual int foo() = 0; 68 virtual ~I() = default; 69 }; 70 71 struct X : public I { 72 int foo() override { 73 return 5; 74 } 75 }; 76 77 fruit::Component<> getComponent() { 78 return fruit::createComponent() 79 .addMultibindingProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());}); 80 } 81 82 int main() { 83 fruit::Injector<> injector(getComponent); 84 85 Assert(injector.getMultibindings<WithAnnot<I>>().size() == 1); 86 Assert(injector.getMultibindings<WithAnnot<I>>()[0]->foo() == 5); 87 } 88 ''' 89 expect_success( 90 COMMON_DEFINITIONS, 91 source, 92 locals()) 93 94@pytest.mark.parametrize('WithAnnot', [ 95 'WithNoAnnot', 96 'WithAnnot1', 97]) 98def test_bind_multibinding_provider_abstract_class_with_no_virtual_destructor_error(WithAnnot): 99 source = ''' 100 struct I { 101 virtual int foo() = 0; 102 }; 103 104 struct X : public I { 105 int foo() override { 106 return 5; 107 } 108 }; 109 110 fruit::Component<> getComponent() { 111 return fruit::createComponent() 112 .addMultibindingProvider<WithAnnot<I*>()>([](){return static_cast<I*>(new X());}); 113 } 114 ''' 115 expect_compile_error( 116 'MultibindingProviderReturningPointerToAbstractClassWithNoVirtualDestructorError<I>', 117 'registerMultibindingProvider\(\) was called with a lambda that returns a pointer to T, but T is an abstract class with no virtual destructor', 118 COMMON_DEFINITIONS, 119 source, 120 locals()) 121 122@pytest.mark.parametrize('ConstructX,XPtr', [ 123 ('X()', 'X'), 124 ('new X()', 'X*'), 125]) 126@pytest.mark.parametrize('WithAnnot', [ 127 'WithNoAnnot', 128 'WithAnnot1', 129]) 130@pytest.mark.parametrize('YVariant', [ 131 'Y', 132 'const Y', 133 'Y*', 134 'const Y*', 135 'Y&', 136 'const Y&', 137 'std::shared_ptr<Y>', 138 'fruit::Provider<Y>', 139 'fruit::Provider<const Y>', 140]) 141def test_bind_multibinding_provider_with_param_success(ConstructX, XPtr, WithAnnot, YVariant): 142 source = ''' 143 struct Y {}; 144 145 struct X : public ConstructionTracker<X> {}; 146 147 fruit::Component<WithAnnot<Y>> getYComponent() { 148 return fruit::createComponent() 149 .registerConstructor<WithAnnot<Y>()>(); 150 } 151 152 fruit::Component<> getComponent() { 153 return fruit::createComponent() 154 .install(getYComponent) 155 .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; }); 156 } 157 158 int main() { 159 fruit::Injector<> injector(getComponent); 160 161 Assert(X::num_objects_constructed == 0); 162 Assert(injector.getMultibindings<X>().size() == 1); 163 Assert(X::num_objects_constructed == 1); 164 } 165 ''' 166 expect_success( 167 COMMON_DEFINITIONS, 168 source, 169 locals()) 170 171@pytest.mark.parametrize('ConstructX,XPtr', [ 172 ('X()', 'X'), 173 ('new X()', 'X*'), 174]) 175@pytest.mark.parametrize('WithAnnot', [ 176 'WithNoAnnot', 177 'WithAnnot1', 178]) 179@pytest.mark.parametrize('YVariant', [ 180 'Y', 181 'const Y', 182 'const Y*', 183 'const Y&', 184 'fruit::Provider<const Y>', 185]) 186def test_bind_multibinding_provider_with_param_const_binding_success(ConstructX, XPtr, WithAnnot, YVariant): 187 source = ''' 188 struct Y {}; 189 190 struct X : public ConstructionTracker<X> {}; 191 192 const Y y{}; 193 194 fruit::Component<WithAnnot<const Y>> getYComponent() { 195 return fruit::createComponent() 196 .bindInstance<WithAnnot<Y>, Y>(y); 197 } 198 199 fruit::Component<> getComponent() { 200 return fruit::createComponent() 201 .install(getYComponent) 202 .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; }); 203 } 204 205 int main() { 206 fruit::Injector<> injector(getComponent); 207 208 Assert(X::num_objects_constructed == 0); 209 Assert(injector.getMultibindings<X>().size() == 1); 210 Assert(X::num_objects_constructed == 1); 211 } 212 ''' 213 expect_success( 214 COMMON_DEFINITIONS, 215 source, 216 locals()) 217 218@pytest.mark.parametrize('ConstructX,XPtr', [ 219 ('X()', 'X'), 220 ('new X()', 'X*'), 221]) 222@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [ 223 ('WithNoAnnot', 'Y'), 224 ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'), 225]) 226@pytest.mark.parametrize('YVariant', [ 227 'Y*', 228 'Y&', 229 'std::shared_ptr<Y>', 230 'fruit::Provider<Y>', 231]) 232def test_bind_multibinding_provider_with_param_error_nonconst_param_required(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant): 233 source = ''' 234 struct Y {}; 235 struct X {}; 236 237 fruit::Component<WithAnnot<const Y>> getYComponent(); 238 239 fruit::Component<> getComponent() { 240 return fruit::createComponent() 241 .install(getYComponent) 242 .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; }); 243 } 244 ''' 245 expect_compile_error( 246 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>', 247 'The type T was provided as constant, however one of the constructors/providers/factories in this component', 248 COMMON_DEFINITIONS, 249 source, 250 locals()) 251 252@pytest.mark.parametrize('ConstructX,XPtr', [ 253 ('X()', 'X'), 254 ('new X()', 'X*'), 255]) 256@pytest.mark.parametrize('WithAnnot,YAnnotRegex', [ 257 ('WithNoAnnot', 'Y'), 258 ('WithAnnot1', 'fruit::Annotated<Annotation1, Y>'), 259]) 260@pytest.mark.parametrize('YVariant', [ 261 'Y*', 262 'Y&', 263 'std::shared_ptr<Y>', 264 'fruit::Provider<Y>', 265]) 266def test_bind_multibinding_provider_with_param_error_nonconst_param_required_install_after(ConstructX, XPtr, WithAnnot, YAnnotRegex, YVariant): 267 source = ''' 268 struct Y {}; 269 struct X {}; 270 271 fruit::Component<WithAnnot<const Y>> getYComponent(); 272 273 fruit::Component<> getComponent() { 274 return fruit::createComponent() 275 .addMultibindingProvider<XPtr(WithAnnot<YVariant>)>([](YVariant){ return ConstructX; }) 276 .install(getYComponent); 277 } 278 ''' 279 expect_compile_error( 280 'NonConstBindingRequiredButConstBindingProvidedError<YAnnotRegex>', 281 'The type T was provided as constant, however one of the constructors/providers/factories in this component', 282 COMMON_DEFINITIONS, 283 source, 284 locals()) 285 286def test_bind_multibinding_provider_requiring_nonconst_then_requiring_const_ok(): 287 source = ''' 288 struct X {}; 289 struct Y {}; 290 291 fruit::Component<> getRootComponent() { 292 return fruit::createComponent() 293 .addMultibindingProvider([](X&) { return Y(); }) 294 .addMultibindingProvider([](const X&) { return Y(); }) 295 .registerConstructor<X()>(); 296 } 297 298 int main() { 299 fruit::Injector<> injector(getRootComponent); 300 injector.getMultibindings<Y>(); 301 } 302 ''' 303 expect_success( 304 COMMON_DEFINITIONS, 305 source, 306 locals()) 307 308def test_bind_multibinding_provider_requiring_nonconst_then_requiring_const_declaring_const_requirement_error(): 309 source = ''' 310 struct X {}; 311 struct Y {}; 312 313 fruit::Component<fruit::Required<const X>> getRootComponent() { 314 return fruit::createComponent() 315 .addMultibindingProvider([](X&) { return Y(); }) 316 .addMultibindingProvider([](const X&) { return Y(); }); 317 } 318 ''' 319 expect_compile_error( 320 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>', 321 'The type T was declared as a const Required type in the returned Component, however', 322 COMMON_DEFINITIONS, 323 source, 324 locals()) 325 326def test_bind_multibinding_provider_requiring_const_then_requiring_nonconst_ok(): 327 source = ''' 328 struct X {}; 329 struct Y {}; 330 331 fruit::Component<> getRootComponent() { 332 return fruit::createComponent() 333 .addMultibindingProvider([](const X&) { return Y(); }) 334 .addMultibindingProvider([](X&) { return Y(); }) 335 .registerConstructor<X()>(); 336 } 337 338 int main() { 339 fruit::Injector<> injector(getRootComponent); 340 injector.getMultibindings<Y>(); 341 } 342 ''' 343 expect_success( 344 COMMON_DEFINITIONS, 345 source, 346 locals()) 347 348def test_bind_multibinding_provider_requiring_const_then_requiring_nonconst_declaring_const_requirement_error(): 349 source = ''' 350 struct X {}; 351 struct Y {}; 352 353 fruit::Component<fruit::Required<const X>> getRootComponent() { 354 return fruit::createComponent() 355 .addMultibindingProvider([](const X&) { return Y(); }) 356 .addMultibindingProvider([](X&) { return Y(); }); 357 } 358 ''' 359 expect_compile_error( 360 'ConstBindingDeclaredAsRequiredButNonConstBindingRequiredError<X>', 361 'The type T was declared as a const Required type in the returned Component, however', 362 COMMON_DEFINITIONS, 363 source, 364 locals()) 365 366@pytest.mark.parametrize('ConstructX,XPtr', [ 367 ('X()', 'X'), 368 ('new X()', 'X*'), 369]) 370@pytest.mark.parametrize('YAnnot,ConstYAnnot,YVariant,YVariantRegex', [ 371 ('Y', 'Y', 'Y**', 'Y\*\*'), 372 ('Y', 'Y', 'std::shared_ptr<Y>*', 'std::shared_ptr<Y>\*'), 373 ('Y', 'const Y', 'Y**', 'Y\*\*'), 374 ('Y', 'const Y', 'std::shared_ptr<Y>*', 'std::shared_ptr<Y>\*'), 375 ('fruit::Annotated<Annotation1, Y>', 'fruit::Annotated<Annotation1, Y>', 'Y**', 'Y\*\*'), 376 ('fruit::Annotated<Annotation1, Y>', 'fruit::Annotated<Annotation1, const Y>', 'Y**', 'Y\*\*'), 377]) 378def test_bind_multibinding_provider_with_param_error_type_not_injectable(ConstructX, XPtr, YAnnot, ConstYAnnot, YVariant, YVariantRegex): 379 source = ''' 380 struct Y {}; 381 struct X {}; 382 383 fruit::Component<> getComponent() { 384 return fruit::createComponent() 385 .addMultibindingProvider<XPtr(YVariant)>([](YVariant){ return ConstructX; }); 386 } 387 ''' 388 expect_compile_error( 389 'NonInjectableTypeError<YVariantRegex>', 390 'The type T is not injectable.', 391 COMMON_DEFINITIONS, 392 source, 393 locals()) 394 395@pytest.mark.parametrize('ConstructX,XAnnot,XPtrAnnot', [ 396 ('X()', 'X', 'X'), 397 ('X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'), 398 ('new X()', 'X', 'X*'), 399 ('new X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'), 400]) 401def test_bind_multibinding_provider_explicit_signature_success(ConstructX, XAnnot, XPtrAnnot): 402 source = ''' 403 struct X : public ConstructionTracker<X> { 404 INJECT(X()) = default; 405 406 static bool constructed; 407 }; 408 409 fruit::Component<> getComponent() { 410 return fruit::createComponent() 411 .addMultibindingProvider<XPtrAnnot()>([](){return ConstructX;}); 412 } 413 414 int main() { 415 fruit::Injector<> injector(getComponent); 416 417 Assert(X::num_objects_constructed == 0); 418 Assert(injector.getMultibindings<XAnnot>().size() == 1); 419 Assert(X::num_objects_constructed == 1); 420 } 421 ''' 422 expect_success( 423 COMMON_DEFINITIONS, 424 source, 425 locals()) 426 427@pytest.mark.parametrize('ConstructX,XAnnot,XPtrAnnot', [ 428 ('X()', 'X', 'X'), 429 ('X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>'), 430 ('new X()', 'X', 'X*'), 431 ('new X()', 'fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>'), 432]) 433def test_bind_multibinding_provider_explicit_signature_with_normalized_component_success(ConstructX, XAnnot, XPtrAnnot): 434 source = ''' 435 struct X : public ConstructionTracker<X> { 436 INJECT(X()) = default; 437 438 static bool constructed; 439 }; 440 441 fruit::Component<> getComponent() { 442 return fruit::createComponent() 443 .addMultibindingProvider<XPtrAnnot()>([](){return ConstructX;}); 444 } 445 446 fruit::Component<> getEmptyComponent() { 447 return fruit::createComponent(); 448 } 449 450 int main() { 451 fruit::NormalizedComponent<> normalizedComponent(getComponent); 452 fruit::Injector<> injector(normalizedComponent, getEmptyComponent); 453 454 Assert(X::num_objects_constructed == 0); 455 Assert(injector.getMultibindings<XAnnot>().size() == 1); 456 Assert(X::num_objects_constructed == 1); 457 } 458 ''' 459 expect_success( 460 COMMON_DEFINITIONS, 461 source, 462 locals()) 463 464@pytest.mark.parametrize('XAnnot,XPtrAnnot,intAnnot', [ 465 ('X', 'X*', 'int'), 466 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', 'fruit::Annotated<Annotation2, int>'), 467]) 468def test_multiple_providers(XAnnot, XPtrAnnot, intAnnot): 469 source = ''' 470 struct X {}; 471 472 fruit::Component<> getComponent() { 473 return fruit::createComponent() 474 .registerProvider<intAnnot()>([](){return 42;}) 475 .addMultibindingProvider<XAnnot(intAnnot)>([](int){return X();}) 476 .addMultibindingProvider<XPtrAnnot(intAnnot)>([](int){return new X();}); 477 } 478 479 int main() { 480 fruit::Injector<> injector(getComponent); 481 482 std::vector<X*> multibindings = injector.getMultibindings<XAnnot>(); 483 Assert(multibindings.size() == 2); 484 } 485 ''' 486 expect_success( 487 COMMON_DEFINITIONS, 488 source, 489 locals()) 490 491@pytest.mark.parametrize('ConstructX', [ 492 'X()', 493 'new X()', 494]) 495@pytest.mark.parametrize('XAnnot', [ 496 'X', 497 'fruit::Annotated<Annotation1, X>', 498]) 499def test_bind_multibinding_provider_malformed_signature(ConstructX, XAnnot): 500 source = ''' 501 struct X {}; 502 503 fruit::Component<> getComponent() { 504 return fruit::createComponent() 505 .addMultibindingProvider<XAnnot>([](){return ConstructX;}); 506 } 507 ''' 508 expect_compile_error( 509 'NotASignatureError<XAnnot>', 510 'CandidateSignature was specified as parameter, but it.s not a signature.', 511 COMMON_DEFINITIONS, 512 source, 513 locals()) 514 515@pytest.mark.parametrize('ConstructX', [ 516 'X(n)', 517 'new X(n)', 518]) 519@pytest.mark.parametrize('XAnnot', [ 520 'X', 521 'fruit::Annotated<Annotation1, X>', 522]) 523def test_bind_multibinding_provider_lambda_with_captures_error(ConstructX, XAnnot): 524 source = ''' 525 struct X { 526 X(int) {} 527 }; 528 529 fruit::Component<> getComponent() { 530 int n = 3; 531 return fruit::createComponent() 532 .addMultibindingProvider<XAnnot()>([=]{return ConstructX;}); 533 } 534 ''' 535 expect_compile_error( 536 'FunctorUsedAsProviderError<.*>', 537 'A stateful lambda or a non-lambda functor was used as provider', 538 COMMON_DEFINITIONS, 539 source, 540 locals()) 541 542# TODO: should XPtrAnnot be just XAnnot in the signature? 543# Make sure the behavior here is consistent with registerProvider() and registerFactory(). 544@pytest.mark.parametrize('XAnnot,XPtrAnnot,XAnnotRegex', [ 545 ('X', 'X*', '(struct )?X'), 546 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X*>', '(struct )?fruit::Annotated<(struct )?Annotation1, ?(struct )?X>'), 547]) 548def test_provider_returns_nullptr_error(XAnnot, XPtrAnnot, XAnnotRegex): 549 source = ''' 550 struct X {}; 551 552 fruit::Component<> getComponent() { 553 return fruit::createComponent() 554 .addMultibindingProvider<XPtrAnnot()>([](){return (X*)nullptr;}); 555 } 556 557 int main() { 558 fruit::Injector<> injector(getComponent); 559 injector.getMultibindings<XAnnot>(); 560 } 561 ''' 562 expect_runtime_error( 563 'Fatal injection error: attempting to get an instance for the type XAnnotRegex but the provider returned nullptr', 564 COMMON_DEFINITIONS, 565 source, 566 locals()) 567 568if __name__== '__main__': 569 main(__file__) 570