• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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;
18 
19 import static com.google.common.collect.ImmutableList.of;
20 import static com.google.common.truth.Truth.assertThat;
21 import static com.google.inject.Asserts.assertContains;
22 import static com.google.inject.name.Names.named;
23 
24 import com.google.common.collect.ImmutableList;
25 import com.google.common.collect.ImmutableSet;
26 import com.google.common.collect.Iterables;
27 import com.google.common.collect.Lists;
28 import com.google.inject.matcher.AbstractMatcher;
29 import com.google.inject.matcher.Matcher;
30 import com.google.inject.matcher.Matchers;
31 import com.google.inject.name.Named;
32 import com.google.inject.spi.InstanceBinding;
33 import com.google.inject.spi.ProvisionListener;
34 import com.google.inject.util.Providers;
35 import java.util.List;
36 import java.util.Set;
37 import java.util.concurrent.atomic.AtomicBoolean;
38 import java.util.concurrent.atomic.AtomicInteger;
39 import java.util.concurrent.atomic.AtomicReference;
40 import junit.framework.TestCase;
41 
42 /**
43  * Tests for {@link Binder#bindListener(Matcher, ProvisionListener...)}
44  *
45  * @author sameb@google.com (Sam Berlin)
46  */
47 // TODO(sameb): Add some tests for private modules & child injectors.
48 public class ProvisionListenerTest extends TestCase {
49 
testExceptionInListenerBeforeProvisioning()50   public void testExceptionInListenerBeforeProvisioning() {
51     Injector injector =
52         Guice.createInjector(
53             new AbstractModule() {
54               @Override
55               protected void configure() {
56                 bindListener(Matchers.any(), new FailBeforeProvision());
57               }
58             });
59     try {
60       injector.getInstance(Foo.class);
61       fail();
62     } catch (ProvisionException pe) {
63       assertEquals(1, pe.getErrorMessages().size());
64       assertContains(
65           pe.getMessage(),
66           "1) Error notifying ProvisionListener "
67               + FailBeforeProvision.class.getName()
68               + " of "
69               + Foo.class.getName(),
70           "Reason: java.lang.RuntimeException: boo",
71           "while locating " + Foo.class.getName());
72       assertEquals("boo", pe.getCause().getMessage());
73     }
74   }
75 
testExceptionInListenerAfterProvisioning()76   public void testExceptionInListenerAfterProvisioning() {
77     Injector injector =
78         Guice.createInjector(
79             new AbstractModule() {
80               @Override
81               protected void configure() {
82                 bindListener(Matchers.any(), new FailAfterProvision());
83               }
84             });
85     try {
86       injector.getInstance(Foo.class);
87       fail();
88     } catch (ProvisionException pe) {
89       assertEquals(1, pe.getErrorMessages().size());
90       assertContains(
91           pe.getMessage(),
92           "1) Error notifying ProvisionListener "
93               + FailAfterProvision.class.getName()
94               + " of "
95               + Foo.class.getName(),
96           "Reason: java.lang.RuntimeException: boo",
97           "while locating " + Foo.class.getName());
98       assertEquals("boo", pe.getCause().getMessage());
99     }
100   }
101 
testExceptionInProvisionExplicitlyCalled()102   public void testExceptionInProvisionExplicitlyCalled() {
103     Injector injector =
104         Guice.createInjector(
105             new AbstractModule() {
106               @Override
107               protected void configure() {
108                 bindListener(Matchers.any(), new JustProvision());
109               }
110             });
111     try {
112       injector.getInstance(FooBomb.class);
113       fail();
114     } catch (ProvisionException pe) {
115       assertEquals(1, pe.getErrorMessages().size());
116       assertContains(
117           pe.getMessage(),
118           "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
119           " at " + FooBomb.class.getName(),
120           " while locating " + FooBomb.class.getName());
121       assertEquals("Retry, Abort, Fail", pe.getCause().getMessage());
122     }
123   }
124 
testExceptionInProvisionAutomaticallyCalled()125   public void testExceptionInProvisionAutomaticallyCalled() {
126     Injector injector =
127         Guice.createInjector(
128             new AbstractModule() {
129               @Override
130               protected void configure() {
131                 bindListener(Matchers.any(), new NoProvision());
132               }
133             });
134     try {
135       injector.getInstance(FooBomb.class);
136       fail();
137     } catch (ProvisionException pe) {
138       assertEquals(1, pe.getErrorMessages().size());
139       assertContains(
140           pe.getMessage(),
141           "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
142           " at " + FooBomb.class.getName(),
143           " while locating " + FooBomb.class.getName());
144       assertEquals("Retry, Abort, Fail", pe.getCause().getMessage());
145     }
146   }
147 
testExceptionInFieldProvision()148   public void testExceptionInFieldProvision() throws Exception {
149     final CountAndCaptureExceptionListener listener = new CountAndCaptureExceptionListener();
150     Injector injector =
151         Guice.createInjector(
152             new AbstractModule() {
153               @Override
154               protected void configure() {
155                 bindListener(
156                     new AbstractMatcher<Binding<?>>() {
157                       @Override
158                       public boolean matches(Binding<?> binding) {
159                         return binding.getKey().getRawType().equals(DependsOnFooBombInField.class);
160                       }
161                     },
162                     listener);
163               }
164             });
165     assertEquals(0, listener.beforeProvision);
166     String expectedMsg = null;
167     try {
168       injector.getInstance(DependsOnFooBombInField.class);
169       fail();
170     } catch (ProvisionException expected) {
171       assertEquals(1, expected.getErrorMessages().size());
172       expectedMsg = Iterables.getOnlyElement(expected.getErrorMessages()).getMessage();
173       assertContains(
174           expected.getMessage(),
175           "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
176           " at " + FooBomb.class.getName(),
177           " while locating " + FooBomb.class.getName(),
178           " while locating " + DependsOnFooBombInField.class.getName());
179       assertContains(
180           listener.capture.get().getMessage(),
181           "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
182           " at " + FooBomb.class.getName(),
183           " while locating " + FooBomb.class.getName());
184       // The message that is captures by the provision listener does not show what is depending on
185       // the thing being listened to.
186       assertThat(listener.capture.get().getMessage())
187           .doesNotContain(" while locating " + DependsOnFooBombInField.class.getName());
188     }
189     assertEquals(1, listener.beforeProvision);
190     assertEquals(
191         expectedMsg,
192         Iterables.getOnlyElement(((ProvisionException) listener.capture.get()).getErrorMessages())
193             .getMessage());
194     assertEquals(0, listener.afterProvision);
195   }
196 
testExceptionInCxtorProvision()197   public void testExceptionInCxtorProvision() throws Exception {
198     final CountAndCaptureExceptionListener listener = new CountAndCaptureExceptionListener();
199     Injector injector =
200         Guice.createInjector(
201             new AbstractModule() {
202               @Override
203               protected void configure() {
204                 bindListener(
205                     new AbstractMatcher<Binding<?>>() {
206                       @Override
207                       public boolean matches(Binding<?> binding) {
208                         return binding.getKey().getRawType().equals(DependsOnFooBombInCxtor.class);
209                       }
210                     },
211                     listener);
212               }
213             });
214     assertEquals(0, listener.beforeProvision);
215     String expectedMsg = null;
216     try {
217       injector.getInstance(DependsOnFooBombInCxtor.class);
218       fail();
219     } catch (ProvisionException expected) {
220       assertEquals(1, expected.getErrorMessages().size());
221       expectedMsg = Iterables.getOnlyElement(expected.getErrorMessages()).getMessage();
222       assertContains(
223           expected.getMessage(),
224           "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
225           " at " + FooBomb.class.getName(),
226           " while locating " + FooBomb.class.getName(),
227           " while locating " + DependsOnFooBombInCxtor.class.getName());
228       assertContains(
229           listener.capture.get().getMessage(),
230           "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail",
231           " at " + FooBomb.class.getName(),
232           " while locating " + FooBomb.class.getName());
233       // The message that is captures by the provision listener does not show what is depending on
234       // the thing being listened to.
235       assertThat(listener.capture.get().getMessage())
236           .doesNotContain(" while locating " + DependsOnFooBombInField.class.getName());
237     }
238     assertEquals(1, listener.beforeProvision);
239     assertEquals(
240         expectedMsg,
241         Iterables.getOnlyElement(((ProvisionException) listener.capture.get()).getErrorMessages())
242             .getMessage());
243     assertEquals(0, listener.afterProvision);
244   }
245 
testListenerCallsProvisionTwice()246   public void testListenerCallsProvisionTwice() {
247     Injector injector =
248         Guice.createInjector(
249             new AbstractModule() {
250               @Override
251               protected void configure() {
252                 bindListener(Matchers.any(), new ProvisionTwice());
253               }
254             });
255     try {
256       injector.getInstance(Foo.class);
257       fail();
258     } catch (ProvisionException pe) {
259       assertEquals(1, pe.getErrorMessages().size());
260       assertContains(
261           pe.getMessage(),
262           "1) Error notifying ProvisionListener "
263               + ProvisionTwice.class.getName()
264               + " of "
265               + Foo.class.getName(),
266           "Reason: java.lang.IllegalStateException: Already provisioned in this listener.",
267           "while locating " + Foo.class.getName());
268       assertEquals("Already provisioned in this listener.", pe.getCause().getMessage());
269     }
270   }
271 
testCachedInScopePreventsProvisionNotify()272   public void testCachedInScopePreventsProvisionNotify() {
273     final Counter count1 = new Counter();
274     Injector injector =
275         Guice.createInjector(
276             new AbstractModule() {
277               @Override
278               protected void configure() {
279                 bindListener(Matchers.any(), count1);
280                 bind(Foo.class).in(Scopes.SINGLETON);
281               }
282             });
283     Foo foo = injector.getInstance(Foo.class);
284     assertNotNull(foo);
285     assertEquals(1, count1.count);
286 
287     // not notified the second time because nothing is provisioned
288     // (it's cached in the scope)
289     count1.count = 0;
290     assertSame(foo, injector.getInstance(Foo.class));
291     assertEquals(0, count1.count);
292   }
293 
testCombineAllBindListenerCalls()294   public void testCombineAllBindListenerCalls() {
295     final Counter count1 = new Counter();
296     final Counter count2 = new Counter();
297     Injector injector =
298         Guice.createInjector(
299             new AbstractModule() {
300               @Override
301               protected void configure() {
302                 bindListener(Matchers.any(), count1);
303                 bindListener(Matchers.any(), count2);
304               }
305             });
306     assertNotNull(injector.getInstance(Foo.class));
307     assertEquals(1, count1.count);
308     assertEquals(1, count2.count);
309   }
310 
testNotifyEarlyListenersIfFailBeforeProvision()311   public void testNotifyEarlyListenersIfFailBeforeProvision() {
312     final Counter count1 = new Counter();
313     final Counter count2 = new Counter();
314     Injector injector =
315         Guice.createInjector(
316             new AbstractModule() {
317               @Override
318               protected void configure() {
319                 bindListener(Matchers.any(), count1, new FailBeforeProvision(), count2);
320               }
321             });
322     try {
323       injector.getInstance(Foo.class);
324       fail();
325     } catch (ProvisionException pe) {
326       assertEquals(1, pe.getErrorMessages().size());
327       assertContains(
328           pe.getMessage(),
329           "1) Error notifying ProvisionListener "
330               + FailBeforeProvision.class.getName()
331               + " of "
332               + Foo.class.getName(),
333           "Reason: java.lang.RuntimeException: boo",
334           "while locating " + Foo.class.getName());
335       assertEquals("boo", pe.getCause().getMessage());
336 
337       assertEquals(1, count1.count);
338       assertEquals(0, count2.count);
339     }
340   }
341 
testNotifyLaterListenersIfFailAfterProvision()342   public void testNotifyLaterListenersIfFailAfterProvision() {
343     final Counter count1 = new Counter();
344     final Counter count2 = new Counter();
345     Injector injector =
346         Guice.createInjector(
347             new AbstractModule() {
348               @Override
349               protected void configure() {
350                 bindListener(Matchers.any(), count1, new FailAfterProvision(), count2);
351               }
352             });
353     try {
354       injector.getInstance(Foo.class);
355       fail();
356     } catch (ProvisionException pe) {
357       assertEquals(1, pe.getErrorMessages().size());
358       assertContains(
359           pe.getMessage(),
360           "1) Error notifying ProvisionListener "
361               + FailAfterProvision.class.getName()
362               + " of "
363               + Foo.class.getName(),
364           "Reason: java.lang.RuntimeException: boo",
365           "while locating " + Foo.class.getName());
366       assertEquals("boo", pe.getCause().getMessage());
367 
368       assertEquals(1, count1.count);
369       assertEquals(1, count2.count);
370     }
371   }
372 
testNotifiedKeysOfAllBindTypes()373   public void testNotifiedKeysOfAllBindTypes() {
374     final Capturer capturer = new Capturer();
375     Injector injector =
376         Guice.createInjector(
377             new AbstractModule() {
378               @Override
379               protected void configure() {
380                 bindListener(Matchers.any(), capturer);
381                 bind(Foo.class).annotatedWith(named("pk")).toProvider(FooP.class);
382                 try {
383                   bind(Foo.class)
384                       .annotatedWith(named("cxtr"))
385                       .toConstructor(Foo.class.getDeclaredConstructor());
386                 } catch (Exception ex) {
387                   throw new RuntimeException(ex);
388                 }
389                 bind(LinkedFoo.class).to(Foo.class);
390                 bind(Interface.class).toInstance(new Implementation());
391                 bindConstant().annotatedWith(named("constant")).to("MyConstant");
392               }
393 
394               @Provides
395               @Named("pi")
396               Foo provideFooBar() {
397                 return new Foo();
398               }
399             });
400 
401     // toInstance & constant bindings are notified in random order, at the very beginning.
402     assertEquals(
403         ImmutableSet.of(Key.get(Interface.class), Key.get(String.class, named("constant"))),
404         capturer.getAsSetAndClear());
405 
406     // simple binding
407     assertNotNull(injector.getInstance(Foo.class));
408     assertEquals(of(Key.get(Foo.class)), capturer.getAndClear());
409 
410     // provider key binding -- notifies about provider & the object, always
411     assertNotNull(injector.getInstance(Key.get(Foo.class, named("pk"))));
412     assertEquals(of(Key.get(FooP.class), Key.get(Foo.class, named("pk"))), capturer.getAndClear());
413     assertNotNull(injector.getInstance(Key.get(Foo.class, named("pk"))));
414     assertEquals(of(Key.get(FooP.class), Key.get(Foo.class, named("pk"))), capturer.getAndClear());
415 
416     // JIT provider key binding -- notifies about provider & the object, always
417     assertNotNull(injector.getInstance(JitFoo2.class));
418     assertEquals(of(Key.get(JitFoo2P.class), Key.get(JitFoo2.class)), capturer.getAndClear());
419     assertNotNull(injector.getInstance(JitFoo2.class));
420     assertEquals(of(Key.get(JitFoo2P.class), Key.get(JitFoo2.class)), capturer.getAndClear());
421 
422     // provider instance binding -- just the object (not the provider)
423     assertNotNull(injector.getInstance(Key.get(Foo.class, named("pi"))));
424     assertEquals(of(Key.get(Foo.class, named("pi"))), capturer.getAndClear());
425 
426     // toConstructor binding
427     assertNotNull(injector.getInstance(Key.get(Foo.class, named("cxtr"))));
428     assertEquals(of(Key.get(Foo.class, named("cxtr"))), capturer.getAndClear());
429 
430     // linked binding -- notifies about the target (that's what's provisioned), not the link
431     assertNotNull(injector.getInstance(LinkedFoo.class));
432     assertEquals(of(Key.get(Foo.class)), capturer.getAndClear());
433 
434     // JIT linked binding -- notifies about the target (that's what's provisioned), not the link
435     assertNotNull(injector.getInstance(JitFoo.class));
436     assertEquals(of(Key.get(Foo.class)), capturer.getAndClear());
437   }
438 
testSingletonMatcher()439   public void testSingletonMatcher() {
440     final Counter counter = new Counter();
441     Injector injector =
442         Guice.createInjector(
443             new AbstractModule() {
444               @Override
445               protected void configure() {
446                 bindListener(
447                     new AbstractMatcher<Binding<?>>() {
448                       @Override
449                       public boolean matches(Binding<?> t) {
450                         return Scopes.isSingleton(t);
451                       }
452                     },
453                     counter);
454               }
455             });
456     assertEquals(0, counter.count);
457     // no increment for getting Many.
458     injector.getInstance(Many.class);
459     assertEquals(0, counter.count);
460     // but an increment for getting Sole, since it's a singleton.
461     injector.getInstance(Sole.class);
462     assertEquals(1, counter.count);
463   }
464 
testCallingBindingDotGetProviderDotGet()465   public void testCallingBindingDotGetProviderDotGet() {
466     Injector injector =
467         Guice.createInjector(
468             new AbstractModule() {
469               @Override
470               protected void configure() {
471                 bindListener(
472                     Matchers.any(),
473                     new ProvisionListener() {
474                       @Override
475                       public <T> void onProvision(ProvisionInvocation<T> provision) {
476                         provision.getBinding().getProvider().get(); // AGH!
477                       }
478                     });
479               }
480             });
481 
482     try {
483       injector.getInstance(Sole.class);
484       fail();
485     } catch (ProvisionException expected) {
486       // We don't really care what kind of error you get, we only care you get an error.
487     }
488 
489     try {
490       injector.getInstance(Many.class);
491       fail();
492     } catch (ProvisionException expected) {
493       // We don't really care what kind of error you get, we only care you get an error.
494     }
495   }
496 
497   interface Interface {}
498 
499   static class Implementation implements Interface {}
500 
501   @Singleton
502   static class Sole {}
503 
504   static class Many {}
505 
506   @ImplementedBy(Foo.class)
507   static interface JitFoo {}
508 
509   @ProvidedBy(JitFoo2P.class)
510   static class JitFoo2 {}
511 
512   static interface LinkedFoo {}
513 
514   static class Foo implements JitFoo, LinkedFoo {}
515 
516   static class FooP implements Provider<Foo> {
517     @Override
get()518     public Foo get() {
519       return new Foo();
520     }
521   }
522 
523   static class JitFoo2P implements Provider<JitFoo2> {
524     @Override
get()525     public JitFoo2 get() {
526       return new JitFoo2();
527     }
528   }
529 
530   static class FooBomb {
FooBomb()531     FooBomb() {
532       throw new RuntimeException("Retry, Abort, Fail");
533     }
534   }
535 
536   static class DependsOnFooBombInField {
537     @Inject FooBomb fooBomb;
538   }
539 
540   static class DependsOnFooBombInCxtor {
541     @Inject
DependsOnFooBombInCxtor(FooBomb fooBomb)542     DependsOnFooBombInCxtor(FooBomb fooBomb) {}
543   }
544 
545   private static class Counter implements ProvisionListener {
546     int count = 0;
547 
548     @Override
onProvision(ProvisionInvocation<T> provision)549     public <T> void onProvision(ProvisionInvocation<T> provision) {
550       count++;
551     }
552   }
553 
554   private static class CountAndCaptureExceptionListener implements ProvisionListener {
555     int beforeProvision = 0;
556     int afterProvision = 0;
557     AtomicReference<RuntimeException> capture = new AtomicReference<>();
558 
559     @Override
onProvision(ProvisionInvocation<T> provision)560     public <T> void onProvision(ProvisionInvocation<T> provision) {
561       beforeProvision++;
562       try {
563         provision.provision();
564       } catch (RuntimeException re) {
565         capture.set(re);
566         throw re;
567       }
568       afterProvision++;
569     }
570   }
571 
572   private static class Capturer implements ProvisionListener {
573     List<Key> keys = Lists.newArrayList();
574 
575     @Override
onProvision(ProvisionInvocation<T> provision)576     public <T> void onProvision(ProvisionInvocation<T> provision) {
577       keys.add(provision.getBinding().getKey());
578       T provisioned = provision.provision();
579       // InstanceBindings are the only kind of binding where the key can
580       // be an instanceof the provisioned, because it isn't linked to any
581       // direct implementation.  I guess maybe it'd also be possible
582       // with a toConstructor binding... but we don't use that in our tests.
583       if (provision.getBinding() instanceof InstanceBinding) {
584         Class<? super T> expected = provision.getBinding().getKey().getRawType();
585         assertTrue(
586             "expected instanceof: " + expected + ", but was: " + provisioned,
587             expected.isInstance(provisioned));
588       } else {
589         assertEquals(provision.getBinding().getKey().getRawType(), provisioned.getClass());
590       }
591     }
592 
getAsSetAndClear()593     Set<Key> getAsSetAndClear() {
594       Set<Key> copy = ImmutableSet.copyOf(keys);
595       keys.clear();
596       return copy;
597     }
598 
getAndClear()599     List<Key> getAndClear() {
600       List<Key> copy = ImmutableList.copyOf(keys);
601       keys.clear();
602       return copy;
603     }
604   }
605 
606   private static class FailBeforeProvision implements ProvisionListener {
607     @Override
onProvision(ProvisionInvocation<T> provision)608     public <T> void onProvision(ProvisionInvocation<T> provision) {
609       throw new RuntimeException("boo");
610     }
611   }
612 
613   private static class FailAfterProvision implements ProvisionListener {
614     @Override
onProvision(ProvisionInvocation<T> provision)615     public <T> void onProvision(ProvisionInvocation<T> provision) {
616       provision.provision();
617       throw new RuntimeException("boo");
618     }
619   }
620 
621   private static class JustProvision implements ProvisionListener {
622     @Override
onProvision(ProvisionInvocation<T> provision)623     public <T> void onProvision(ProvisionInvocation<T> provision) {
624       provision.provision();
625     }
626   }
627 
628   private static class NoProvision implements ProvisionListener {
629     @Override
onProvision(ProvisionInvocation<T> provision)630     public <T> void onProvision(ProvisionInvocation<T> provision) {}
631   }
632 
633   private static class ProvisionTwice implements ProvisionListener {
634     @Override
onProvision(ProvisionInvocation<T> provision)635     public <T> void onProvision(ProvisionInvocation<T> provision) {
636       provision.provision();
637       provision.provision();
638     }
639   }
640 
641   private static class ChainAsserter implements ProvisionListener {
642     private final List<Class<?>> provisionList;
643 
644     private final List<Class<?>> expected;
645 
ChainAsserter(List<Class<?>> provisionList, Iterable<Class<?>> expected)646     public ChainAsserter(List<Class<?>> provisionList, Iterable<Class<?>> expected) {
647       this.provisionList = provisionList;
648       this.expected = ImmutableList.copyOf(expected);
649     }
650 
651     @Override
onProvision(ProvisionInvocation<T> provision)652     public <T> void onProvision(ProvisionInvocation<T> provision) {
653       List<Class<?>> actual = Lists.newArrayList();
654       for (com.google.inject.spi.DependencyAndSource dep : provision.getDependencyChain()) {
655         actual.add(dep.getDependency().getKey().getRawType());
656       }
657       assertEquals(expected, actual);
658 
659       provisionList.add(provision.getBinding().getKey().getRawType());
660     }
661   }
662 
keyMatcher(final Class<?> clazz)663   private static Matcher<Binding<?>> keyMatcher(final Class<?> clazz) {
664     return new AbstractMatcher<Binding<?>>() {
665       @Override
666       public boolean matches(Binding<?> t) {
667         return t.getKey().equals(Key.get(clazz));
668       }
669     };
670   }
671 
672   @SuppressWarnings("unchecked")
673   public void testDependencyChain() {
674     final List<Class<?>> pList = Lists.newArrayList();
675     final List<Class<?>> totalList = Lists.newArrayList();
676     Injector injector =
677         Guice.createInjector(
678             new AbstractModule() {
679               @Override
680               protected void configure() {
681                 bind(Instance.class).toInstance(new Instance());
682                 bind(B.class).to(BImpl.class);
683                 bind(D.class).toProvider(DP.class);
684 
685                 bindListener(
686                     Matchers.any(),
687                     new ProvisionListener() {
688                       @Override
689                       public <T> void onProvision(ProvisionInvocation<T> provision) {
690                         totalList.add(provision.getBinding().getKey().getRawType());
691                       }
692                     });
693 
694                 // Build up a list of asserters for our dependency chains.
695                 ImmutableList.Builder<Class<?>> chain = ImmutableList.builder();
696                 chain.add(Instance.class);
697                 bindListener(keyMatcher(Instance.class), new ChainAsserter(pList, chain.build()));
698 
699                 chain.add(A.class);
700                 bindListener(keyMatcher(A.class), new ChainAsserter(pList, chain.build()));
701 
702                 chain.add(B.class).add(BImpl.class);
703                 bindListener(keyMatcher(BImpl.class), new ChainAsserter(pList, chain.build()));
704 
705                 chain.add(C.class);
706                 bindListener(keyMatcher(C.class), new ChainAsserter(pList, chain.build()));
707 
708                 // the chain has D before DP even though DP is provisioned & notified first
709                 // because we do DP because of D, and need DP to provision D.
710                 chain.add(D.class).add(DP.class);
711                 bindListener(keyMatcher(D.class), new ChainAsserter(pList, chain.build()));
712                 bindListener(keyMatcher(DP.class), new ChainAsserter(pList, chain.build()));
713 
714                 chain.add(E.class);
715                 bindListener(keyMatcher(E.class), new ChainAsserter(pList, chain.build()));
716 
717                 chain.add(F.class);
718                 bindListener(keyMatcher(F.class), new ChainAsserter(pList, chain.build()));
719               }
720 
721               @Provides
722               C c(D d) {
723                 return new C() {};
724               }
725             });
726     injector.getInstance(Instance.class);
727     // make sure we're checking all of the chain asserters..
728     assertEquals(
729         of(Instance.class, A.class, BImpl.class, C.class, DP.class, D.class, E.class, F.class),
730         pList);
731     // and make sure that nothing else was notified that we didn't expect.
732     assertEquals(totalList, pList);
733   }
734 
735   public void testModuleRequestInjection() {
736     final AtomicBoolean notified = new AtomicBoolean();
737     Guice.createInjector(
738         new AbstractModule() {
739           @Override
740           protected void configure() {
741             requestInjection(
742                 new Object() {
743                   @Inject Foo foo;
744                 });
745             bindListener(
746                 Matchers.any(),
747                 new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified));
748           }
749         });
750     assertTrue(notified.get());
751   }
752 
753   public void testToProviderInstance() {
754     final AtomicBoolean notified = new AtomicBoolean();
755     Guice.createInjector(
756         new AbstractModule() {
757           @Override
758           protected void configure() {
759             bind(Object.class)
760                 .toProvider(
761                     new Provider<Object>() {
762                       @Inject Foo foo;
763 
764                       @Override
765                       public Object get() {
766                         return null;
767                       }
768                     });
769             bindListener(
770                 Matchers.any(),
771                 new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified));
772           }
773         });
774     assertTrue(notified.get());
775   }
776 
777   public void testInjectorInjectMembers() {
778     final Object object =
779         new Object() {
780           @Inject Foo foo;
781         };
782     final AtomicBoolean notified = new AtomicBoolean();
783     Guice.createInjector(
784             new AbstractModule() {
785               @Override
786               protected void configure() {
787                 bindListener(
788                     Matchers.any(),
789                     new SpecialChecker(Foo.class, object.getClass().getName(), notified));
790               }
791             })
792         .injectMembers(object);
793     assertTrue(notified.get());
794   }
795 
796   private static class SpecialChecker implements ProvisionListener {
797     private final Class<?> notifyType;
798 
799     private final String firstSource;
800 
801     private final AtomicBoolean notified;
802 
803     public SpecialChecker(Class<?> notifyType, String firstSource, AtomicBoolean notified) {
804       this.notifyType = notifyType;
805       this.firstSource = firstSource;
806       this.notified = notified;
807     }
808 
809     @Override
810     public <T> void onProvision(ProvisionInvocation<T> provision) {
811       notified.set(true);
812       assertEquals(notifyType, provision.getBinding().getKey().getRawType());
813       assertEquals(2, provision.getDependencyChain().size());
814 
815       assertNull(provision.getDependencyChain().get(0).getDependency());
816       assertContains(provision.getDependencyChain().get(0).getBindingSource(), firstSource);
817 
818       assertEquals(
819           notifyType, provision.getDependencyChain().get(1).getDependency().getKey().getRawType());
820       assertContains(
821           provision.getDependencyChain().get(1).getBindingSource(),
822           notifyType.getName() + ".class(");
823 
824     }
825   }
826 
827   private static class Instance {
828     @Inject A a;
829   }
830 
831   private static class A {
832     @Inject
833     A(B b) {}
834   }
835 
836   private interface B {}
837 
838   private static class BImpl implements B {
839     @Inject
840     void inject(C c) {}
841   }
842 
843   private interface C {}
844 
845   private interface D {}
846 
847   private static class DP implements Provider<D> {
848     @Inject Provider<E> ep;
849 
850     @Override
851     public D get() {
852       ep.get();
853       return new D() {};
854     }
855   }
856 
857   private static class E {
858     @SuppressWarnings("unused")
859     @Inject
860     F f;
861   }
862 
863   private static class F {}
864 
865   public void testBindToInjectorWithListeningGivesSaneException() {
866     try {
867       Guice.createInjector(
868           new AbstractModule() {
869             @Override
870             protected void configure() {
871               bindListener(Matchers.any(), new Counter());
872               bind(Injector.class).toProvider(Providers.<Injector>of(null));
873             }
874           });
875       fail();
876     } catch (CreationException ce) {
877       assertContains(
878           ce.getMessage(), "Binding to core guice framework type is not allowed: Injector.");
879     }
880   }
881 
882   public void testProvisionIsNotifiedAfterContextsClear() {
883     Injector injector =
884         Guice.createInjector(
885             new AbstractModule() {
886               @Override
887               protected void configure() {
888                 bindListener(
889                     Matchers.any(),
890                     new ProvisionListener() {
891                       @Override
892                       public <T> void onProvision(ProvisionInvocation<T> provision) {
893                         Object provisioned = provision.provision();
894                         if (provisioned instanceof X) {
895                           ((X) provisioned).init();
896                         } else if (provisioned instanceof Y) {
897                           X.createY = false;
898                           ((Y) provisioned).init();
899                         }
900                       }
901                     });
902               }
903             });
904 
905     X.createY = true;
906     X x = injector.getInstance(X.class);
907     assertNotSame(x, x.y.x);
908     assertFalse("x.id: " + x.id + ", x.y.x.id: " + x.y.x.id, x.id == x.y.x.id);
909   }
910 
911   private static class X {
912     static final AtomicInteger COUNTER = new AtomicInteger();
913     static boolean createY;
914 
915     final int id = COUNTER.getAndIncrement();
916     final Provider<Y> yProvider;
917     Y y;
918 
919     @Inject
920     X(Provider<Y> yProvider) {
921       this.yProvider = yProvider;
922     }
923 
924     void init() {
925       if (createY) {
926         this.y = yProvider.get();
927       }
928     }
929   }
930 
931   private static class Y {
932     final Provider<X> xProvider;
933     X x;
934 
935     @Inject
936     Y(Provider<X> xProvider) {
937       this.xProvider = xProvider;
938     }
939 
940     void init() {
941       this.x = xProvider.get();
942     }
943   }
944 
945   public void testDeDuplicateProvisionListeners() {
946     final Counter counter = new Counter();
947     Injector injector =
948         Guice.createInjector(
949             new AbstractModule() {
950               @Override
951               protected void configure() {
952                 bindListener(Matchers.any(), counter);
953                 bindListener(Matchers.any(), counter);
954               }
955             });
956     injector.getInstance(Many.class);
957     assertEquals("ProvisionListener not de-duplicated", 1, counter.count);
958   }
959 }
960