1 /* 2 * Copyright (C) 2015 Google Inc. 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 */ 16 17 package com.google.inject.spi; 18 19 import static com.google.inject.Asserts.assertContains; 20 import static com.google.inject.name.Names.named; 21 import static java.lang.annotation.ElementType.METHOD; 22 import static java.lang.annotation.RetentionPolicy.RUNTIME; 23 24 import com.google.common.collect.ImmutableSet; 25 import com.google.common.collect.Iterables; 26 import com.google.inject.AbstractModule; 27 import com.google.inject.Binder; 28 import com.google.inject.Binding; 29 import com.google.inject.CreationException; 30 import com.google.inject.Exposed; 31 import com.google.inject.Guice; 32 import com.google.inject.Injector; 33 import com.google.inject.Key; 34 import com.google.inject.Module; 35 import com.google.inject.PrivateModule; 36 import com.google.inject.internal.util.StackTraceElements; 37 import com.google.inject.name.Named; 38 import com.google.inject.name.Names; 39 import java.lang.annotation.Annotation; 40 import java.lang.annotation.Documented; 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.Target; 43 import java.util.Set; 44 import junit.framework.TestCase; 45 46 /** Tests for {@link ModuleAnnotatedMethodScanner} usage. */ 47 public class ModuleAnnotatedMethodScannerTest extends TestCase { 48 testScanning()49 public void testScanning() throws Exception { 50 Module module = 51 new AbstractModule() { 52 53 @TestProvides 54 @Named("foo") 55 String foo() { 56 return "foo"; 57 } 58 59 @TestProvides 60 @Named("foo2") 61 String foo2() { 62 return "foo2"; 63 } 64 }; 65 Injector injector = Guice.createInjector(module, NamedMunger.module()); 66 67 // assert no bindings named "foo" or "foo2" exist -- they were munged. 68 assertMungedBinding(injector, String.class, "foo", "foo"); 69 assertMungedBinding(injector, String.class, "foo2", "foo2"); 70 71 Binding<String> fooBinding = injector.getBinding(Key.get(String.class, named("foo-munged"))); 72 Binding<String> foo2Binding = injector.getBinding(Key.get(String.class, named("foo2-munged"))); 73 // Validate the provider has a sane toString 74 assertEquals( 75 methodName(TestProvides.class, "foo", module), fooBinding.getProvider().toString()); 76 assertEquals( 77 methodName(TestProvides.class, "foo2", module), foo2Binding.getProvider().toString()); 78 } 79 testSkipSources()80 public void testSkipSources() throws Exception { 81 Module module = 82 new AbstractModule() { 83 @Override 84 protected void configure() { 85 binder() 86 .skipSources(getClass()) 87 .install( 88 new AbstractModule() { 89 90 @TestProvides 91 @Named("foo") 92 String foo() { 93 return "foo"; 94 } 95 }); 96 } 97 }; 98 Injector injector = Guice.createInjector(module, NamedMunger.module()); 99 assertMungedBinding(injector, String.class, "foo", "foo"); 100 } 101 testWithSource()102 public void testWithSource() throws Exception { 103 Module module = 104 new AbstractModule() { 105 @Override 106 protected void configure() { 107 binder() 108 .withSource("source") 109 .install( 110 new AbstractModule() { 111 112 @TestProvides 113 @Named("foo") 114 String foo() { 115 return "foo"; 116 } 117 }); 118 } 119 }; 120 Injector injector = Guice.createInjector(module, NamedMunger.module()); 121 assertMungedBinding(injector, String.class, "foo", "foo"); 122 } 123 testMoreThanOneClaimedAnnotationFails()124 public void testMoreThanOneClaimedAnnotationFails() throws Exception { 125 Module module = 126 new AbstractModule() { 127 128 @TestProvides 129 @TestProvides2 130 String foo() { 131 return "foo"; 132 } 133 }; 134 try { 135 Guice.createInjector(module, NamedMunger.module()); 136 fail(); 137 } catch (CreationException expected) { 138 assertEquals(1, expected.getErrorMessages().size()); 139 assertContains( 140 expected.getMessage(), 141 "More than one annotation claimed by NamedMunger on method " 142 + module.getClass().getName() 143 + ".foo(). Methods can only have " 144 + "one annotation claimed per scanner."); 145 } 146 } 147 methodName(Class<? extends Annotation> annotation, String method, Object container)148 private String methodName(Class<? extends Annotation> annotation, String method, Object container) 149 throws Exception { 150 return "@" 151 + annotation.getName() 152 + " " 153 + StackTraceElements.forMember(container.getClass().getDeclaredMethod(method)); 154 } 155 156 @Documented 157 @Target(METHOD) 158 @Retention(RUNTIME) 159 private @interface TestProvides {} 160 161 @Documented 162 @Target(METHOD) 163 @Retention(RUNTIME) 164 private @interface TestProvides2 {} 165 166 private static class NamedMunger extends ModuleAnnotatedMethodScanner { module()167 static Module module() { 168 return new AbstractModule() { 169 @Override 170 protected void configure() { 171 binder().scanModulesForAnnotatedMethods(new NamedMunger()); 172 } 173 }; 174 } 175 176 @Override toString()177 public String toString() { 178 return "NamedMunger"; 179 } 180 181 @Override annotationClasses()182 public Set<? extends Class<? extends Annotation>> annotationClasses() { 183 return ImmutableSet.of(TestProvides.class, TestProvides2.class); 184 } 185 186 @Override prepareMethod( Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint)187 public <T> Key<T> prepareMethod( 188 Binder binder, Annotation annotation, Key<T> key, InjectionPoint injectionPoint) { 189 return Key.get( 190 key.getTypeLiteral(), Names.named(((Named) key.getAnnotation()).value() + "-munged")); 191 } 192 } 193 194 private void assertMungedBinding( 195 Injector injector, Class<?> clazz, String originalName, Object expectedValue) { 196 assertNull(injector.getExistingBinding(Key.get(clazz, named(originalName)))); 197 Binding<?> fooBinding = injector.getBinding(Key.get(clazz, named(originalName + "-munged"))); 198 assertEquals(expectedValue, fooBinding.getProvider().get()); 199 } 200 201 public void testFailingScanner() { 202 try { 203 Guice.createInjector(new SomeModule(), FailingScanner.module()); 204 fail(); 205 } catch (CreationException expected) { 206 Message m = Iterables.getOnlyElement(expected.getErrorMessages()); 207 assertEquals( 208 "An exception was caught and reported. Message: Failing in the scanner.", m.getMessage()); 209 assertEquals(IllegalStateException.class, m.getCause().getClass()); 210 ElementSource source = (ElementSource) Iterables.getOnlyElement(m.getSources()); 211 assertEquals( 212 SomeModule.class.getName(), Iterables.getOnlyElement(source.getModuleClassNames())); 213 assertEquals( 214 String.class.getName() + " " + SomeModule.class.getName() + ".aString()", 215 source.toString()); 216 } 217 } 218 219 public static class FailingScanner extends ModuleAnnotatedMethodScanner { 220 static Module module() { 221 return new AbstractModule() { 222 @Override 223 protected void configure() { 224 binder().scanModulesForAnnotatedMethods(new FailingScanner()); 225 } 226 }; 227 } 228 229 @Override 230 public Set<? extends Class<? extends Annotation>> annotationClasses() { 231 return ImmutableSet.of(TestProvides.class); 232 } 233 234 @Override 235 public <T> Key<T> prepareMethod( 236 Binder binder, Annotation rawAnnotation, Key<T> key, InjectionPoint injectionPoint) { 237 throw new IllegalStateException("Failing in the scanner."); 238 } 239 } 240 241 static class SomeModule extends AbstractModule { 242 @TestProvides 243 String aString() { 244 return "Foo"; 245 } 246 247 } 248 249 public void testChildInjectorInheritsScanner() { 250 Injector parent = Guice.createInjector(NamedMunger.module()); 251 Injector child = 252 parent.createChildInjector( 253 new AbstractModule() { 254 255 @TestProvides 256 @Named("foo") 257 String foo() { 258 return "foo"; 259 } 260 }); 261 assertMungedBinding(child, String.class, "foo", "foo"); 262 } 263 264 public void testChildInjectorScannersDontImpactSiblings() { 265 Module module = 266 new AbstractModule() { 267 268 @TestProvides 269 @Named("foo") 270 String foo() { 271 return "foo"; 272 } 273 }; 274 Injector parent = Guice.createInjector(); 275 Injector child = parent.createChildInjector(NamedMunger.module(), module); 276 assertMungedBinding(child, String.class, "foo", "foo"); 277 278 // no foo nor foo-munged in sibling, since scanner never saw it. 279 Injector sibling = parent.createChildInjector(module); 280 assertNull(sibling.getExistingBinding(Key.get(String.class, named("foo")))); 281 assertNull(sibling.getExistingBinding(Key.get(String.class, named("foo-munged")))); 282 } 283 284 public void testPrivateModuleInheritScanner_usingPrivateModule() { 285 Injector injector = 286 Guice.createInjector( 287 NamedMunger.module(), 288 new PrivateModule() { 289 @Override 290 protected void configure() {} 291 292 @Exposed 293 @TestProvides 294 @Named("foo") 295 String foo() { 296 return "foo"; 297 } 298 }); 299 assertMungedBinding(injector, String.class, "foo", "foo"); 300 } 301 302 public void testPrivateModule_skipSourcesWithinPrivateModule() { 303 Injector injector = 304 Guice.createInjector( 305 NamedMunger.module(), 306 new PrivateModule() { 307 @Override 308 protected void configure() { 309 binder() 310 .skipSources(getClass()) 311 .install( 312 new AbstractModule() { 313 314 @Exposed 315 @TestProvides 316 @Named("foo") 317 String foo() { 318 return "foo"; 319 } 320 }); 321 } 322 }); 323 assertMungedBinding(injector, String.class, "foo", "foo"); 324 } 325 326 public void testPrivateModule_skipSourcesForPrivateModule() { 327 Injector injector = 328 Guice.createInjector( 329 NamedMunger.module(), 330 new AbstractModule() { 331 @Override 332 protected void configure() { 333 binder() 334 .skipSources(getClass()) 335 .install( 336 new PrivateModule() { 337 @Override 338 protected void configure() {} 339 340 @Exposed 341 @TestProvides 342 @Named("foo") 343 String foo() { 344 return "foo"; 345 } 346 }); 347 } 348 }); 349 assertMungedBinding(injector, String.class, "foo", "foo"); 350 } 351 352 public void testPrivateModuleInheritScanner_usingPrivateBinder() { 353 Injector injector = 354 Guice.createInjector( 355 NamedMunger.module(), 356 new AbstractModule() { 357 @Override 358 protected void configure() { 359 binder() 360 .newPrivateBinder() 361 .install( 362 new AbstractModule() { 363 364 @Exposed 365 @TestProvides 366 @Named("foo") 367 String foo() { 368 return "foo"; 369 } 370 }); 371 } 372 }); 373 assertMungedBinding(injector, String.class, "foo", "foo"); 374 } 375 376 public void testPrivateModuleInheritScanner_skipSourcesFromPrivateBinder() { 377 Injector injector = 378 Guice.createInjector( 379 NamedMunger.module(), 380 new AbstractModule() { 381 @Override 382 protected void configure() { 383 binder() 384 .newPrivateBinder() 385 .skipSources(getClass()) 386 .install( 387 new AbstractModule() { 388 389 @Exposed 390 @TestProvides 391 @Named("foo") 392 String foo() { 393 return "foo"; 394 } 395 }); 396 } 397 }); 398 assertMungedBinding(injector, String.class, "foo", "foo"); 399 } 400 401 public void testPrivateModuleInheritScanner_skipSourcesFromPrivateBinder2() { 402 Injector injector = 403 Guice.createInjector( 404 NamedMunger.module(), 405 new AbstractModule() { 406 @Override 407 protected void configure() { 408 binder() 409 .skipSources(getClass()) 410 .newPrivateBinder() 411 .install( 412 new AbstractModule() { 413 414 @Exposed 415 @TestProvides 416 @Named("foo") 417 String foo() { 418 return "foo"; 419 } 420 }); 421 } 422 }); 423 assertMungedBinding(injector, String.class, "foo", "foo"); 424 } 425 426 public void testPrivateModuleScannersDontImpactSiblings_usingPrivateModule() { 427 Injector injector = 428 Guice.createInjector( 429 new PrivateModule() { 430 @Override 431 protected void configure() { 432 install(NamedMunger.module()); 433 } 434 435 @Exposed 436 @TestProvides 437 @Named("foo") 438 String foo() { 439 return "foo"; 440 } 441 }, 442 new PrivateModule() { 443 @Override 444 protected void configure() {} 445 446 // ignored! (because the scanner doesn't run over this module) 447 @Exposed 448 @TestProvides 449 @Named("foo") 450 String foo() { 451 return "foo"; 452 } 453 }); 454 assertMungedBinding(injector, String.class, "foo", "foo"); 455 } 456 457 public void testPrivateModuleScannersDontImpactSiblings_usingPrivateBinder() { 458 Injector injector = 459 Guice.createInjector( 460 new AbstractModule() { 461 @Override 462 protected void configure() { 463 binder() 464 .newPrivateBinder() 465 .install( 466 new AbstractModule() { 467 @Override 468 protected void configure() { 469 install(NamedMunger.module()); 470 } 471 472 @Exposed 473 @TestProvides 474 @Named("foo") 475 String foo() { 476 return "foo"; 477 } 478 }); 479 } 480 }, 481 new AbstractModule() { 482 @Override 483 protected void configure() { 484 binder() 485 .newPrivateBinder() 486 .install( 487 new AbstractModule() { 488 489 // ignored! (because the scanner doesn't run over this module) 490 @Exposed 491 @TestProvides 492 @Named("foo") 493 String foo() { 494 return "foo"; 495 } 496 }); 497 } 498 }); 499 assertMungedBinding(injector, String.class, "foo", "foo"); 500 } 501 502 public void testPrivateModuleWithinPrivateModule() { 503 Injector injector = 504 Guice.createInjector( 505 NamedMunger.module(), 506 new PrivateModule() { 507 @Override 508 protected void configure() { 509 expose(Key.get(String.class, named("foo-munged"))); 510 install( 511 new PrivateModule() { 512 @Override 513 protected void configure() {} 514 515 @Exposed 516 @TestProvides 517 @Named("foo") 518 String foo() { 519 return "foo"; 520 } 521 }); 522 } 523 }); 524 assertMungedBinding(injector, String.class, "foo", "foo"); 525 } 526 } 527