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 25def escape_regex(regex): 26 # We un-escape the space because we strip the spaces in fruit_test_common, and this would otherwise leave a 27 # stray backslash. 28 return re.escape(regex).replace('\\ ', ' ') 29 30def test_bind_instance_success(): 31 source = ''' 32 struct X { 33 int n; 34 35 X(int n) 36 : n(n) { 37 } 38 }; 39 40 fruit::Component<X> getComponent(X* x) { 41 return fruit::createComponent() 42 .bindInstance(*x); 43 } 44 45 int main() { 46 X x(34); 47 fruit::Injector<X> injector(getComponent, &x); 48 X& x1 = injector.get<X&>(); 49 Assert(&x == &x1); 50 } 51 ''' 52 expect_success(COMMON_DEFINITIONS, source) 53 54def test_bind_instance_annotated_success(): 55 source = ''' 56 struct X { 57 int n; 58 59 X(int n) 60 : n(n) { 61 } 62 }; 63 64 fruit::Component<fruit::Annotated<Annotation1, X>> getComponent(X* x) { 65 return fruit::createComponent() 66 .bindInstance<fruit::Annotated<Annotation1, X>>(*x); 67 } 68 69 int main() { 70 X x(34); 71 fruit::Injector<fruit::Annotated<Annotation1, X>> injector(getComponent, &x); 72 X& x1 = injector.get<fruit::Annotated<Annotation1, X&>>(); 73 Assert(&x == &x1); 74 } 75 ''' 76 expect_success(COMMON_DEFINITIONS, source) 77 78def test_bind_const_instance_success(): 79 source = ''' 80 struct X { 81 int n; 82 83 X(int n) 84 : n(n) { 85 } 86 }; 87 88 fruit::Component<const X> getComponent(const X* x) { 89 return fruit::createComponent() 90 .bindInstance(*x); 91 } 92 93 const X x(34); 94 95 int main() { 96 fruit::Injector<const X> injector(getComponent, &x); 97 const X& x1 = injector.get<const X&>(); 98 Assert(&x == &x1); 99 } 100 ''' 101 expect_success(COMMON_DEFINITIONS, source) 102 103def test_bind_const_instance_annotated_success(): 104 source = ''' 105 struct X { 106 int n; 107 108 X(int n) 109 : n(n) { 110 } 111 }; 112 113 fruit::Component<fruit::Annotated<Annotation1, const X>> getComponent(const X* x) { 114 return fruit::createComponent() 115 .bindInstance<fruit::Annotated<Annotation1, X>>(*x); 116 } 117 118 const X x(34); 119 120 int main() { 121 fruit::Injector<fruit::Annotated<Annotation1, const X>> injector(getComponent, &x); 122 const X& x1 = injector.get<fruit::Annotated<Annotation1, const X&>>(); 123 Assert(&x == &x1); 124 } 125 ''' 126 expect_success(COMMON_DEFINITIONS, source) 127 128@pytest.mark.parametrize('XAnnot,MaybeConstXAnnot,XPtr,XPtrAnnot', [ 129 ('X', 'X', 'X*', 'X*'), 130 ('X', 'const X', 'const X*', 'const X*'), 131 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, X>', 'X*', 'fruit::Annotated<Annotation1, X*>'), 132 ('fruit::Annotated<Annotation1, X>', 'fruit::Annotated<Annotation1, const X>', 'const X*', 'fruit::Annotated<Annotation1, const X*>'), 133]) 134def test_bind_instance_two_explicit_type_arguments_success(XAnnot, MaybeConstXAnnot, XPtr, XPtrAnnot): 135 source = ''' 136 struct X { 137 int n; 138 139 X(int n) 140 : n(n) { 141 } 142 }; 143 144 fruit::Component<MaybeConstXAnnot> getComponent(XPtr x) { 145 return fruit::createComponent() 146 .bindInstance<XAnnot, X>(*x); 147 } 148 149 int main() { 150 X x(34); 151 fruit::Injector<MaybeConstXAnnot> injector(getComponent, &x); 152 XPtr x1 = injector.get<XPtrAnnot>(); 153 Assert(&x == x1); 154 } 155 ''' 156 expect_success(COMMON_DEFINITIONS, source, locals()) 157 158@pytest.mark.parametrize('XAnnot', [ 159 'X', 160 'fruit::Annotated<Annotation1, X>', 161]) 162def test_bind_instance_abstract_class_ok(XAnnot): 163 source = ''' 164 struct X { 165 virtual void foo() = 0; 166 }; 167 168 fruit::Component<> getComponentForInstanceHelper(X* x) { 169 return fruit::createComponent() 170 .bindInstance<XAnnot, X>(*x); 171 } 172 173 fruit::Component<XAnnot> getComponentForInstance(X* x) { 174 return fruit::createComponent() 175 .install(getComponentForInstanceHelper, x) 176 .bindInstance<XAnnot, X>(*x); 177 } 178 ''' 179 expect_success( 180 COMMON_DEFINITIONS, 181 source, 182 locals()) 183 184@pytest.mark.parametrize('intAnnot,intPtrAnnot', [ 185 ('int', 'int*'), 186 ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'), 187]) 188def test_bind_instance_multiple_equivalent_bindings_success(intAnnot, intPtrAnnot): 189 source = ''' 190 fruit::Component<> getComponentForInstanceHelper(int* n) { 191 return fruit::createComponent() 192 .bindInstance<intAnnot, int>(*n); 193 } 194 195 fruit::Component<intAnnot> getComponentForInstance(int* n) { 196 return fruit::createComponent() 197 .install(getComponentForInstanceHelper, n) 198 .bindInstance<intAnnot, int>(*n); 199 } 200 201 int main() { 202 int n = 5; 203 fruit::Injector<intAnnot> injector(getComponentForInstance, &n); 204 if (injector.get<intPtrAnnot>() != &n) 205 abort(); 206 } 207 ''' 208 expect_success( 209 COMMON_DEFINITIONS, 210 source, 211 locals()) 212 213@pytest.mark.parametrize('intAnnot,intPtrAnnot', [ 214 ('int', 'int*'), 215 ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'), 216]) 217def test_bind_instance_multiple_equivalent_bindings_different_constness_success(intAnnot, intPtrAnnot): 218 source = ''' 219 fruit::Component<> getComponentForInstanceHelper(const int* n) { 220 return fruit::createComponent() 221 .bindInstance<intAnnot, int>(*n); 222 } 223 224 fruit::Component<intAnnot> getComponentForInstance(int* n) { 225 return fruit::createComponent() 226 .install(getComponentForInstanceHelper, n) 227 .bindInstance<intAnnot, int>(*n); 228 } 229 230 int main() { 231 int n = 5; 232 fruit::Injector<intAnnot> injector(getComponentForInstance, &n); 233 if (injector.get<intPtrAnnot>() != &n) 234 abort(); 235 } 236 ''' 237 expect_success( 238 COMMON_DEFINITIONS, 239 source, 240 locals()) 241 242@pytest.mark.parametrize('intAnnot,intPtrAnnot', [ 243 ('int', 'int*'), 244 ('fruit::Annotated<Annotation1, int>', 'fruit::Annotated<Annotation1, int*>'), 245]) 246def test_bind_instance_multiple_equivalent_bindings_different_constness_other_order_success(intAnnot, intPtrAnnot): 247 source = ''' 248 fruit::Component<> getComponentForInstanceHelper(const int* n) { 249 return fruit::createComponent() 250 .bindInstance<intAnnot, int>(*n); 251 } 252 253 fruit::Component<intAnnot> getComponentForInstance(int* n) { 254 return fruit::createComponent() 255 .bindInstance<intAnnot, int>(*n) 256 .install(getComponentForInstanceHelper, n); 257 } 258 259 int main() { 260 int n = 5; 261 fruit::Injector<intAnnot> injector(getComponentForInstance, &n); 262 if (injector.get<intPtrAnnot>() != &n) 263 abort(); 264 } 265 ''' 266 expect_success( 267 COMMON_DEFINITIONS, 268 source, 269 locals()) 270 271@pytest.mark.parametrize('XVariant', [ 272 'X*', 273 'const X*', 274 'std::shared_ptr<X>', 275]) 276def test_bind_instance_non_normalized_type_error(XVariant): 277 if XVariant.endswith('&'): 278 XVariantRegexp = escape_regex(XVariant[:-1]) 279 else: 280 XVariantRegexp = escape_regex(XVariant) 281 source = ''' 282 struct X {}; 283 284 fruit::Component<> getComponent(XVariant x) { 285 return fruit::createComponent() 286 .bindInstance(x); 287 } 288 ''' 289 expect_compile_error( 290 'NonClassTypeError<XVariantRegexp,X>', 291 'A non-class type T was specified. Use C instead.', 292 COMMON_DEFINITIONS, 293 source, 294 locals()) 295 296@pytest.mark.parametrize('XVariant,XVariantRegexp', [ 297 ('const X', 'const X'), 298 ('X*', 'X\*'), 299 ('const X*', 'const X\*'), 300 ('X&', 'X&'), 301 ('const X&', 'const X&'), 302 ('std::shared_ptr<X>', 'std::shared_ptr<X>'), 303]) 304def test_bind_instance_non_normalized_type_error_with_annotation(XVariant, XVariantRegexp): 305 source = ''' 306 struct X {}; 307 308 fruit::Component<> getComponent(XVariant x) { 309 return fruit::createComponent() 310 .bindInstance<fruit::Annotated<Annotation1, XVariant>>(x); 311 } 312 ''' 313 expect_compile_error( 314 'NonClassTypeError<XVariantRegexp,X>', 315 'A non-class type T was specified. Use C instead.', 316 COMMON_DEFINITIONS, 317 source, 318 locals()) 319 320@pytest.mark.parametrize('XAnnotVariant,XVariant', [ 321 ('const X', 'const X'), 322 ('X*', 'X*'), 323 ('const X*', 'const X*'), 324 ('X&', 'X&'), 325 ('const X&', 'const X&'), 326 ('std::shared_ptr<X>', 'std::shared_ptr<X>'), 327 328 ('fruit::Annotated<Annotation1, const X>', 'const X'), 329 ('fruit::Annotated<Annotation1, X*>', 'X*'), 330 ('fruit::Annotated<Annotation1, const X*>', 'const X*'), 331 ('fruit::Annotated<Annotation1, X&>', 'X&'), 332 ('fruit::Annotated<Annotation1, const X&>', 'const X&'), 333 ('fruit::Annotated<Annotation1, std::shared_ptr<X>>', 'std::shared_ptr<X>'), 334 335 ('fruit::Annotated<Annotation1, X>', 'const X'), 336 ('fruit::Annotated<Annotation1, X>', 'X*'), 337 ('fruit::Annotated<Annotation1, X>', 'const X*'), 338 ('fruit::Annotated<Annotation1, X>', 'X&'), 339 ('fruit::Annotated<Annotation1, X>', 'const X&'), 340 ('fruit::Annotated<Annotation1, X>', 'std::shared_ptr<X>'), 341]) 342def test_bind_instance_non_normalized_type_error_two_explicit_type_arguments(XAnnotVariant, XVariant): 343 XVariantRegexp = escape_regex(XVariant) 344 source = ''' 345 struct X {}; 346 347 fruit::Component<> getComponent(XVariant x) { 348 return fruit::createComponent() 349 .bindInstance<XAnnotVariant, XVariant>(x); 350 } 351 ''' 352 expect_compile_error( 353 'NonClassTypeError<XVariantRegexp,X>', 354 'A non-class type T was specified. Use C instead.', 355 COMMON_DEFINITIONS, 356 source, 357 locals()) 358 359@pytest.mark.parametrize('XVariant,XVariantRegex', [ 360 ('X*', 'X\*'), 361 ('const X*', 'const X\*'), 362 ('std::shared_ptr<X>', 'std::shared_ptr<X>'), 363]) 364def test_register_instance_error_must_be_reference(XVariant, XVariantRegex): 365 source = ''' 366 struct X {}; 367 368 fruit::Component<> getComponentForInstance(XVariant x) { 369 return fruit::createComponent() 370 .bindInstance(x); 371 } 372 ''' 373 expect_compile_error( 374 'NonClassTypeError<XVariantRegex,X>', 375 'A non-class type T was specified. Use C instead.', 376 COMMON_DEFINITIONS, 377 source, 378 locals()) 379 380@pytest.mark.parametrize('XVariant,XVariantRegex', [ 381 ('X*', 'X\*'), 382 ('const X*', 'const X\*'), 383 ('std::shared_ptr<X>', 'std::shared_ptr<X>'), 384]) 385def test_register_instance_error_must_be_reference_with_annotation(XVariant, XVariantRegex): 386 source = ''' 387 struct X {}; 388 389 fruit::Component<> getComponentForInstance(XVariant x) { 390 return fruit::createComponent() 391 .bindInstance<fruit::Annotated<Annotation1, X>>(x); 392 } 393 ''' 394 expect_compile_error( 395 'NonClassTypeError<XVariantRegex,X>', 396 'A non-class type T was specified. Use C instead.', 397 COMMON_DEFINITIONS, 398 source, 399 locals()) 400 401@pytest.mark.parametrize('XAnnot', [ 402 'X', 403 'fruit::Annotated<Annotation1, X>', 404]) 405def test_bind_instance_mismatched_type_arguments(XAnnot): 406 source = ''' 407 struct X {}; 408 409 fruit::Component<> getComponent(int* n) { 410 return fruit::createComponent() 411 .bindInstance<XAnnot, int>(*n); 412 } 413 ''' 414 expect_compile_error( 415 'TypeMismatchInBindInstanceError<X,int>', 416 'A type parameter was specified in bindInstance.. but it doesn.t match the value type', 417 COMMON_DEFINITIONS, 418 source, 419 locals()) 420 421@pytest.mark.parametrize('BaseAnnot,BasePtrAnnot', [ 422 ('Base', 'Base*'), 423 ('fruit::Annotated<Annotation1, Base>', 'fruit::Annotated<Annotation1, Base*>'), 424]) 425def test_bind_instance_to_subclass(BaseAnnot, BasePtrAnnot): 426 source = ''' 427 struct Base { 428 virtual void f() = 0; 429 virtual ~Base() { 430 } 431 }; 432 433 struct Derived : public Base { 434 void f() override { 435 } 436 }; 437 438 fruit::Component<BaseAnnot> getComponent(Derived* derived) { 439 return fruit::createComponent() 440 .bindInstance<BaseAnnot, Base>(*derived); 441 } 442 443 int main() { 444 Derived derived; 445 fruit::Injector<BaseAnnot> injector(getComponent, &derived); 446 Base* base = injector.get<BasePtrAnnot>(); 447 base->f(); 448 } 449 ''' 450 expect_success(COMMON_DEFINITIONS, source, locals()) 451 452@pytest.mark.parametrize('XVariant,XVariantRegex', [ 453 ('X**', r'X\*\*'), 454 ('std::shared_ptr<X>*', r'std::shared_ptr<X>\*'), 455 ('X*&', r'X\*&'), 456 ('fruit::Annotated<Annotation1, X**>', r'X\*\*'), 457]) 458def test_bind_instance_type_not_normalized(XVariant, XVariantRegex): 459 source = ''' 460 struct X {}; 461 462 using XVariantT = XVariant; 463 fruit::Component<> getComponent(XVariantT x) { 464 return fruit::createComponent() 465 .bindInstance<XVariant, XVariant>(x); 466 } 467 ''' 468 expect_compile_error( 469 'NonClassTypeError<XVariantRegex,X>', 470 'A non-class type T was specified.', 471 COMMON_DEFINITIONS, 472 source, 473 locals()) 474 475@pytest.mark.parametrize('XVariant,XVariantRegex', [ 476 ('X(*)()', r'X(\((__cdecl)?\*\))?\((void)?\)'), 477]) 478def test_bind_instance_type_not_injectable_error(XVariant, XVariantRegex): 479 source = ''' 480 struct X {}; 481 482 using XVariantT = XVariant; 483 fruit::Component<> getComponent(XVariantT x) { 484 return fruit::createComponent() 485 .bindInstance<XVariant, XVariant>(x); 486 } 487 ''' 488 expect_compile_error( 489 'NonInjectableTypeError<XVariantRegex>', 490 'The type T is not injectable.', 491 COMMON_DEFINITIONS, 492 source, 493 locals()) 494 495if __name__== '__main__': 496 main(__file__) 497