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