PageRenderTime 53ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/core/test/com/google/inject/spi/ProviderMethodsTest.java

https://gitlab.com/metamorphiccode/guice
Java | 952 lines | 737 code | 128 blank | 87 comment | 1 complexity | 21f1746f856fce1c7a0a21f40a8df968 MD5 | raw file
  1. /**
  2. * Copyright (C) 2007 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. package com.google.inject.spi;
  17. import static com.google.inject.Asserts.assertContains;
  18. import static java.lang.annotation.RetentionPolicy.RUNTIME;
  19. import com.google.common.collect.ImmutableList;
  20. import com.google.common.collect.ImmutableSet;
  21. import com.google.common.collect.Iterables;
  22. import com.google.common.collect.Lists;
  23. import com.google.inject.AbstractModule;
  24. import com.google.inject.Binder;
  25. import com.google.inject.Binding;
  26. import com.google.inject.BindingAnnotation;
  27. import com.google.inject.CreationException;
  28. import com.google.inject.Guice;
  29. import com.google.inject.Inject;
  30. import com.google.inject.Injector;
  31. import com.google.inject.Key;
  32. import com.google.inject.Module;
  33. import com.google.inject.Provides;
  34. import com.google.inject.ProvisionException;
  35. import com.google.inject.Singleton;
  36. import com.google.inject.Stage;
  37. import com.google.inject.TypeLiteral;
  38. import com.google.inject.internal.Errors;
  39. import com.google.inject.internal.InternalFlags;
  40. import com.google.inject.internal.ProviderMethod;
  41. import com.google.inject.internal.ProviderMethodsModule;
  42. import com.google.inject.name.Named;
  43. import com.google.inject.name.Names;
  44. import com.google.inject.util.Providers;
  45. import com.google.inject.util.Types;
  46. import junit.framework.TestCase;
  47. import java.lang.annotation.ElementType;
  48. import java.lang.annotation.Retention;
  49. import java.lang.annotation.RetentionPolicy;
  50. import java.lang.annotation.Target;
  51. import java.lang.reflect.Method;
  52. import java.util.ArrayList;
  53. import java.util.Collection;
  54. import java.util.List;
  55. import java.util.Set;
  56. import java.util.concurrent.atomic.AtomicReference;
  57. import java.util.logging.Handler;
  58. import java.util.logging.LogRecord;
  59. import java.util.logging.Logger;
  60. /**
  61. * @author crazybob@google.com (Bob Lee)
  62. */
  63. public class ProviderMethodsTest extends TestCase implements Module {
  64. @SuppressWarnings("unchecked")
  65. public void testProviderMethods() {
  66. Injector injector = Guice.createInjector(this);
  67. Bob bob = injector.getInstance(Bob.class);
  68. assertEquals("A Bob", bob.getName());
  69. Bob clone = injector.getInstance(Bob.class);
  70. assertEquals("A Bob", clone.getName());
  71. assertNotSame(bob, clone);
  72. assertSame(bob.getDaughter(), clone.getDaughter());
  73. Key soleBobKey = Key.get(Bob.class, Sole.class);
  74. assertSame(
  75. injector.getInstance(soleBobKey),
  76. injector.getInstance(soleBobKey)
  77. );
  78. }
  79. public void configure(Binder binder) {}
  80. interface Bob {
  81. String getName();
  82. Dagny getDaughter();
  83. }
  84. interface Dagny {
  85. int getAge();
  86. }
  87. @Provides
  88. Bob provideBob(final Dagny dagny) {
  89. return new Bob() {
  90. public String getName() {
  91. return "A Bob";
  92. }
  93. public Dagny getDaughter() {
  94. return dagny;
  95. }
  96. };
  97. }
  98. @Provides
  99. @Singleton
  100. @Sole
  101. Bob provideSoleBob(final Dagny dagny) {
  102. return new Bob() {
  103. public String getName() {
  104. return "Only Bob";
  105. }
  106. public Dagny getDaughter() {
  107. return dagny;
  108. }
  109. };
  110. }
  111. @Provides
  112. @Singleton
  113. Dagny provideDagny() {
  114. return new Dagny() {
  115. public int getAge() {
  116. return 1;
  117. }
  118. };
  119. }
  120. @Retention(RUNTIME)
  121. @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
  122. @BindingAnnotation
  123. @interface Sole {}
  124. // We'll have to make getProvider() support circular dependencies before this
  125. // will work.
  126. //
  127. // public void testCircularDependency() {
  128. // Injector injector = Guice.createInjector(new Module() {
  129. // public void configure(Binder binder) {
  130. // binder.install(ProviderMethods.from(ProviderMethodsTest.this));
  131. // }
  132. // });
  133. //
  134. // Foo foo = injector.getInstance(Foo.class);
  135. // assertEquals(5, foo.getI());
  136. // assertEquals(10, foo.getBar().getI());
  137. // assertEquals(5, foo.getBar().getFoo().getI());
  138. // }
  139. //
  140. // interface Foo {
  141. // Bar getBar();
  142. // int getI();
  143. // }
  144. //
  145. // interface Bar {
  146. // Foo getFoo();
  147. // int getI();
  148. // }
  149. //
  150. // @Provides Foo newFoo(final Bar bar) {
  151. // return new Foo() {
  152. //
  153. // public Bar getBar() {
  154. // return bar;
  155. // }
  156. //
  157. // public int getI() {
  158. // return 5;
  159. // }
  160. // };
  161. // }
  162. //
  163. // @Provides Bar newBar(final Foo foo) {
  164. // return new Bar() {
  165. //
  166. // public Foo getFoo() {
  167. // return foo;
  168. // }
  169. //
  170. // public int getI() {
  171. // return 10;
  172. // }
  173. // };
  174. // }
  175. public void testMultipleBindingAnnotations() {
  176. try {
  177. Guice.createInjector(new AbstractModule() {
  178. @Override protected void configure() {}
  179. @Provides @Named("A") @Blue
  180. public String provideString() {
  181. return "a";
  182. }
  183. });
  184. fail();
  185. } catch (CreationException expected) {
  186. assertContains(expected.getMessage(),
  187. "more than one annotation annotated with @BindingAnnotation:", "Named", "Blue",
  188. "at " + getClass().getName(), ".provideString(ProviderMethodsTest.java:");
  189. }
  190. }
  191. @Retention(RUNTIME)
  192. @BindingAnnotation @interface Blue {}
  193. public void testGenericProviderMethods() {
  194. Injector injector = Guice.createInjector(
  195. new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {});
  196. assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First"))));
  197. assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second"))));
  198. assertEquals(ImmutableSet.of("A", "B"),
  199. injector.getInstance(Key.get(Types.setOf(String.class))));
  200. assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("First"))).intValue());
  201. assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("Second"))).intValue());
  202. assertEquals(ImmutableSet.of(1, 2),
  203. injector.getInstance(Key.get(Types.setOf(Integer.class))));
  204. }
  205. abstract class ProvideTs<T> extends AbstractModule {
  206. final T first;
  207. final T second;
  208. protected ProvideTs(T first, T second) {
  209. this.first = first;
  210. this.second = second;
  211. }
  212. @Override protected void configure() {}
  213. @Named("First") @Provides T provideFirst() {
  214. return first;
  215. }
  216. @Named("Second") @Provides T provideSecond() {
  217. return second;
  218. }
  219. @Provides Set<T> provideBoth(@Named("First") T first, @Named("Second") T second) {
  220. return ImmutableSet.of(first, second);
  221. }
  222. }
  223. public void testAutomaticProviderMethods() {
  224. Injector injector = Guice.createInjector((Module) new AbstractModule() {
  225. @Override protected void configure() { }
  226. private int next = 1;
  227. @Provides @Named("count")
  228. public Integer provideCount() {
  229. return next++;
  230. }
  231. });
  232. assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
  233. assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
  234. assertEquals(3, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue());
  235. }
  236. /**
  237. * If the user installs provider methods for the module manually, that shouldn't cause a double
  238. * binding of the provider methods' types.
  239. */
  240. public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() {
  241. Module installsSelf = new AbstractModule() {
  242. @Override protected void configure() {
  243. install(this);
  244. bind(Integer.class).toInstance(5);
  245. }
  246. @Provides public String provideString(Integer count) {
  247. return "A" + count;
  248. }
  249. };
  250. Injector injector = Guice.createInjector(installsSelf);
  251. assertEquals("A5", injector.getInstance(String.class));
  252. }
  253. public void testWildcardProviderMethods() {
  254. final List<String> strings = ImmutableList.of("A", "B", "C");
  255. final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3);
  256. Injector injector = Guice.createInjector(new AbstractModule() {
  257. @Override protected void configure() {
  258. @SuppressWarnings("unchecked")
  259. Key<List<? super Integer>> listOfSupertypesOfInteger = (Key<List<? super Integer>>)
  260. Key.get(Types.listOf(Types.supertypeOf(Integer.class)));
  261. bind(listOfSupertypesOfInteger).toInstance(numbers);
  262. }
  263. @Provides public List<? extends CharSequence> provideCharSequences() {
  264. return strings;
  265. }
  266. @Provides public Class<?> provideType() {
  267. return Float.class;
  268. }
  269. });
  270. assertSame(strings, injector.getInstance(HasWildcardInjection.class).charSequences);
  271. assertSame(numbers, injector.getInstance(HasWildcardInjection.class).numbers);
  272. assertSame(Float.class, injector.getInstance(HasWildcardInjection.class).type);
  273. }
  274. static class HasWildcardInjection {
  275. @Inject List<? extends CharSequence> charSequences;
  276. @Inject List<? super Integer> numbers;
  277. @Inject Class<?> type;
  278. }
  279. public void testProviderMethodDependenciesAreExposed() throws Exception {
  280. Module module = new AbstractModule() {
  281. @Override protected void configure() {
  282. bind(Integer.class).toInstance(50);
  283. bindConstant().annotatedWith(Names.named("units")).to("Kg");
  284. }
  285. @Provides @Named("weight") String provideWeight(Integer count, @Named("units") String units) {
  286. return count + units;
  287. }
  288. };
  289. Injector injector = Guice.createInjector(module);
  290. ProviderInstanceBinding<?> binding = (ProviderInstanceBinding<?>) injector.getBinding(
  291. Key.get(String.class, Names.named("weight")));
  292. Method method =
  293. module.getClass().getDeclaredMethod("provideWeight", Integer.class, String.class);
  294. InjectionPoint point = new InjectionPoint(TypeLiteral.get(module.getClass()), method, false);
  295. assertEquals(ImmutableSet.<Dependency<?>>of(
  296. new Dependency<Integer>(point, Key.get(Integer.class), false, 0),
  297. new Dependency<String>(point, Key.get(String.class, Names.named("units")), false, 1)),
  298. binding.getDependencies());
  299. }
  300. public void testNonModuleProviderMethods() {
  301. final Object methodsObject = new Object() {
  302. @Provides @Named("foo") String provideFoo() {
  303. return "foo-value";
  304. }
  305. };
  306. Module module = new AbstractModule() {
  307. @Override protected void configure() {
  308. install(ProviderMethodsModule.forObject(methodsObject));
  309. }
  310. };
  311. Injector injector = Guice.createInjector(module);
  312. Key<String> key = Key.get(String.class, Names.named("foo"));
  313. assertEquals("foo-value", injector.getInstance(key));
  314. // Test the provider method object itself. This makes sure getInstance works, since GIN uses it
  315. List<Element> elements = Elements.getElements(module);
  316. assertEquals(1, elements.size());
  317. Element element = elements.get(0);
  318. assertTrue(element + " instanceof ProviderInstanceBinding",
  319. element instanceof ProviderInstanceBinding);
  320. ProviderInstanceBinding binding = (ProviderInstanceBinding) element;
  321. javax.inject.Provider provider = binding.getUserSuppliedProvider();
  322. assertTrue(provider instanceof ProviderMethod);
  323. assertEquals(methodsObject, ((ProviderMethod) provider).getInstance());
  324. assertSame(provider, binding.getProviderInstance());
  325. }
  326. public void testVoidProviderMethods() {
  327. try {
  328. Guice.createInjector(new AbstractModule() {
  329. @Override protected void configure() {}
  330. @Provides void provideFoo() {}
  331. });
  332. fail();
  333. } catch (CreationException expected) {
  334. assertContains(expected.getMessage(),
  335. "1) Provider methods must return a value. Do not return void.",
  336. getClass().getName(), ".provideFoo(ProviderMethodsTest.java:");
  337. }
  338. }
  339. public void testInjectsJustOneLogger() {
  340. AtomicReference<Logger> loggerRef = new AtomicReference<Logger>();
  341. Injector injector = Guice.createInjector(new FooModule(loggerRef));
  342. assertNull(loggerRef.get());
  343. injector.getInstance(Integer.class);
  344. Logger lastLogger = loggerRef.getAndSet(null);
  345. assertNotNull(lastLogger);
  346. injector.getInstance(Integer.class);
  347. assertSame(lastLogger, loggerRef.get());
  348. assertEquals(FooModule.class.getName(), lastLogger.getName());
  349. }
  350. private static class FooModule extends AbstractModule {
  351. private final AtomicReference<Logger> loggerRef;
  352. public FooModule(AtomicReference<Logger> loggerRef) {
  353. this.loggerRef = loggerRef;
  354. }
  355. @Override protected void configure() {}
  356. @SuppressWarnings("unused")
  357. @Provides Integer foo(Logger logger) {
  358. loggerRef.set(logger);
  359. return 42;
  360. }
  361. }
  362. public void testSpi() throws Exception {
  363. Module m1 = new AbstractModule() {
  364. @Override protected void configure() {}
  365. @Provides @Named("foo") String provideFoo(Integer dep) { return "foo"; }
  366. };
  367. Module m2 = new AbstractModule() {
  368. @Override protected void configure() {}
  369. @Provides Integer provideInt(@Named("foo") String dep) { return 42; }
  370. };
  371. Injector injector = Guice.createInjector(m1, m2);
  372. Binding<String> stringBinding =
  373. injector.getBinding(Key.get(String.class, Names.named("foo")));
  374. ProvidesMethodBinding<String> stringMethod =
  375. stringBinding.acceptTargetVisitor(new BindingCapturer<String>());
  376. assertEquals(m1, stringMethod.getEnclosingInstance());
  377. assertEquals(m1.getClass().getDeclaredMethod("provideFoo", Integer.class),
  378. stringMethod.getMethod());
  379. assertEquals(((HasDependencies) stringBinding).getDependencies(),
  380. stringMethod.getDependencies());
  381. assertEquals(Key.get(String.class, Names.named("foo")), stringMethod.getKey());
  382. Binding<Integer> intBinding = injector.getBinding(Integer.class);
  383. ProvidesMethodBinding<Integer> intMethod =
  384. intBinding.acceptTargetVisitor(new BindingCapturer<Integer>());
  385. assertEquals(m2, intMethod.getEnclosingInstance());
  386. assertEquals(m2.getClass().getDeclaredMethod("provideInt", String.class),
  387. intMethod.getMethod());
  388. assertEquals(((HasDependencies) intBinding).getDependencies(),
  389. intMethod.getDependencies());
  390. assertEquals(Key.get(Integer.class), intMethod.getKey());
  391. }
  392. private static class BindingCapturer<T> extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>>
  393. implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> {
  394. @SuppressWarnings("unchecked")
  395. public ProvidesMethodBinding<T> visit(
  396. ProvidesMethodBinding<? extends T> providesMethodBinding) {
  397. return (ProvidesMethodBinding<T>)providesMethodBinding;
  398. }
  399. @Override protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) {
  400. throw new IllegalStateException("unexpected visit of: " + binding);
  401. }
  402. }
  403. public void testProvidesMethodVisibility() {
  404. Injector injector = Guice.createInjector(new VisibilityModule());
  405. assertEquals(42, injector.getInstance(Integer.class).intValue());
  406. assertEquals(42L, injector.getInstance(Long.class).longValue());
  407. assertEquals(42D, injector.getInstance(Double.class).doubleValue());
  408. assertEquals(42F, injector.getInstance(Float.class).floatValue());
  409. }
  410. private static class VisibilityModule extends AbstractModule {
  411. @Override protected void configure() {}
  412. @SuppressWarnings("unused")
  413. @Provides Integer foo() {
  414. return 42;
  415. }
  416. @SuppressWarnings("unused")
  417. @Provides private Long bar() {
  418. return 42L;
  419. }
  420. @SuppressWarnings("unused")
  421. @Provides protected Double baz() {
  422. return 42D;
  423. }
  424. @SuppressWarnings("unused")
  425. @Provides public Float quux() {
  426. return 42F;
  427. }
  428. }
  429. public void testProvidesMethodInheritenceHierarchy() {
  430. try {
  431. Guice.createInjector(new Sub1Module(), new Sub2Module());
  432. fail("Expected injector creation failure");
  433. } catch (CreationException expected) {
  434. // both of our super class bindings cause errors
  435. assertContains(expected.getMessage(),
  436. "A binding to java.lang.Long was already configured",
  437. "A binding to java.lang.Integer was already configured");
  438. }
  439. }
  440. public void testProvidesMethodsDefinedInSuperClass() {
  441. Injector injector = Guice.createInjector(new Sub1Module());
  442. assertEquals(42, injector.getInstance(Integer.class).intValue());
  443. assertEquals(42L, injector.getInstance(Long.class).longValue());
  444. assertEquals(42D, injector.getInstance(Double.class).doubleValue());
  445. }
  446. private static class BaseModule extends AbstractModule {
  447. @Override protected void configure() {}
  448. @Provides Integer foo() {
  449. return 42;
  450. }
  451. @Provides Long bar() {
  452. return 42L;
  453. }
  454. }
  455. private static class Sub1Module extends BaseModule {
  456. @Provides Double baz() {
  457. return 42D;
  458. }
  459. }
  460. private static class Sub2Module extends BaseModule {
  461. @Provides Float quux() {
  462. return 42F;
  463. }
  464. }
  465. /*if[AOP]*/
  466. public void testShareFastClass() {
  467. CallerInspecterModule module = new CallerInspecterModule();
  468. Guice.createInjector(Stage.PRODUCTION, module);
  469. assertEquals(module.fooCallerClass, module.barCallerClass);
  470. assertTrue(module.fooCallerClass.contains("$$FastClassByGuice$$"));
  471. }
  472. private static class CallerInspecterModule extends AbstractModule {
  473. // start them off as unequal
  474. String barCallerClass = "not_set_bar";
  475. String fooCallerClass = "not_set_foo";
  476. @Override protected void configure() {}
  477. @Provides @Singleton Integer foo() {
  478. this.fooCallerClass = new Exception().getStackTrace()[1].getClassName();
  479. return 42;
  480. }
  481. @Provides @Singleton Long bar() {
  482. this.barCallerClass = new Exception().getStackTrace()[1].getClassName();
  483. return 42L;
  484. }
  485. }
  486. public void testShareFastClassWithSuperClass() {
  487. CallerInspecterSubClassModule module = new CallerInspecterSubClassModule();
  488. Guice.createInjector(Stage.PRODUCTION, module);
  489. assertEquals("Expected provider methods in the same class to share fastclass classes",
  490. module.fooCallerClass, module.barCallerClass);
  491. assertFalse(
  492. "Did not expect provider methods in the subclasses to share fastclass classes "
  493. + "with their parent classes",
  494. module.bazCallerClass.equals(module.barCallerClass));
  495. }
  496. private static class CallerInspecterSubClassModule extends CallerInspecterModule {
  497. String bazCallerClass;
  498. @Override protected void configure() {}
  499. @Provides @Singleton Double baz() {
  500. this.bazCallerClass = new Exception().getStackTrace()[1].getClassName();
  501. return 42D;
  502. }
  503. }
  504. /*end[AOP]*/
  505. static class SuperClassModule extends AbstractModule {
  506. @Override protected void configure() {}
  507. @Provides Number providerMethod() {
  508. return 1D;
  509. }
  510. @Provides @Named("rawlist") List rawProvider(@Named("list") List<String> f) {
  511. return f;
  512. }
  513. @Provides @Named("unrawlist") List<String> rawParameterProvider(@Named("rawlist") List f) {
  514. return f;
  515. }
  516. @Provides @Named("list") List<String> annotatedGenericProviderMethod() {
  517. return new ArrayList<String>();
  518. }
  519. @Provides @Named("collection") Collection<String> annotatedGenericParameterProviderMethod(
  520. @Named("list") List<String> foo) {
  521. return foo;
  522. }
  523. @Provides private String privateProviderMethod() {
  524. return "hello";
  525. }
  526. }
  527. public void testOverrideProviderMethod_overrideHasProvides() {
  528. class SubClassModule extends SuperClassModule {
  529. @Override @Provides Number providerMethod() {
  530. return 2D;
  531. }
  532. }
  533. try {
  534. Guice.createInjector(new SubClassModule());
  535. fail();
  536. } catch (CreationException e) {
  537. assertContains(e.getMessage(),
  538. "Overriding @Provides methods is not allowed.",
  539. "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
  540. "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
  541. }
  542. }
  543. public void testOverrideProviderMethod_overrideHasProvides_withNewAnnotation() {
  544. class SubClassModule extends SuperClassModule {
  545. @Override @Provides @Named("foo") Number providerMethod() {
  546. return 2D;
  547. }
  548. }
  549. try {
  550. Guice.createInjector(new SubClassModule());
  551. fail();
  552. } catch (CreationException e) {
  553. assertContains(e.getMessage(),
  554. "Overriding @Provides methods is not allowed.",
  555. "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
  556. "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
  557. }
  558. }
  559. public void testOverrideProviderMethod_overrideDoesntHaveProvides() {
  560. class SubClassModule extends SuperClassModule {
  561. @Override Number providerMethod() {
  562. return 2D;
  563. }
  564. }
  565. try {
  566. Guice.createInjector(new SubClassModule());
  567. fail();
  568. } catch (CreationException e) {
  569. assertContains(e.getMessage(),
  570. "Overriding @Provides methods is not allowed.",
  571. "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
  572. "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
  573. }
  574. }
  575. public void testOverrideProviderMethod_overrideDoesntHaveProvides_withNewAnnotation() {
  576. class SubClassModule extends SuperClassModule {
  577. @Override @Named("foo") Number providerMethod() {
  578. return 2D;
  579. }
  580. }
  581. try {
  582. Guice.createInjector(new SubClassModule());
  583. fail();
  584. } catch (CreationException e) {
  585. assertContains(e.getMessage(),
  586. "Overriding @Provides methods is not allowed.",
  587. "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
  588. "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
  589. }
  590. }
  591. public void testOverrideProviderMethod_covariantOverrideDoesntHaveProvides() {
  592. class SubClassModule extends SuperClassModule {
  593. @Override Double providerMethod() {
  594. return 2D;
  595. }
  596. }
  597. try {
  598. Guice.createInjector(new SubClassModule());
  599. fail();
  600. } catch (CreationException e) {
  601. assertContains(e.getMessage(),
  602. "Overriding @Provides methods is not allowed.",
  603. "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
  604. "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
  605. }
  606. }
  607. public void testOverrideProviderMethod_covariantOverrideHasProvides() {
  608. class SubClassModule extends SuperClassModule {
  609. @Override @Provides Double providerMethod() {
  610. return 2D;
  611. }
  612. }
  613. try {
  614. Guice.createInjector(new SubClassModule());
  615. fail();
  616. } catch (CreationException e) {
  617. assertContains(e.getMessage(),
  618. "Overriding @Provides methods is not allowed.",
  619. "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()",
  620. "overridden by: " + SubClassModule.class.getName() + ".providerMethod()");
  621. }
  622. }
  623. public void testOverrideProviderMethod_fakeOverridePrivateMethod() {
  624. class SubClassModule extends SuperClassModule {
  625. // not actually an override, just looks like it
  626. String privateProviderMethod() {
  627. return "sub";
  628. }
  629. }
  630. assertEquals("hello", Guice.createInjector(new SubClassModule()).getInstance(String.class));
  631. }
  632. public void testOverrideProviderMethod_subclassRawTypes_returnType() {
  633. class SubClassModule extends SuperClassModule {
  634. @Override List annotatedGenericProviderMethod() {
  635. return super.annotatedGenericProviderMethod();
  636. }
  637. }
  638. try {
  639. Guice.createInjector(new SubClassModule());
  640. fail();
  641. } catch (CreationException e) {
  642. assertContains(e.getMessage(),
  643. "Overriding @Provides methods is not allowed.",
  644. "@Provides method: " + SuperClassModule.class.getName()
  645. + ".annotatedGenericProviderMethod()",
  646. "overridden by: " + SubClassModule.class.getName() + ".annotatedGenericProviderMethod()");
  647. }
  648. }
  649. public void testOverrideProviderMethod_subclassRawTypes_parameterType() {
  650. class SubClassModule extends SuperClassModule {
  651. @Override Collection<String> annotatedGenericParameterProviderMethod(List foo) {
  652. return super.annotatedGenericParameterProviderMethod(foo);
  653. }
  654. }
  655. try {
  656. Guice.createInjector(new SubClassModule());
  657. fail();
  658. } catch (CreationException e) {
  659. assertContains(e.getMessage(),
  660. "Overriding @Provides methods is not allowed.",
  661. "@Provides method: " + SuperClassModule.class.getName()
  662. + ".annotatedGenericParameterProviderMethod()",
  663. "overridden by: " + SubClassModule.class.getName()
  664. + ".annotatedGenericParameterProviderMethod()");
  665. }
  666. }
  667. public void testOverrideProviderMethod_superclassRawTypes_returnType() {
  668. class SubClassModule extends SuperClassModule {
  669. // remove the rawtype from the override
  670. @Override List<String> rawProvider(List<String> f) {
  671. return f;
  672. }
  673. }
  674. try {
  675. Guice.createInjector(new SubClassModule());
  676. fail();
  677. } catch (CreationException e) {
  678. assertContains(e.getMessage(),
  679. "Overriding @Provides methods is not allowed.",
  680. "@Provides method: " + SuperClassModule.class.getName() + ".rawProvider()",
  681. "overridden by: " + SubClassModule.class.getName() + ".rawProvider()");
  682. }
  683. }
  684. abstract static class GenericSuperModule<T> extends AbstractModule {
  685. @Provides String provide(T thing) {
  686. return thing.toString();
  687. }
  688. }
  689. // This is a tricky case where signatures don't match, but it is an override (facilitated via a
  690. // bridge method)
  691. public void testOverrideProviderMethod_erasureBasedOverrides() {
  692. class SubClassModule extends GenericSuperModule<Integer> {
  693. @Override String provide(Integer thing) {
  694. return thing.toString();
  695. }
  696. @Override protected void configure() {
  697. bind(Integer.class).toInstance(3);
  698. }
  699. }
  700. try {
  701. Guice.createInjector(new SubClassModule());
  702. fail();
  703. } catch (CreationException e) {
  704. assertContains(e.getMessage(),
  705. "Overriding @Provides methods is not allowed.",
  706. "@Provides method: " + GenericSuperModule.class.getName() + ".provide()",
  707. "overridden by: " + SubClassModule.class.getName() + ".provide()");
  708. }
  709. }
  710. class RestrictedSuper extends AbstractModule {
  711. @Provides public String provideFoo() { return "foo"; }
  712. @Override protected void configure() {}
  713. }
  714. public class ExposedSub extends RestrictedSuper {}
  715. public void testOverrideProviderMethod_increasedVisibility() {
  716. // ensure we don't detect the synthetic provideFoo method in ExposedSub as an override (it is,
  717. // but since it is synthetic it would be annoying to throw an error on it).
  718. assertEquals("foo", Guice.createInjector(new ExposedSub()).getInstance(String.class));
  719. }
  720. interface ProviderInterface<T> {
  721. T getT();
  722. }
  723. static class ModuleImpl extends AbstractModule implements ProviderInterface<String> {
  724. @Override protected void configure() {}
  725. @Provides public String getT() {
  726. return "string";
  727. }
  728. @Provides public Object getObject() {
  729. return new Object();
  730. }
  731. /* javac will synthesize a bridge method for getT with the types erased, equivalent to:
  732. * @Provides public Object getT() { ... }
  733. */
  734. }
  735. public void testIgnoreSyntheticBridgeMethods() {
  736. Guice.createInjector(new ModuleImpl());
  737. }
  738. public void testNullability() throws Exception {
  739. Module module = new AbstractModule() {
  740. @Override
  741. protected void configure() {
  742. bind(String.class).toProvider(Providers.<String>of(null));
  743. }
  744. @SuppressWarnings("unused")
  745. @Provides
  746. Integer fail(String foo) {
  747. return 1;
  748. }
  749. @SuppressWarnings("unused")
  750. @Provides
  751. Long succeed(@Nullable String foo) {
  752. return 2L;
  753. }
  754. };
  755. Injector injector = Guice.createInjector(module);
  756. InjectionPoint fooPoint = InjectionPoint.forMethod(
  757. module.getClass().getDeclaredMethod("fail", String.class),
  758. TypeLiteral.get(module.getClass()));
  759. Dependency<?> fooDependency = Iterables.getOnlyElement(fooPoint.getDependencies());
  760. runNullableTest(injector, fooDependency, module);
  761. injector.getInstance(Long.class);
  762. }
  763. private void runNullableTest(Injector injector, Dependency<?> dependency, Module module) {
  764. switch (InternalFlags.getNullableProvidesOption()) {
  765. case ERROR:
  766. validateNullableFails(injector, module);
  767. break;
  768. case IGNORE:
  769. validateNullableIgnored(injector);
  770. break;
  771. case WARN:
  772. validateNullableWarns(injector, dependency);
  773. break;
  774. }
  775. }
  776. private void validateNullableFails(Injector injector, Module module) {
  777. try {
  778. injector.getInstance(Integer.class);
  779. fail();
  780. } catch (ProvisionException expected) {
  781. assertContains(expected.getMessage(),
  782. "1) null returned by binding at " + module.getClass().getName() + ".configure(",
  783. "but parameter 0 of " + module.getClass().getName() + ".fail() is not @Nullable",
  784. "while locating java.lang.String",
  785. "for parameter 0 at " + module.getClass().getName() + ".fail(",
  786. "while locating java.lang.Integer");
  787. assertEquals(1, expected.getErrorMessages().size());
  788. }
  789. }
  790. private void validateNullableIgnored(Injector injector) {
  791. injector.getInstance(Integer.class); // no exception
  792. }
  793. private void validateNullableWarns(Injector injector, Dependency<?> dependency) {
  794. final List<LogRecord> logRecords = Lists.newArrayList();
  795. final Handler fakeHandler = new Handler() {
  796. @Override
  797. public void publish(LogRecord logRecord) {
  798. logRecords.add(logRecord);
  799. }
  800. @Override
  801. public void flush() {}
  802. @Override
  803. public void close() throws SecurityException {}
  804. };
  805. Logger.getLogger(Guice.class.getName()).addHandler(fakeHandler);
  806. try {
  807. injector.getInstance(Integer.class); // no exception, but assert it does log.
  808. LogRecord record = Iterables.getOnlyElement(logRecords);
  809. assertEquals(
  810. "Guice injected null into parameter {0} of {1} (a {2}), please mark it @Nullable."
  811. + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an"
  812. + " error.",
  813. record.getMessage());
  814. assertEquals(dependency.getParameterIndex(), record.getParameters()[0]);
  815. assertEquals(Errors.convert(dependency.getInjectionPoint().getMember()),
  816. record.getParameters()[1]);
  817. assertEquals(Errors.convert(dependency.getKey()), record.getParameters()[2]);
  818. } finally {
  819. Logger.getLogger(Guice.class.getName()).removeHandler(fakeHandler);
  820. }
  821. }
  822. @Retention(RetentionPolicy.RUNTIME)
  823. @interface Nullable {}
  824. }