PageRenderTime 57ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/core/test/com/google/inject/TypeListenerTest.java

https://gitlab.com/metamorphiccode/guice
Java | 710 lines | 559 code | 106 blank | 45 comment | 0 complexity | a9195d3e77e2df5049640be40cbf5b01 MD5 | raw file
  1. /**
  2. * Copyright (C) 2009 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;
  17. import static com.google.inject.Asserts.asModuleChain;
  18. import static com.google.inject.Asserts.assertContains;
  19. import static com.google.inject.Asserts.getDeclaringSourcePart;
  20. import static com.google.inject.matcher.Matchers.any;
  21. import static com.google.inject.matcher.Matchers.only;
  22. import static com.google.inject.name.Names.named;
  23. import com.google.common.collect.ImmutableList;
  24. import com.google.common.collect.Lists;
  25. import com.google.inject.matcher.Matcher;
  26. import com.google.inject.matcher.Matchers;
  27. import com.google.inject.spi.InjectionListener;
  28. import com.google.inject.spi.Message;
  29. import com.google.inject.spi.TypeEncounter;
  30. import com.google.inject.spi.TypeListener;
  31. import junit.framework.TestCase;
  32. import java.util.List;
  33. import java.util.concurrent.atomic.AtomicInteger;
  34. import java.util.concurrent.atomic.AtomicReference;
  35. /**
  36. * @author jessewilson@google.com (Jesse Wilson)
  37. */
  38. public class TypeListenerTest extends TestCase {
  39. private final Matcher<Object> onlyAbcd = Matchers.only(new TypeLiteral<A>() {})
  40. .or(only(new TypeLiteral<B>() {}))
  41. .or(only(new TypeLiteral<C>() {}))
  42. .or(only(new TypeLiteral<D>() {}));
  43. final TypeListener failingTypeListener = new TypeListener() {
  44. int failures = 0;
  45. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  46. throw new ClassCastException("whoops, failure #" + (++failures));
  47. }
  48. @Override public String toString() {
  49. return "clumsy";
  50. }
  51. };
  52. final InjectionListener<Object> failingInjectionListener = new InjectionListener<Object>() {
  53. int failures = 0;
  54. public void afterInjection(Object injectee) {
  55. throw new ClassCastException("whoops, failure #" + (++failures));
  56. }
  57. @Override public String toString() {
  58. return "goofy";
  59. }
  60. };
  61. final MembersInjector<Object> failingMembersInjector = new MembersInjector<Object>() {
  62. int failures = 0;
  63. public void injectMembers(Object instance) {
  64. throw new ClassCastException("whoops, failure #" + (++failures));
  65. }
  66. @Override public String toString() {
  67. return "awkward";
  68. }
  69. };
  70. public void testTypeListenersAreFired() {
  71. final AtomicInteger firedCount = new AtomicInteger();
  72. final TypeListener typeListener = new TypeListener() {
  73. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  74. assertEquals(new TypeLiteral<A>() {}, type);
  75. firedCount.incrementAndGet();
  76. }
  77. };
  78. Guice.createInjector(new AbstractModule() {
  79. @Override protected void configure() {
  80. bindListener(onlyAbcd, typeListener);
  81. bind(A.class);
  82. }
  83. });
  84. assertEquals(1, firedCount.get());
  85. }
  86. public void testInstallingInjectionListener() {
  87. final List<Object> injectees = Lists.newArrayList();
  88. final InjectionListener<Object> injectionListener = new InjectionListener<Object>() {
  89. public void afterInjection(Object injectee) {
  90. injectees.add(injectee);
  91. }
  92. };
  93. Injector injector = Guice.createInjector(new AbstractModule() {
  94. @Override protected void configure() {
  95. bindListener(onlyAbcd, new TypeListener() {
  96. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  97. encounter.register(injectionListener);
  98. }
  99. });
  100. bind(A.class);
  101. }
  102. });
  103. assertEquals(ImmutableList.of(), injectees);
  104. Object a1 = injector.getInstance(A.class);
  105. assertEquals(ImmutableList.of(a1), injectees);
  106. Object a2 = injector.getInstance(A.class);
  107. assertEquals(ImmutableList.of(a1, a2), injectees);
  108. Object b1 = injector.getInstance(B.class);
  109. assertEquals(ImmutableList.of(a1, a2, b1), injectees);
  110. Provider<A> aProvider = injector.getProvider(A.class);
  111. assertEquals(ImmutableList.of(a1, a2, b1), injectees);
  112. A a3 = aProvider.get();
  113. A a4 = aProvider.get();
  114. assertEquals(ImmutableList.of(a1, a2, b1, a3, a4), injectees);
  115. }
  116. /*if[AOP]*/
  117. private static org.aopalliance.intercept.MethodInterceptor prefixInterceptor(
  118. final String prefix) {
  119. return new org.aopalliance.intercept.MethodInterceptor() {
  120. public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation)
  121. throws Throwable {
  122. return prefix + methodInvocation.proceed();
  123. }
  124. };
  125. }
  126. public void testAddingInterceptors() throws NoSuchMethodException {
  127. final Matcher<Object> buzz = only(C.class.getMethod("buzz"));
  128. Injector injector = Guice.createInjector(new AbstractModule() {
  129. @Override protected void configure() {
  130. bindInterceptor(any(), buzz, prefixInterceptor("ka"));
  131. bindInterceptor(any(), any(), prefixInterceptor("fe"));
  132. bindListener(onlyAbcd, new TypeListener() {
  133. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  134. encounter.bindInterceptor(any(), prefixInterceptor("li"));
  135. encounter.bindInterceptor(buzz, prefixInterceptor("no"));
  136. }
  137. });
  138. }
  139. });
  140. // interceptors must be invoked in the order they're bound.
  141. C c = injector.getInstance(C.class);
  142. assertEquals("kafelinobuzz", c.buzz());
  143. assertEquals("felibeep", c.beep());
  144. }
  145. /*end[AOP]*/
  146. class OuterThrowsModule extends AbstractModule {
  147. @Override protected void configure() {
  148. install(new InnerThrowsModule());
  149. }
  150. }
  151. class InnerThrowsModule extends AbstractModule {
  152. @Override protected void configure() {
  153. bindListener(onlyAbcd, failingTypeListener);
  154. bind(B.class);
  155. bind(C.class);
  156. }
  157. }
  158. public void testTypeListenerThrows() {
  159. try {
  160. Guice.createInjector(new OuterThrowsModule());
  161. fail();
  162. } catch (CreationException expected) {
  163. assertContains(expected.getMessage(),
  164. "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
  165. getDeclaringSourcePart(getClass()),
  166. asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
  167. "of " + B.class.getName(),
  168. "Reason: java.lang.ClassCastException: whoops, failure #1",
  169. "2) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
  170. getDeclaringSourcePart(getClass()),
  171. asModuleChain(OuterThrowsModule.class, InnerThrowsModule.class),
  172. "of " + C.class.getName(),
  173. "Reason: java.lang.ClassCastException: whoops, failure #2");
  174. }
  175. Injector injector = Guice.createInjector(new AbstractModule() {
  176. @Override protected void configure() {
  177. bindListener(onlyAbcd, failingTypeListener);
  178. }
  179. });
  180. try {
  181. injector.getProvider(B.class);
  182. fail();
  183. } catch (ConfigurationException expected) {
  184. assertContains(expected.getMessage(),
  185. "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
  186. getDeclaringSourcePart(getClass()),
  187. "of " + B.class.getName(),
  188. "Reason: java.lang.ClassCastException: whoops, failure #3");
  189. }
  190. // getting it again should yield the same exception #3
  191. try {
  192. injector.getInstance(B.class);
  193. fail();
  194. } catch (ConfigurationException expected) {
  195. assertContains(expected.getMessage(),
  196. "1) Error notifying TypeListener clumsy (bound at " + getClass().getName(),
  197. getDeclaringSourcePart(getClass()),
  198. "of " + B.class.getName(),
  199. "Reason: java.lang.ClassCastException: whoops, failure #3");
  200. }
  201. // non-injected types do not participate
  202. assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
  203. }
  204. public void testInjectionListenerThrows() {
  205. Injector injector = Guice.createInjector(new AbstractModule() {
  206. @Override protected void configure() {
  207. bindListener(onlyAbcd, new TypeListener() {
  208. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  209. encounter.register(failingInjectionListener);
  210. }
  211. });
  212. bind(B.class);
  213. }
  214. });
  215. try {
  216. injector.getInstance(A.class);
  217. fail();
  218. } catch (ProvisionException e) {
  219. assertContains(e.getMessage(),
  220. "1) Error notifying InjectionListener goofy of " + A.class.getName(),
  221. " Reason: java.lang.ClassCastException: whoops, failure #1");
  222. }
  223. // second time through should be a new cause (#2)
  224. try {
  225. injector.getInstance(A.class);
  226. fail();
  227. } catch (ProvisionException e) {
  228. assertContains(e.getMessage(),
  229. "1) Error notifying InjectionListener goofy of " + A.class.getName(),
  230. " Reason: java.lang.ClassCastException: whoops, failure #2");
  231. }
  232. // we should get errors for all types, but only on getInstance()
  233. Provider<B> bProvider = injector.getProvider(B.class);
  234. try {
  235. bProvider.get();
  236. fail();
  237. } catch (ProvisionException e) {
  238. assertContains(e.getMessage(),
  239. "1) Error notifying InjectionListener goofy of " + B.class.getName(),
  240. " Reason: java.lang.ClassCastException: whoops, failure #3");
  241. }
  242. // non-injected types do not participate
  243. assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
  244. }
  245. public void testInjectMembersTypeListenerFails() {
  246. try {
  247. Guice.createInjector(new AbstractModule() {
  248. @Override protected void configure() {
  249. getMembersInjector(A.class);
  250. bindListener(onlyAbcd, failingTypeListener);
  251. }
  252. });
  253. fail();
  254. } catch (CreationException expected) {
  255. assertContains(expected.getMessage(),
  256. "1) Error notifying TypeListener clumsy (bound at ",
  257. TypeListenerTest.class.getName(), getDeclaringSourcePart(getClass()),
  258. "of " + A.class.getName(),
  259. " Reason: java.lang.ClassCastException: whoops, failure #1");
  260. }
  261. }
  262. public void testConstructedTypeListenerIsTheSameAsMembersInjectorListener() {
  263. final AtomicInteger typeEncounters = new AtomicInteger();
  264. final AtomicInteger injections = new AtomicInteger();
  265. final InjectionListener<A> listener = new InjectionListener<A>() {
  266. public void afterInjection(A injectee) {
  267. injections.incrementAndGet();
  268. assertNotNull(injectee.injector);
  269. }
  270. };
  271. Injector injector = Guice.createInjector(new AbstractModule() {
  272. @Override protected void configure() {
  273. bindListener(onlyAbcd, new TypeListener() {
  274. @SuppressWarnings("unchecked")
  275. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  276. typeEncounters.incrementAndGet();
  277. encounter.register((InjectionListener) listener);
  278. }
  279. });
  280. bind(A.class);
  281. getMembersInjector(A.class);
  282. }
  283. });
  284. // creating the injector shouldn't trigger injections
  285. assertEquals(0, injections.getAndSet(0));
  286. // constructing an A should trigger an injection
  287. injector.getInstance(A.class);
  288. assertEquals(1, injections.getAndSet(0));
  289. // injecting an A should trigger an injection
  290. injector.injectMembers(new A());
  291. assertEquals(1, injections.getAndSet(0));
  292. // getting a provider shouldn't
  293. Provider<A> aProvider = injector.getProvider(A.class);
  294. MembersInjector<A> aMembersInjector = injector.getMembersInjector(A.class);
  295. assertEquals(0, injections.getAndSet(0));
  296. // exercise the provider
  297. aProvider.get();
  298. aProvider.get();
  299. assertEquals(2, injections.getAndSet(0));
  300. // exercise the members injector
  301. aMembersInjector.injectMembers(new A());
  302. aMembersInjector.injectMembers(new A());
  303. assertEquals(2, injections.getAndSet(0));
  304. // we should only have encountered one type
  305. assertEquals(1, typeEncounters.getAndSet(0));
  306. }
  307. public void testLookupsAtInjectorCreateTime() {
  308. final AtomicReference<Provider<B>> bProviderReference = new AtomicReference<Provider<B>>();
  309. final AtomicReference<MembersInjector<A>> aMembersInjectorReference
  310. = new AtomicReference<MembersInjector<A>>();
  311. final InjectionListener<Object> lookupsTester = new InjectionListener<Object>() {
  312. public void afterInjection(Object injectee) {
  313. assertNotNull(bProviderReference.get().get());
  314. A a = new A();
  315. aMembersInjectorReference.get().injectMembers(a);
  316. assertNotNull(a.injector);
  317. }
  318. };
  319. Guice.createInjector(new AbstractModule() {
  320. @Override protected void configure() {
  321. bindListener(only(TypeLiteral.get(C.class)), new TypeListener() {
  322. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  323. Provider<B> bProvider = encounter.getProvider(B.class);
  324. try {
  325. bProvider.get();
  326. fail();
  327. } catch (IllegalStateException expected) {
  328. assertEquals("This Provider cannot be used until the Injector has been created.",
  329. expected.getMessage());
  330. }
  331. bProviderReference.set(bProvider);
  332. MembersInjector<A> aMembersInjector = encounter.getMembersInjector(A.class);
  333. try {
  334. aMembersInjector.injectMembers(new A());
  335. fail();
  336. } catch (IllegalStateException expected) {
  337. assertEquals(
  338. "This MembersInjector cannot be used until the Injector has been created.",
  339. expected.getMessage());
  340. }
  341. aMembersInjectorReference.set(aMembersInjector);
  342. encounter.register(lookupsTester);
  343. }
  344. });
  345. // this ensures the type listener fires, and also the afterInjection() listener
  346. bind(C.class).asEagerSingleton();
  347. }
  348. });
  349. lookupsTester.afterInjection(null);
  350. }
  351. public void testLookupsPostCreate() {
  352. Injector injector = Guice.createInjector(new AbstractModule() {
  353. @Override protected void configure() {
  354. bindListener(only(TypeLiteral.get(C.class)), new TypeListener() {
  355. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  356. assertNotNull(encounter.getProvider(B.class).get());
  357. A a = new A();
  358. encounter.getMembersInjector(A.class).injectMembers(a);
  359. assertNotNull(a.injector);
  360. }
  361. });
  362. }
  363. });
  364. injector.getInstance(C.class);
  365. }
  366. public void testMembersInjector() {
  367. final MembersInjector<D> membersInjector = new MembersInjector<D>() {
  368. public void injectMembers(D instance) {
  369. instance.userInjected++;
  370. assertEquals(instance.guiceInjected, instance.userInjected);
  371. }
  372. };
  373. final InjectionListener<D> injectionListener = new InjectionListener<D>() {
  374. public void afterInjection(D injectee) {
  375. assertTrue(injectee.userInjected > 0);
  376. injectee.listenersNotified++;
  377. assertEquals(injectee.guiceInjected, injectee.listenersNotified);
  378. }
  379. };
  380. Injector injector = Guice.createInjector(new AbstractModule() {
  381. @Override protected void configure() {
  382. bindListener(onlyAbcd, new TypeListener() {
  383. @SuppressWarnings("unchecked")
  384. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  385. encounter.register((MembersInjector) membersInjector);
  386. encounter.register((InjectionListener) injectionListener);
  387. }
  388. });
  389. D boundThreeTimes = new D();
  390. bind(D.class).annotatedWith(named("i")).toInstance(boundThreeTimes);
  391. bind(D.class).annotatedWith(named("ii")).toInstance(boundThreeTimes);
  392. bind(D.class).annotatedWith(named("iii")).toInstance(boundThreeTimes);
  393. }
  394. });
  395. D boundThreeTimes = injector.getInstance(Key.get(D.class, named("iii")));
  396. boundThreeTimes.assertAllCounts(1);
  397. D getInstance = injector.getInstance(D.class);
  398. getInstance.assertAllCounts(1);
  399. D memberInjection = new D();
  400. injector.injectMembers(memberInjection);
  401. memberInjection.assertAllCounts(1);
  402. injector.injectMembers(memberInjection);
  403. injector.injectMembers(memberInjection);
  404. memberInjection.assertAllCounts(3);
  405. injector.getMembersInjector(D.class).injectMembers(memberInjection);
  406. memberInjection.assertAllCounts(4);
  407. }
  408. public void testMembersInjectorThrows() {
  409. Injector injector = Guice.createInjector(new AbstractModule() {
  410. @Override protected void configure() {
  411. bindListener(onlyAbcd, new TypeListener() {
  412. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  413. encounter.register(failingMembersInjector);
  414. }
  415. });
  416. bind(B.class);
  417. }
  418. });
  419. try {
  420. injector.getInstance(A.class);
  421. fail();
  422. } catch (ProvisionException e) {
  423. assertContains(e.getMessage(),
  424. "1) Error injecting " + A.class.getName() + " using awkward.",
  425. "Reason: java.lang.ClassCastException: whoops, failure #1");
  426. }
  427. // second time through should be a new cause (#2)
  428. try {
  429. injector.getInstance(A.class);
  430. fail();
  431. } catch (ProvisionException e) {
  432. assertContains(e.getMessage(),
  433. "1) Error injecting " + A.class.getName() + " using awkward.",
  434. "Reason: java.lang.ClassCastException: whoops, failure #2");
  435. }
  436. // we should get errors for all types, but only on getInstance()
  437. Provider<B> bProvider = injector.getProvider(B.class);
  438. try {
  439. bProvider.get();
  440. fail();
  441. } catch (ProvisionException e) {
  442. assertContains(e.getMessage(),
  443. "1) Error injecting " + B.class.getName() + " using awkward.",
  444. "Reason: java.lang.ClassCastException: whoops, failure #3");
  445. }
  446. // non-injected types do not participate
  447. assertSame(Stage.DEVELOPMENT, injector.getInstance(Stage.class));
  448. }
  449. /**
  450. * We had a bug where we weren't notifying of types encountered for member injection when those
  451. * types had no members to be injected. Constructed types are always injected because they always
  452. * have at least one injection point: the class constructor.
  453. */
  454. public void testTypesWithNoInjectableMembersAreNotified() {
  455. final AtomicInteger notificationCount = new AtomicInteger();
  456. Guice.createInjector(new AbstractModule() {
  457. @Override protected void configure() {
  458. bindListener(onlyAbcd, new TypeListener() {
  459. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  460. notificationCount.incrementAndGet();
  461. }
  462. });
  463. bind(C.class).toInstance(new C());
  464. }
  465. });
  466. assertEquals(1, notificationCount.get());
  467. }
  468. public void testEncounterCannotBeUsedAfterHearReturns() {
  469. final AtomicReference<TypeEncounter<?>> encounterReference = new AtomicReference<TypeEncounter<?>>();
  470. Guice.createInjector(new AbstractModule() {
  471. @Override protected void configure() {
  472. bindListener(any(), new TypeListener() {
  473. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  474. encounterReference.set(encounter);
  475. }
  476. });
  477. bind(C.class);
  478. }
  479. });
  480. TypeEncounter<?> encounter = encounterReference.get();
  481. try {
  482. encounter.register(new InjectionListener<Object>() {
  483. public void afterInjection(Object injectee) {}
  484. });
  485. fail();
  486. } catch (IllegalStateException expected) {
  487. }
  488. /*if[AOP]*/
  489. try {
  490. encounter.bindInterceptor(any(), new org.aopalliance.intercept.MethodInterceptor() {
  491. public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation)
  492. throws Throwable {
  493. return methodInvocation.proceed();
  494. }
  495. });
  496. fail();
  497. } catch (IllegalStateException expected) {
  498. }
  499. /*end[AOP]*/
  500. try {
  501. encounter.addError(new Exception());
  502. fail();
  503. } catch (IllegalStateException expected) {
  504. }
  505. try {
  506. encounter.getMembersInjector(A.class);
  507. fail();
  508. } catch (IllegalStateException expected) {
  509. }
  510. try {
  511. encounter.getProvider(B.class);
  512. fail();
  513. } catch (IllegalStateException expected) {
  514. }
  515. }
  516. public void testAddErrors() {
  517. try {
  518. Guice.createInjector(new AbstractModule() {
  519. @Override protected void configure() {
  520. requestInjection(new Object());
  521. bindListener(Matchers.any(), new TypeListener() {
  522. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  523. encounter.addError("There was an error on %s", type);
  524. encounter.addError(new IllegalArgumentException("whoops!"));
  525. encounter.addError(new Message("And another problem"));
  526. encounter.addError(new IllegalStateException());
  527. }
  528. });
  529. }
  530. });
  531. fail();
  532. } catch (CreationException expected) {
  533. assertContains(expected.getMessage(),
  534. "1) There was an error on java.lang.Object",
  535. "2) An exception was caught and reported. Message: whoops!",
  536. "3) And another problem",
  537. "4) An exception was caught and reported. Message: null",
  538. "4 errors");
  539. }
  540. }
  541. private static class CountingMembersInjector implements MembersInjector<D> {
  542. public void injectMembers(D instance) {
  543. ++instance.userInjected;
  544. }
  545. }
  546. private static class CountingInjectionListener implements InjectionListener<D> {
  547. public void afterInjection(D injectee) {
  548. ++injectee.listenersNotified;
  549. }
  550. }
  551. private static class DuplicatingTypeListener implements TypeListener {
  552. int count = 0;
  553. @SuppressWarnings({"rawtypes", "unchecked"})
  554. public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
  555. ++count;
  556. MembersInjector membersInjector = new CountingMembersInjector();
  557. encounter.register(membersInjector);
  558. encounter.register(membersInjector);
  559. InjectionListener injectionListener = new CountingInjectionListener();
  560. encounter.register(injectionListener);
  561. encounter.register(injectionListener);
  562. }
  563. }
  564. public void testDeDuplicateTypeListeners() {
  565. final DuplicatingTypeListener typeListener = new DuplicatingTypeListener();
  566. Injector injector = Guice.createInjector(new AbstractModule() {
  567. @Override
  568. protected void configure() {
  569. bindListener(any(), typeListener);
  570. bindListener(only(new TypeLiteral<D>() {}), typeListener);
  571. }
  572. });
  573. D d = injector.getInstance(D.class);
  574. d.assertAllCounts(1);
  575. assertEquals(1, typeListener.count);
  576. }
  577. // TODO: recursively accessing a lookup should fail
  578. static class A {
  579. @Inject Injector injector;
  580. @Inject Stage stage;
  581. }
  582. static class B {}
  583. public static class C {
  584. public String buzz() {
  585. return "buzz";
  586. }
  587. public String beep() {
  588. return "beep";
  589. }
  590. }
  591. static class D {
  592. int guiceInjected = 0;
  593. int userInjected = 0;
  594. int listenersNotified = 0;
  595. @Inject void guiceInjected() {
  596. guiceInjected++;
  597. }
  598. void assertAllCounts(int expected) {
  599. assertEquals(expected, guiceInjected);
  600. assertEquals(expected, userInjected);
  601. assertEquals(expected, listenersNotified);
  602. }
  603. }
  604. }