Home | History | Annotate | Download | only in inject
      1 /*
      2  * Copyright (C) 2010 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.inject.Asserts.*;
     20 import static com.google.inject.name.Names.named;
     21 
     22 import com.google.common.base.Objects;
     23 import com.google.common.collect.Lists;
     24 import com.google.inject.name.Named;
     25 import com.google.inject.spi.Element;
     26 import com.google.inject.spi.Elements;
     27 import com.google.inject.util.Providers;
     28 import java.lang.annotation.Annotation;
     29 import java.lang.reflect.Constructor;
     30 import java.util.Arrays;
     31 import java.util.Collection;
     32 import java.util.LinkedHashSet;
     33 import java.util.List;
     34 import java.util.logging.Logger;
     35 import junit.framework.TestCase;
     36 
     37 /**
     38  * A suite of tests for duplicate bindings.
     39  *
     40  * @author sameb (at) google.com (Sam Berlin)
     41  */
     42 public class DuplicateBindingsTest extends TestCase {
     43 
     44   private FooImpl foo = new FooImpl();
     45   private Provider<Foo> pFoo = Providers.<Foo>of(new FooImpl());
     46   private Class<? extends Provider<? extends Foo>> pclFoo = FooProvider.class;
     47   private Class<? extends Foo> clFoo = FooImpl.class;
     48   private Constructor<FooImpl> cFoo = FooImpl.cxtor();
     49 
     50   public void testDuplicateBindingsAreIgnored() {
     51     Injector injector =
     52         Guice.createInjector(
     53             new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
     54             new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo));
     55     List<Key<?>> bindings = Lists.newArrayList(injector.getAllBindings().keySet());
     56     removeBasicBindings(bindings);
     57 
     58     // Ensure only one binding existed for each type.
     59     assertTrue(bindings.remove(Key.get(Foo.class, named("instance"))));
     60     assertTrue(bindings.remove(Key.get(Foo.class, named("pInstance"))));
     61     assertTrue(bindings.remove(Key.get(Foo.class, named("pKey"))));
     62     assertTrue(bindings.remove(Key.get(Foo.class, named("linkedKey"))));
     63     assertTrue(bindings.remove(Key.get(FooImpl.class)));
     64     assertTrue(bindings.remove(Key.get(Foo.class, named("constructor"))));
     65     assertTrue(bindings.remove(Key.get(FooProvider.class))); // JIT binding
     66     assertTrue(bindings.remove(Key.get(Foo.class, named("providerMethod"))));
     67 
     68     assertEquals(bindings.toString(), 0, bindings.size());
     69   }
     70 
     71   public void testElementsDeduplicate() {
     72     List<Element> elements =
     73         Elements.getElements(
     74             new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
     75             new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo));
     76     assertEquals(14, elements.size());
     77     assertEquals(7, new LinkedHashSet<Element>(elements).size());
     78   }
     79 
     80   public void testProviderMethodsFailIfInstancesDiffer() {
     81     try {
     82       Guice.createInjector(new FailingProviderModule(), new FailingProviderModule());
     83       fail("should have failed");
     84     } catch (CreationException ce) {
     85       assertContains(
     86           ce.getMessage(),
     87           "A binding to "
     88               + Foo.class.getName()
     89               + " was already configured "
     90               + "at "
     91               + FailingProviderModule.class.getName(),
     92           "at " + FailingProviderModule.class.getName());
     93     }
     94   }
     95 
     96   public void testSameScopeInstanceIgnored() {
     97     Guice.createInjector(
     98         new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo),
     99         new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo));
    100 
    101     Guice.createInjector(
    102         new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo),
    103         new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo));
    104   }
    105 
    106   public void testSameScopeAnnotationIgnored() {
    107     Guice.createInjector(
    108         new AnnotatedScopeModule(Singleton.class, foo, pFoo, pclFoo, clFoo, cFoo),
    109         new AnnotatedScopeModule(Singleton.class, foo, pFoo, pclFoo, clFoo, cFoo));
    110   }
    111 
    112   public void testMixedAnnotationAndScopeForSingletonIgnored() {
    113     Guice.createInjector(
    114         new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo),
    115         new AnnotatedScopeModule(Singleton.class, foo, pFoo, pclFoo, clFoo, cFoo));
    116   }
    117 
    118   public void testMixedScopeAndUnscopedIgnored() {
    119     Guice.createInjector(
    120         new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
    121         new ScopedModule(Scopes.NO_SCOPE, foo, pFoo, pclFoo, clFoo, cFoo));
    122   }
    123 
    124   public void testMixedScopeFails() {
    125     try {
    126       Guice.createInjector(
    127           new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
    128           new ScopedModule(Scopes.SINGLETON, foo, pFoo, pclFoo, clFoo, cFoo));
    129       fail("expected exception");
    130     } catch (CreationException ce) {
    131       String segment1 =
    132           "A binding to "
    133               + Foo.class.getName()
    134               + " annotated with "
    135               + named("pInstance")
    136               + " was already configured at "
    137               + SimpleModule.class.getName();
    138       String segment2 =
    139           "A binding to "
    140               + Foo.class.getName()
    141               + " annotated with "
    142               + named("pKey")
    143               + " was already configured at "
    144               + SimpleModule.class.getName();
    145       String segment3 =
    146           "A binding to "
    147               + Foo.class.getName()
    148               + " annotated with "
    149               + named("constructor")
    150               + " was already configured at "
    151               + SimpleModule.class.getName();
    152       String segment4 =
    153           "A binding to "
    154               + FooImpl.class.getName()
    155               + " was already configured at "
    156               + SimpleModule.class.getName();
    157       String atSegment = "at " + ScopedModule.class.getName();
    158       if (isIncludeStackTraceOff()) {
    159         assertContains(
    160             ce.getMessage(),
    161             segment1,
    162             atSegment,
    163             segment2,
    164             atSegment,
    165             segment3,
    166             atSegment,
    167             segment4,
    168             atSegment);
    169       } else {
    170         assertContains(
    171             ce.getMessage(),
    172             segment1,
    173             atSegment,
    174             segment2,
    175             atSegment,
    176             segment4,
    177             atSegment,
    178             segment3,
    179             atSegment);
    180       }
    181     }
    182   }
    183 
    184   @SuppressWarnings("unchecked")
    185   public void testMixedTargetsFails() {
    186     try {
    187       Guice.createInjector(
    188           new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
    189           new SimpleModule(
    190               new FooImpl(),
    191               Providers.<Foo>of(new FooImpl()),
    192               (Class) BarProvider.class,
    193               (Class) Bar.class,
    194               (Constructor) Bar.cxtor()));
    195       fail("expected exception");
    196     } catch (CreationException ce) {
    197       assertContains(
    198           ce.getMessage(),
    199           "A binding to "
    200               + Foo.class.getName()
    201               + " annotated with "
    202               + named("pInstance")
    203               + " was already configured at "
    204               + SimpleModule.class.getName(),
    205           "at " + SimpleModule.class.getName(),
    206           "A binding to "
    207               + Foo.class.getName()
    208               + " annotated with "
    209               + named("pKey")
    210               + " was already configured at "
    211               + SimpleModule.class.getName(),
    212           "at " + SimpleModule.class.getName(),
    213           "A binding to "
    214               + Foo.class.getName()
    215               + " annotated with "
    216               + named("linkedKey")
    217               + " was already configured at "
    218               + SimpleModule.class.getName(),
    219           "at " + SimpleModule.class.getName(),
    220           "A binding to "
    221               + Foo.class.getName()
    222               + " annotated with "
    223               + named("constructor")
    224               + " was already configured at "
    225               + SimpleModule.class.getName(),
    226           "at " + SimpleModule.class.getName());
    227     }
    228   }
    229 
    230   public void testExceptionInEqualsThrowsCreationException() {
    231     try {
    232       Guice.createInjector(new ThrowingModule(), new ThrowingModule());
    233       fail("expected exception");
    234     } catch (CreationException ce) {
    235       assertContains(
    236           ce.getMessage(),
    237           "A binding to "
    238               + Foo.class.getName()
    239               + " was already configured at "
    240               + ThrowingModule.class.getName(),
    241           "and an error was thrown while checking duplicate bindings.  Error: java.lang.RuntimeException: Boo!",
    242           "at " + ThrowingModule.class.getName());
    243     }
    244   }
    245 
    246   public void testChildInjectorDuplicateParentFail() {
    247     Injector injector = Guice.createInjector(new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo));
    248 
    249     try {
    250       injector.createChildInjector(new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo));
    251       fail("expected exception");
    252     } catch (CreationException ce) {
    253       assertContains(
    254           ce.getMessage(),
    255           "A binding to "
    256               + Foo.class.getName()
    257               + " annotated with "
    258               + named("pInstance")
    259               + " was already configured at "
    260               + SimpleModule.class.getName(),
    261           "at " + SimpleModule.class.getName(),
    262           "A binding to "
    263               + Foo.class.getName()
    264               + " annotated with "
    265               + named("pKey")
    266               + " was already configured at "
    267               + SimpleModule.class.getName(),
    268           "at " + SimpleModule.class.getName(),
    269           "A binding to "
    270               + Foo.class.getName()
    271               + " annotated with "
    272               + named("linkedKey")
    273               + " was already configured at "
    274               + SimpleModule.class.getName(),
    275           "at " + SimpleModule.class.getName(),
    276           "A binding to "
    277               + Foo.class.getName()
    278               + " annotated with "
    279               + named("constructor")
    280               + " was already configured at "
    281               + SimpleModule.class.getName(),
    282           "at " + SimpleModule.class.getName(),
    283           "A binding to "
    284               + Foo.class.getName()
    285               + " annotated with "
    286               + named("providerMethod")
    287               + " was already configured at "
    288               + SimpleProviderModule.class.getName(),
    289           "at " + SimpleProviderModule.class.getName());
    290     }
    291   }
    292 
    293   public void testDuplicatesSolelyInChildIgnored() {
    294     Injector injector = Guice.createInjector();
    295     injector.createChildInjector(
    296         new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo),
    297         new SimpleModule(foo, pFoo, pclFoo, clFoo, cFoo));
    298   }
    299 
    300   public void testDifferentBindingTypesFail() {
    301     List<Element> elements = Elements.getElements(new FailedModule(foo, pFoo, pclFoo, clFoo, cFoo));
    302 
    303     // Make sure every combination of the elements with another element fails.
    304     // This ensures that duplication checks the kind of binding also.
    305     for (Element e1 : elements) {
    306       for (Element e2 : elements) {
    307         // if they're the same, this shouldn't fail.
    308         try {
    309           Guice.createInjector(Elements.getModule(Arrays.asList(e1, e2)));
    310           if (e1 != e2) {
    311             fail("must fail!");
    312           }
    313         } catch (CreationException expected) {
    314           if (e1 != e2) {
    315             assertContains(
    316                 expected.getMessage(),
    317                 "A binding to "
    318                     + Foo.class.getName()
    319                     + " was already configured at "
    320                     + FailedModule.class.getName(),
    321                 "at " + FailedModule.class.getName());
    322           } else {
    323             throw expected;
    324           }
    325         }
    326       }
    327     }
    328   }
    329 
    330   public void testJitBindingsAreCheckedAfterConversions() {
    331     Guice.createInjector(
    332         new AbstractModule() {
    333           @Override
    334           protected void configure() {
    335             bind(A.class);
    336             bind(A.class).to(RealA.class);
    337           }
    338         });
    339   }
    340 
    341   public void testEqualsNotCalledByDefaultOnInstance() {
    342     final HashEqualsTester a = new HashEqualsTester();
    343     a.throwOnEquals = true;
    344     Guice.createInjector(
    345         new AbstractModule() {
    346           @Override
    347           protected void configure() {
    348             bind(String.class);
    349             bind(HashEqualsTester.class).toInstance(a);
    350           }
    351         });
    352   }
    353 
    354   public void testEqualsNotCalledByDefaultOnProvider() {
    355     final HashEqualsTester a = new HashEqualsTester();
    356     a.throwOnEquals = true;
    357     Guice.createInjector(
    358         new AbstractModule() {
    359           @Override
    360           protected void configure() {
    361             bind(String.class);
    362             bind(Object.class).toProvider(a);
    363           }
    364         });
    365   }
    366 
    367   public void testHashcodeNeverCalledOnInstance() {
    368     final HashEqualsTester a = new HashEqualsTester();
    369     a.throwOnHashcode = true;
    370     a.equality = "test";
    371 
    372     final HashEqualsTester b = new HashEqualsTester();
    373     b.throwOnHashcode = true;
    374     b.equality = "test";
    375     Guice.createInjector(
    376         new AbstractModule() {
    377           @Override
    378           protected void configure() {
    379             bind(String.class);
    380             bind(HashEqualsTester.class).toInstance(a);
    381             bind(HashEqualsTester.class).toInstance(b);
    382           }
    383         });
    384   }
    385 
    386   public void testHashcodeNeverCalledOnProviderInstance() {
    387     final HashEqualsTester a = new HashEqualsTester();
    388     a.throwOnHashcode = true;
    389     a.equality = "test";
    390 
    391     final HashEqualsTester b = new HashEqualsTester();
    392     b.throwOnHashcode = true;
    393     b.equality = "test";
    394     Guice.createInjector(
    395         new AbstractModule() {
    396           @Override
    397           protected void configure() {
    398             bind(String.class);
    399             bind(Object.class).toProvider(a);
    400             bind(Object.class).toProvider(b);
    401           }
    402         });
    403   }
    404 
    405   private static class RealA extends A {}
    406 
    407   @ImplementedBy(RealA.class)
    408   private static class A {}
    409 
    410   private void removeBasicBindings(Collection<Key<?>> bindings) {
    411     bindings.remove(Key.get(Injector.class));
    412     bindings.remove(Key.get(Logger.class));
    413     bindings.remove(Key.get(Stage.class));
    414   }
    415 
    416   private static class ThrowingModule extends AbstractModule {
    417     @Override
    418     protected void configure() {
    419       bind(Foo.class)
    420           .toInstance(
    421               new Foo() {
    422                 @Override
    423                 public boolean equals(Object obj) {
    424                   throw new RuntimeException("Boo!");
    425                 }
    426 
    427                 @Override
    428                 public int hashCode() {
    429                   throw new RuntimeException("Boo!");
    430                 }
    431               });
    432     }
    433   }
    434 
    435   private abstract static class FooModule extends AbstractModule {
    436     protected final FooImpl foo;
    437     protected final Provider<Foo> pFoo;
    438     protected final Class<? extends Provider<? extends Foo>> pclFoo;
    439     protected final Class<? extends Foo> clFoo;
    440     protected final Constructor<FooImpl> cFoo;
    441 
    442     FooModule(
    443         FooImpl foo,
    444         Provider<Foo> pFoo,
    445         Class<? extends Provider<? extends Foo>> pclFoo,
    446         Class<? extends Foo> clFoo,
    447         Constructor<FooImpl> cFoo) {
    448       this.foo = foo;
    449       this.pFoo = pFoo;
    450       this.pclFoo = pclFoo;
    451       this.clFoo = clFoo;
    452       this.cFoo = cFoo;
    453     }
    454   }
    455 
    456   private static class FailedModule extends FooModule {
    457     FailedModule(
    458         FooImpl foo,
    459         Provider<Foo> pFoo,
    460         Class<? extends Provider<? extends Foo>> pclFoo,
    461         Class<? extends Foo> clFoo,
    462         Constructor<FooImpl> cFoo) {
    463       super(foo, pFoo, pclFoo, clFoo, cFoo);
    464     }
    465 
    466     @Override
    467     protected void configure() {
    468       // InstanceBinding
    469       bind(Foo.class).toInstance(foo);
    470 
    471       // ProviderInstanceBinding
    472       bind(Foo.class).toProvider(pFoo);
    473 
    474       // ProviderKeyBinding
    475       bind(Foo.class).toProvider(pclFoo);
    476 
    477       // LinkedKeyBinding
    478       bind(Foo.class).to(clFoo);
    479 
    480       // ConstructorBinding
    481       bind(Foo.class).toConstructor(cFoo);
    482     }
    483 
    484     @Provides
    485     Foo foo() {
    486       return null;
    487     }
    488   }
    489 
    490   private static class FailingProviderModule extends AbstractModule {
    491 
    492     @Provides
    493     Foo foo() {
    494       return null;
    495     }
    496   }
    497 
    498   private static class SimpleProviderModule extends AbstractModule {
    499 
    500     @Provides
    501     @Named("providerMethod")
    502     Foo foo() {
    503       return null;
    504     }
    505 
    506     @Override
    507     public boolean equals(Object obj) {
    508       return obj.getClass() == getClass();
    509     }
    510   }
    511 
    512   private static class SimpleModule extends FooModule {
    513     SimpleModule(
    514         FooImpl foo,
    515         Provider<Foo> pFoo,
    516         Class<? extends Provider<? extends Foo>> pclFoo,
    517         Class<? extends Foo> clFoo,
    518         Constructor<FooImpl> cFoo) {
    519       super(foo, pFoo, pclFoo, clFoo, cFoo);
    520     }
    521 
    522     @Override
    523     protected void configure() {
    524       // InstanceBinding
    525       bind(Foo.class).annotatedWith(named("instance")).toInstance(foo);
    526 
    527       // ProviderInstanceBinding
    528       bind(Foo.class).annotatedWith(named("pInstance")).toProvider(pFoo);
    529 
    530       // ProviderKeyBinding
    531       bind(Foo.class).annotatedWith(named("pKey")).toProvider(pclFoo);
    532 
    533       // LinkedKeyBinding
    534       bind(Foo.class).annotatedWith(named("linkedKey")).to(clFoo);
    535 
    536       // UntargettedBinding / ConstructorBinding
    537       bind(FooImpl.class);
    538 
    539       // ConstructorBinding
    540       bind(Foo.class).annotatedWith(named("constructor")).toConstructor(cFoo);
    541 
    542       // ProviderMethod
    543       // (reconstructed from an Element to ensure it doesn't get filtered out
    544       //  by deduplicating Modules)
    545       install(Elements.getModule(Elements.getElements(new SimpleProviderModule())));
    546     }
    547   }
    548 
    549   private static class ScopedModule extends FooModule {
    550     private final Scope scope;
    551 
    552     ScopedModule(
    553         Scope scope,
    554         FooImpl foo,
    555         Provider<Foo> pFoo,
    556         Class<? extends Provider<? extends Foo>> pclFoo,
    557         Class<? extends Foo> clFoo,
    558         Constructor<FooImpl> cFoo) {
    559       super(foo, pFoo, pclFoo, clFoo, cFoo);
    560       this.scope = scope;
    561     }
    562 
    563     @Override
    564     protected void configure() {
    565       // ProviderInstanceBinding
    566       bind(Foo.class).annotatedWith(named("pInstance")).toProvider(pFoo).in(scope);
    567 
    568       // ProviderKeyBinding
    569       bind(Foo.class).annotatedWith(named("pKey")).toProvider(pclFoo).in(scope);
    570 
    571       // LinkedKeyBinding
    572       bind(Foo.class).annotatedWith(named("linkedKey")).to(clFoo).in(scope);
    573 
    574       // UntargettedBinding / ConstructorBinding
    575       bind(FooImpl.class).in(scope);
    576 
    577       // ConstructorBinding
    578       bind(Foo.class).annotatedWith(named("constructor")).toConstructor(cFoo).in(scope);
    579     }
    580   }
    581 
    582   private static class AnnotatedScopeModule extends FooModule {
    583     private final Class<? extends Annotation> scope;
    584 
    585     AnnotatedScopeModule(
    586         Class<? extends Annotation> scope,
    587         FooImpl foo,
    588         Provider<Foo> pFoo,
    589         Class<? extends Provider<? extends Foo>> pclFoo,
    590         Class<? extends Foo> clFoo,
    591         Constructor<FooImpl> cFoo) {
    592       super(foo, pFoo, pclFoo, clFoo, cFoo);
    593       this.scope = scope;
    594     }
    595 
    596     @Override
    597     protected void configure() {
    598       // ProviderInstanceBinding
    599       bind(Foo.class).annotatedWith(named("pInstance")).toProvider(pFoo).in(scope);
    600 
    601       // ProviderKeyBinding
    602       bind(Foo.class).annotatedWith(named("pKey")).toProvider(pclFoo).in(scope);
    603 
    604       // LinkedKeyBinding
    605       bind(Foo.class).annotatedWith(named("linkedKey")).to(clFoo).in(scope);
    606 
    607       // UntargettedBinding / ConstructorBinding
    608       bind(FooImpl.class).in(scope);
    609 
    610       // ConstructorBinding
    611       bind(Foo.class).annotatedWith(named("constructor")).toConstructor(cFoo).in(scope);
    612     }
    613   }
    614 
    615   private static interface Foo {}
    616 
    617   private static class FooImpl implements Foo {
    618     @Inject
    619     public FooImpl() {}
    620 
    621     private static Constructor<FooImpl> cxtor() {
    622       try {
    623         return FooImpl.class.getConstructor();
    624       } catch (SecurityException e) {
    625         throw new RuntimeException(e);
    626       } catch (NoSuchMethodException e) {
    627         throw new RuntimeException(e);
    628       }
    629     }
    630   }
    631 
    632   private static class FooProvider implements Provider<Foo> {
    633     @Override
    634     public Foo get() {
    635       return new FooImpl();
    636     }
    637   }
    638 
    639   private static class Bar implements Foo {
    640     @Inject
    641     public Bar() {}
    642 
    643     private static Constructor<Bar> cxtor() {
    644       try {
    645         return Bar.class.getConstructor();
    646       } catch (SecurityException e) {
    647         throw new RuntimeException(e);
    648       } catch (NoSuchMethodException e) {
    649         throw new RuntimeException(e);
    650       }
    651     }
    652   }
    653 
    654   private static class BarProvider implements Provider<Foo> {
    655     @Override
    656     public Foo get() {
    657       return new Bar();
    658     }
    659   }
    660 
    661   private static class HashEqualsTester implements Provider<Object> {
    662     private String equality;
    663     private boolean throwOnEquals;
    664     private boolean throwOnHashcode;
    665 
    666     @Override
    667     public boolean equals(Object obj) {
    668       if (throwOnEquals) {
    669         throw new RuntimeException();
    670       } else if (obj instanceof HashEqualsTester) {
    671         HashEqualsTester o = (HashEqualsTester) obj;
    672         if (o.throwOnEquals) {
    673           throw new RuntimeException();
    674         }
    675         if (equality == null && o.equality == null) {
    676           return this == o;
    677         } else {
    678           return Objects.equal(equality, o.equality);
    679         }
    680       } else {
    681         return false;
    682       }
    683     }
    684 
    685     @Override
    686     public int hashCode() {
    687       if (throwOnHashcode) {
    688         throw new RuntimeException();
    689       } else {
    690         return super.hashCode();
    691       }
    692     }
    693 
    694     @Override
    695     public Object get() {
    696       return new Object();
    697     }
    698   }
    699 }
    700