• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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