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