blob: 6c797b1acc3ead883f6101d7ba67d0aed4b39d50 [file] [log] [blame]
samebe18906a2015-02-02 14:07:53 -08001/**
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
17package com.google.inject.spi;
18
19import static com.google.inject.Asserts.assertContains;
20import static com.google.inject.name.Names.named;
21import static java.lang.annotation.ElementType.METHOD;
22import static java.lang.annotation.RetentionPolicy.RUNTIME;
23
24import com.google.common.collect.ImmutableSet;
sameb54da0e32015-02-19 18:36:52 -080025import com.google.common.collect.Iterables;
samebe18906a2015-02-02 14:07:53 -080026import com.google.inject.AbstractModule;
27import com.google.inject.Binder;
28import com.google.inject.Binding;
29import com.google.inject.CreationException;
sameb54da0e32015-02-19 18:36:52 -080030import com.google.inject.Exposed;
samebe18906a2015-02-02 14:07:53 -080031import com.google.inject.Guice;
32import com.google.inject.Injector;
33import com.google.inject.Key;
34import com.google.inject.Module;
sameb54da0e32015-02-19 18:36:52 -080035import com.google.inject.PrivateModule;
samebe18906a2015-02-02 14:07:53 -080036import com.google.inject.internal.util.StackTraceElements;
37import com.google.inject.name.Named;
38import com.google.inject.name.Names;
39
40import junit.framework.TestCase;
41
42import java.lang.annotation.Annotation;
43import java.lang.annotation.Documented;
44import java.lang.annotation.Retention;
45import java.lang.annotation.Target;
46import java.util.Set;
47
48/** Tests for {@link ModuleAnnotatedMethodScanner} usage. */
49public class ModuleAnnotatedMethodScannerTest extends TestCase {
sameb54da0e32015-02-19 18:36:52 -080050
samebe18906a2015-02-02 14:07:53 -080051 public void testScanning() throws Exception {
52 Module module = new AbstractModule() {
sameb54da0e32015-02-19 18:36:52 -080053 @Override protected void configure() {}
54
samebe18906a2015-02-02 14:07:53 -080055 @TestProvides @Named("foo") String foo() {
56 return "foo";
57 }
sameb54da0e32015-02-19 18:36:52 -080058
samebe18906a2015-02-02 14:07:53 -080059 @TestProvides @Named("foo2") String foo2() {
60 return "foo2";
61 }
62 };
sameb54da0e32015-02-19 18:36:52 -080063 Injector injector = Guice.createInjector(module, NamedMunger.module());
samebe18906a2015-02-02 14:07:53 -080064
65 // assert no bindings named "foo" or "foo2" exist -- they were munged.
sameb54da0e32015-02-19 18:36:52 -080066 assertMungedBinding(injector, String.class, "foo", "foo");
67 assertMungedBinding(injector, String.class, "foo2", "foo2");
samebe18906a2015-02-02 14:07:53 -080068
69 Binding<String> fooBinding = injector.getBinding(Key.get(String.class, named("foo-munged")));
70 Binding<String> foo2Binding = injector.getBinding(Key.get(String.class, named("foo2-munged")));
samebe18906a2015-02-02 14:07:53 -080071 // Validate the provider has a sane toString
72 assertEquals(methodName(TestProvides.class, "foo", module),
73 fooBinding.getProvider().toString());
74 assertEquals(methodName(TestProvides.class, "foo2", module),
75 foo2Binding.getProvider().toString());
76 }
77
sameb02c489e2015-03-20 12:29:45 -070078 public void testSkipSources() throws Exception {
79 Module module = new AbstractModule() {
80 @Override protected void configure() {
81 binder().skipSources(getClass()).install(new AbstractModule() {
82 @Override protected void configure() {}
83
84 @TestProvides @Named("foo") String foo() { return "foo"; }
85 });
86 }
87 };
88 Injector injector = Guice.createInjector(module, NamedMunger.module());
89 assertMungedBinding(injector, String.class, "foo", "foo");
90 }
91
92 public void testWithSource() throws Exception {
93 Module module = new AbstractModule() {
94 @Override protected void configure() {
95 binder().withSource("source").install(new AbstractModule() {
96 @Override protected void configure() {}
97
98 @TestProvides @Named("foo") String foo() { return "foo"; }
99 });
100 }
101 };
102 Injector injector = Guice.createInjector(module, NamedMunger.module());
103 assertMungedBinding(injector, String.class, "foo", "foo");
104 }
105
samebe18906a2015-02-02 14:07:53 -0800106 public void testMoreThanOneClaimedAnnotationFails() throws Exception {
samebe18906a2015-02-02 14:07:53 -0800107 Module module = new AbstractModule() {
sameb54da0e32015-02-19 18:36:52 -0800108 @Override protected void configure() {}
109
samebe18906a2015-02-02 14:07:53 -0800110 @TestProvides @TestProvides2 String foo() {
111 return "foo";
112 }
113 };
114 try {
sameb54da0e32015-02-19 18:36:52 -0800115 Guice.createInjector(module, NamedMunger.module());
samebe18906a2015-02-02 14:07:53 -0800116 fail();
117 } catch(CreationException expected) {
118 assertEquals(1, expected.getErrorMessages().size());
119 assertContains(expected.getMessage(),
sameb54da0e32015-02-19 18:36:52 -0800120 "More than one annotation claimed by NamedMunger on method "
samebe18906a2015-02-02 14:07:53 -0800121 + module.getClass().getName() + ".foo(). Methods can only have "
122 + "one annotation claimed per scanner.");
123 }
124 }
sameb54da0e32015-02-19 18:36:52 -0800125
samebe18906a2015-02-02 14:07:53 -0800126 private String methodName(Class<? extends Annotation> annotation, String method, Object container)
127 throws Exception {
128 return "@" + annotation.getName() + " "
129 + StackTraceElements.forMember(container.getClass().getDeclaredMethod(method));
130 }
sameb54da0e32015-02-19 18:36:52 -0800131
samebe18906a2015-02-02 14:07:53 -0800132 @Documented @Target(METHOD) @Retention(RUNTIME)
133 private @interface TestProvides {}
134
135 @Documented @Target(METHOD) @Retention(RUNTIME)
136 private @interface TestProvides2 {}
sameb54da0e32015-02-19 18:36:52 -0800137
samebe18906a2015-02-02 14:07:53 -0800138 private static class NamedMunger extends ModuleAnnotatedMethodScanner {
sameb54da0e32015-02-19 18:36:52 -0800139 static Module module() {
140 return new AbstractModule() {
141 @Override protected void configure() {
142 binder().scanModulesForAnnotatedMethods(new NamedMunger());
143 }
144 };
145 }
146
147 @Override
148 public String toString() {
149 return "NamedMunger";
150 }
151
samebe18906a2015-02-02 14:07:53 -0800152 @Override
153 public Set<? extends Class<? extends Annotation>> annotationClasses() {
154 return ImmutableSet.of(TestProvides.class, TestProvides2.class);
155 }
156
157 @Override
158 public <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key,
159 InjectionPoint injectionPoint) {
160 return Key.get(key.getTypeLiteral(),
161 Names.named(((Named) key.getAnnotation()).value() + "-munged"));
162 }
163 }
sameb7445e442015-02-10 15:37:44 -0800164
sameb54da0e32015-02-19 18:36:52 -0800165 private void assertMungedBinding(Injector injector, Class<?> clazz, String originalName,
166 Object expectedValue) {
167 assertNull(injector.getExistingBinding(Key.get(clazz, named(originalName))));
168 Binding<?> fooBinding = injector.getBinding(Key.get(clazz, named(originalName + "-munged")));
169 assertEquals(expectedValue, fooBinding.getProvider().get());
170 }
171
172 public void testFailingScanner() {
sameb7445e442015-02-10 15:37:44 -0800173 try {
sameb54da0e32015-02-19 18:36:52 -0800174 Guice.createInjector(new SomeModule(), FailingScanner.module());
sameb7445e442015-02-10 15:37:44 -0800175 fail();
176 } catch (CreationException expected) {
177 Message m = Iterables.getOnlyElement(expected.getErrorMessages());
178 assertEquals(
179 "An exception was caught and reported. Message: Failing in the scanner.",
180 m.getMessage());
181 assertEquals(IllegalStateException.class, m.getCause().getClass());
182 ElementSource source = (ElementSource) Iterables.getOnlyElement(m.getSources());
183 assertEquals(SomeModule.class.getName(),
184 Iterables.getOnlyElement(source.getModuleClassNames()));
185 assertEquals(String.class.getName() + " " + SomeModule.class.getName() + ".aString()",
186 source.toString());
187 }
188 }
189
sameb7445e442015-02-10 15:37:44 -0800190 public static class FailingScanner extends ModuleAnnotatedMethodScanner {
sameb54da0e32015-02-19 18:36:52 -0800191 static Module module() {
192 return new AbstractModule() {
193 @Override protected void configure() {
194 binder().scanModulesForAnnotatedMethods(new FailingScanner());
195 }
196 };
197 }
198
sameb7445e442015-02-10 15:37:44 -0800199 @Override public Set<? extends Class<? extends Annotation>> annotationClasses() {
200 return ImmutableSet.of(TestProvides.class);
201 }
202
203 @Override public <T> Key<T> prepareMethod(
204 Binder binder, Annotation rawAnnotation, Key<T> key, InjectionPoint injectionPoint) {
205 throw new IllegalStateException("Failing in the scanner.");
206 }
207 }
208
209 static class SomeModule extends AbstractModule {
210 @TestProvides String aString() {
211 return "Foo";
212 }
213
214 @Override protected void configure() {}
215 }
sameb54da0e32015-02-19 18:36:52 -0800216
217 public void testChildInjectorInheritsScanner() {
218 Injector parent = Guice.createInjector(NamedMunger.module());
219 Injector child = parent.createChildInjector(new AbstractModule() {
220 @Override protected void configure() {}
221
222 @TestProvides @Named("foo") String foo() {
223 return "foo";
224 }
225 });
226 assertMungedBinding(child, String.class, "foo", "foo");
227 }
228
229 public void testChildInjectorScannersDontImpactSiblings() {
230 Module module = new AbstractModule() {
231 @Override
232 protected void configure() {}
233
234 @TestProvides @Named("foo") String foo() {
235 return "foo";
236 }
237 };
238 Injector parent = Guice.createInjector();
239 Injector child = parent.createChildInjector(NamedMunger.module(), module);
240 assertMungedBinding(child, String.class, "foo", "foo");
241
242 // no foo nor foo-munged in sibling, since scanner never saw it.
243 Injector sibling = parent.createChildInjector(module);
244 assertNull(sibling.getExistingBinding(Key.get(String.class, named("foo"))));
245 assertNull(sibling.getExistingBinding(Key.get(String.class, named("foo-munged"))));
246 }
247
248 public void testPrivateModuleInheritScanner_usingPrivateModule() {
249 Injector injector = Guice.createInjector(NamedMunger.module(), new PrivateModule() {
250 @Override protected void configure() {}
251
252 @Exposed @TestProvides @Named("foo") String foo() {
253 return "foo";
254 }
255 });
256 assertMungedBinding(injector, String.class, "foo", "foo");
257 }
258
sameb02c489e2015-03-20 12:29:45 -0700259 public void testPrivateModule_skipSourcesWithinPrivateModule() {
260 Injector injector = Guice.createInjector(NamedMunger.module(), new PrivateModule() {
261 @Override protected void configure() {
262 binder().skipSources(getClass()).install(new AbstractModule() {
263 @Override protected void configure() {}
264 @Exposed @TestProvides @Named("foo") String foo() {
265 return "foo";
266 }
267 });
268 }
269 });
270 assertMungedBinding(injector, String.class, "foo", "foo");
271 }
272
273 public void testPrivateModule_skipSourcesForPrivateModule() {
274 Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
275 @Override protected void configure() {
276 binder().skipSources(getClass()).install(new PrivateModule() {
277 @Override protected void configure() {}
278
279 @Exposed @TestProvides @Named("foo") String foo() {
280 return "foo";
281 }
282 });
283 }});
284 assertMungedBinding(injector, String.class, "foo", "foo");
285 }
286
sameb54da0e32015-02-19 18:36:52 -0800287 public void testPrivateModuleInheritScanner_usingPrivateBinder() {
288 Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
289 @Override protected void configure() {
290 binder().newPrivateBinder().install(new AbstractModule() {
291 @Override protected void configure() {}
292
293 @Exposed @TestProvides @Named("foo") String foo() {
294 return "foo";
295 }
296 });
297 }
298 });
299 assertMungedBinding(injector, String.class, "foo", "foo");
300 }
301
sameb02c489e2015-03-20 12:29:45 -0700302 public void testPrivateModuleInheritScanner_skipSourcesFromPrivateBinder() {
303 Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
304 @Override protected void configure() {
305 binder().newPrivateBinder().skipSources(getClass()).install(new AbstractModule() {
306 @Override protected void configure() {}
307
308 @Exposed @TestProvides @Named("foo") String foo() {
309 return "foo";
310 }
311 });
312 }
313 });
314 assertMungedBinding(injector, String.class, "foo", "foo");
315 }
316
317 public void testPrivateModuleInheritScanner_skipSourcesFromPrivateBinder2() {
318 Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
319 @Override protected void configure() {
320 binder().skipSources(getClass()).newPrivateBinder().install(new AbstractModule() {
321 @Override protected void configure() {}
322
323 @Exposed @TestProvides @Named("foo") String foo() {
324 return "foo";
325 }
326 });
327 }
328 });
329 assertMungedBinding(injector, String.class, "foo", "foo");
330 }
331
sameb54da0e32015-02-19 18:36:52 -0800332 public void testPrivateModuleScannersDontImpactSiblings_usingPrivateModule() {
333 Injector injector = Guice.createInjector(new PrivateModule() {
334 @Override protected void configure() {
335 install(NamedMunger.module());
336 }
337
338 @Exposed @TestProvides @Named("foo") String foo() {
339 return "foo";
340 }
341 }, new PrivateModule() {
342 @Override protected void configure() {}
343
344 // ignored! (because the scanner doesn't run over this module)
345 @Exposed @TestProvides @Named("foo") String foo() {
346 return "foo";
347 }
348 });
349 assertMungedBinding(injector, String.class, "foo", "foo");
350 }
351
352 public void testPrivateModuleScannersDontImpactSiblings_usingPrivateBinder() {
353 Injector injector = Guice.createInjector(new AbstractModule() {
354 @Override protected void configure() {
355 binder().newPrivateBinder().install(new AbstractModule() {
356 @Override protected void configure() {
357 install(NamedMunger.module());
358 }
359
360 @Exposed @TestProvides @Named("foo") String foo() {
361 return "foo";
362 }
363 });
364 }
365 }, new AbstractModule() {
366 @Override protected void configure() {
367 binder().newPrivateBinder().install(new AbstractModule() {
368 @Override protected void configure() {}
369
370 // ignored! (because the scanner doesn't run over this module)
371 @Exposed @TestProvides @Named("foo") String foo() {
372 return "foo";
373 }
374 });
375 }});
376 assertMungedBinding(injector, String.class, "foo", "foo");
377 }
sameb02c489e2015-03-20 12:29:45 -0700378
379 public void testPrivateModuleWithinPrivateModule() {
380 Injector injector = Guice.createInjector(NamedMunger.module(), new PrivateModule() {
381 @Override protected void configure() {
382 expose(Key.get(String.class, named("foo-munged")));
383 install(new PrivateModule() {
384 @Override protected void configure() {}
385
386 @Exposed @TestProvides @Named("foo") String foo() {
387 return "foo";
388 }
389 });
390 }
391 });
392 assertMungedBinding(injector, String.class, "foo", "foo");
393 }
samebe18906a2015-02-02 14:07:53 -0800394}