Home | History | Annotate | Download | only in throwingproviders
      1 /**
      2  * Copyright (C) 2007 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject.throwingproviders;
     18 
     19 import static java.lang.annotation.ElementType.METHOD;
     20 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     21 
     22 import com.google.common.base.Function;
     23 import com.google.common.collect.ImmutableList;
     24 import com.google.common.collect.ImmutableSet;
     25 import com.google.common.collect.Iterables;
     26 import com.google.common.collect.Lists;
     27 import com.google.inject.AbstractModule;
     28 import com.google.inject.Asserts;
     29 import com.google.inject.BindingAnnotation;
     30 import com.google.inject.CreationException;
     31 import com.google.inject.Guice;
     32 import com.google.inject.Inject;
     33 import com.google.inject.Injector;
     34 import com.google.inject.Key;
     35 import com.google.inject.OutOfScopeException;
     36 import com.google.inject.Provider;
     37 import com.google.inject.ProvisionException;
     38 import com.google.inject.Scope;
     39 import com.google.inject.ScopeAnnotation;
     40 import com.google.inject.TypeLiteral;
     41 import com.google.inject.internal.util.Classes;
     42 import com.google.inject.name.Named;
     43 import com.google.inject.name.Names;
     44 import com.google.inject.spi.Dependency;
     45 import com.google.inject.spi.HasDependencies;
     46 import com.google.inject.spi.Message;
     47 import com.google.inject.throwingproviders.ThrowingProviderBinder.Result;
     48 
     49 import junit.framework.TestCase;
     50 
     51 import java.io.IOException;
     52 import java.lang.annotation.Annotation;
     53 import java.lang.annotation.ElementType;
     54 import java.lang.annotation.Retention;
     55 import java.lang.annotation.RetentionPolicy;
     56 import java.lang.annotation.Target;
     57 import java.net.BindException;
     58 import java.rmi.AccessException;
     59 import java.rmi.RemoteException;
     60 import java.util.ArrayList;
     61 import java.util.Arrays;
     62 import java.util.List;
     63 import java.util.Set;
     64 import java.util.TooManyListenersException;
     65 
     66 /**
     67  * @author jmourits (at) google.com (Jerome Mourits)
     68  * @author jessewilson (at) google.com (Jesse Wilson)
     69  * @author sameb (at) google.com (Sam Berlin)
     70  */
     71 public class CheckedProviderTest extends TestCase {
     72   @Target(METHOD) @Retention(RUNTIME) @BindingAnnotation
     73   @interface NotExceptionScoping { };
     74 
     75   private static final Function<Dependency<?>, Key<?>> DEPENDENCY_TO_KEY =
     76       new Function<Dependency<?>, Key<?>>() {
     77         public Key<?> apply(Dependency<?> from) {
     78           return from.getKey();
     79         }
     80       };
     81 
     82   private final TypeLiteral<RemoteProvider<Foo>> remoteProviderOfFoo
     83       = new TypeLiteral<RemoteProvider<Foo>>() { };
     84   private final MockRemoteProvider<Foo> mockRemoteProvider = new MockRemoteProvider<Foo>();
     85   private final TestScope testScope = new TestScope();
     86 
     87   private Injector bindInjector;
     88   private Injector providesInjector;
     89   private Injector cxtorInjector;
     90 
     91   @Override
     92   protected void setUp() throws Exception {
     93     MockFoo.nextToThrow = null;
     94     MockFoo.nextToReturn = null;
     95     AnotherMockFoo.nextToThrow = null;
     96     AnotherMockFoo.nextToReturn = null;
     97 
     98     bindInjector = Guice.createInjector(new AbstractModule() {
     99       @Override
    100       protected void configure() {
    101         ThrowingProviderBinder.create(binder())
    102             .bind(RemoteProvider.class, Foo.class)
    103             .to(mockRemoteProvider)
    104             .in(testScope);
    105 
    106         ThrowingProviderBinder.create(binder())
    107             .bind(RemoteProvider.class, Foo.class)
    108             .annotatedWith(NotExceptionScoping.class)
    109             .scopeExceptions(false)
    110             .to(mockRemoteProvider)
    111             .in(testScope);
    112 
    113       }
    114     });
    115 
    116     providesInjector = Guice.createInjector(new AbstractModule() {
    117       @Override
    118       protected void configure() {
    119        install(ThrowingProviderBinder.forModule(this));
    120        bindScope(TestScope.Scoped.class, testScope);
    121       }
    122 
    123       @SuppressWarnings("unused")
    124       @CheckedProvides(RemoteProvider.class)
    125       @TestScope.Scoped
    126       Foo throwOrGet() throws RemoteException, BindException {
    127         return mockRemoteProvider.get();
    128       }
    129 
    130       @SuppressWarnings("unused")
    131       @CheckedProvides(value = RemoteProvider.class, scopeExceptions = false)
    132       @NotExceptionScoping
    133       @TestScope.Scoped
    134       Foo notExceptionScopingThrowOrGet() throws RemoteException, BindException {
    135         return mockRemoteProvider.get();
    136       }
    137 
    138     });
    139 
    140     cxtorInjector = Guice.createInjector(new AbstractModule() {
    141       @Override
    142       protected void configure() {
    143         ThrowingProviderBinder.create(binder())
    144           .bind(RemoteProvider.class, Foo.class)
    145           .providing(MockFoo.class)
    146           .in(testScope);
    147 
    148         ThrowingProviderBinder.create(binder())
    149           .bind(RemoteProvider.class, Foo.class)
    150           .annotatedWith(NotExceptionScoping.class)
    151           .scopeExceptions(false)
    152           .providing(MockFoo.class)
    153           .in(testScope);
    154 
    155       }
    156     });
    157   }
    158 
    159   public void testExceptionsThrown_Bind() throws Exception {
    160     tExceptionsThrown(bindInjector);
    161   }
    162 
    163   public void testExceptionsThrown_Provides() throws Exception {
    164     tExceptionsThrown(providesInjector);
    165   }
    166 
    167   public void testExceptionsThrown_Cxtor() throws Exception {
    168     tExceptionsThrown(cxtorInjector);
    169   }
    170 
    171   private void tExceptionsThrown(Injector injector) throws Exception {
    172     RemoteProvider<Foo> remoteProvider =
    173       injector.getInstance(Key.get(remoteProviderOfFoo));
    174 
    175     mockRemoteProvider.throwOnNextGet(new BindException("kaboom!"));
    176     MockFoo.nextToThrow = new BindException("kaboom!");
    177     try {
    178       remoteProvider.get();
    179       fail();
    180     } catch (BindException expected) {
    181       assertEquals("kaboom!", expected.getMessage());
    182     }
    183   }
    184 
    185   public void testValuesScoped_Bind() throws Exception  {
    186     tValuesScoped(bindInjector, null);
    187   }
    188 
    189   public void testValuesScoped_Provides() throws Exception  {
    190     tValuesScoped(providesInjector, null);
    191   }
    192 
    193   public void testValuesScopedWhenNotExceptionScoping_Bind() throws Exception  {
    194     tValuesScoped(bindInjector, NotExceptionScoping.class);
    195   }
    196 
    197   public void testValuesScopedWhenNotExceptionScoping_Provides() throws Exception  {
    198     tValuesScoped(providesInjector, NotExceptionScoping.class);
    199   }
    200 
    201   private void tValuesScoped(Injector injector,
    202       Class<? extends Annotation> annotation) throws Exception {
    203     Key<RemoteProvider<Foo>> key = annotation != null ?
    204         Key.get(remoteProviderOfFoo, annotation) :
    205         Key.get(remoteProviderOfFoo);
    206     RemoteProvider<Foo> remoteProvider = injector.getInstance(key);
    207 
    208     mockRemoteProvider.setNextToReturn(new SimpleFoo("A"));
    209     assertEquals("A", remoteProvider.get().s());
    210 
    211     mockRemoteProvider.setNextToReturn(new SimpleFoo("B"));
    212     assertEquals("A", remoteProvider.get().s());
    213 
    214     testScope.beginNewScope();
    215     assertEquals("B", remoteProvider.get().s());
    216   }
    217 
    218   public void testValuesScoped_Cxtor() throws Exception {
    219     RemoteProvider<Foo> remoteProvider =
    220         cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
    221 
    222     Foo retrieved = remoteProvider.get();
    223     assertSame(retrieved, remoteProvider.get()); // same, not in new scope.
    224 
    225     testScope.beginNewScope();
    226     assertNotSame(retrieved, remoteProvider.get()); // different, new scope.
    227   }
    228 
    229   public void testExceptionsScoped_Bind() throws Exception {
    230     tExceptionsScoped(bindInjector);
    231   }
    232 
    233   public void testExceptionsScoped_Provides() throws Exception {
    234     tExceptionsScoped(providesInjector);
    235   }
    236 
    237   public void testExceptionScopes_Cxtor() throws Exception {
    238     tExceptionsScoped(cxtorInjector);
    239   }
    240 
    241   private void tExceptionsScoped(Injector injector) throws Exception {
    242     RemoteProvider<Foo> remoteProvider =
    243         injector.getInstance(Key.get(remoteProviderOfFoo));
    244 
    245     mockRemoteProvider.throwOnNextGet(new RemoteException("A"));
    246     MockFoo.nextToThrow = new RemoteException("A");
    247     try {
    248       remoteProvider.get();
    249       fail();
    250     } catch (RemoteException expected) {
    251       assertEquals("A", expected.getMessage());
    252     }
    253 
    254     mockRemoteProvider.throwOnNextGet(new RemoteException("B"));
    255     MockFoo.nextToThrow = new RemoteException("B");
    256     try {
    257       remoteProvider.get();
    258       fail();
    259     } catch (RemoteException expected) {
    260       assertEquals("A", expected.getMessage());
    261     }
    262   }
    263 
    264   public void testExceptionsNotScopedWhenNotExceptionScoping_Bind() throws Exception {
    265     tExceptionsNotScopedWhenNotExceptionScoping(bindInjector);
    266   }
    267 
    268   public void testExceptionsNotScopedWhenNotExceptionScoping_Provides() throws Exception {
    269     tExceptionsNotScopedWhenNotExceptionScoping(providesInjector);
    270   }
    271 
    272   public void testExceptionNotScopedWhenNotExceptionScoping_Cxtor() throws Exception {
    273     tExceptionsNotScopedWhenNotExceptionScoping(cxtorInjector);
    274   }
    275 
    276   private void tExceptionsNotScopedWhenNotExceptionScoping(Injector injector) throws Exception {
    277     RemoteProvider<Foo> remoteProvider =
    278         injector.getInstance(Key.get(remoteProviderOfFoo, NotExceptionScoping.class));
    279 
    280     mockRemoteProvider.throwOnNextGet(new RemoteException("A"));
    281     MockFoo.nextToThrow = new RemoteException("A");
    282     try {
    283       remoteProvider.get();
    284       fail();
    285     } catch (RemoteException expected) {
    286       assertEquals("A", expected.getMessage());
    287     }
    288 
    289     mockRemoteProvider.throwOnNextGet(new RemoteException("B"));
    290     MockFoo.nextToThrow = new RemoteException("B");
    291     try {
    292       remoteProvider.get();
    293       fail();
    294     } catch (RemoteException expected) {
    295       assertEquals("B", expected.getMessage());
    296     }
    297   }
    298 
    299   public void testAnnotations_Bind() throws Exception {
    300     final MockRemoteProvider<Foo> mockRemoteProviderA = new MockRemoteProvider<Foo>();
    301     final MockRemoteProvider<Foo> mockRemoteProviderB = new MockRemoteProvider<Foo>();
    302     bindInjector = Guice.createInjector(new AbstractModule() {
    303       @Override
    304       protected void configure() {
    305         ThrowingProviderBinder.create(binder())
    306             .bind(RemoteProvider.class, Foo.class)
    307             .annotatedWith(Names.named("a"))
    308             .to(mockRemoteProviderA);
    309 
    310         ThrowingProviderBinder.create(binder())
    311             .bind(RemoteProvider.class, Foo.class)
    312             .to(mockRemoteProviderB);
    313       }
    314     });
    315     tAnnotations(bindInjector, mockRemoteProviderA, mockRemoteProviderB);
    316   }
    317 
    318   public void testAnnotations_Provides() throws Exception {
    319     final MockRemoteProvider<Foo> mockRemoteProviderA = new MockRemoteProvider<Foo>();
    320     final MockRemoteProvider<Foo> mockRemoteProviderB = new MockRemoteProvider<Foo>();
    321     providesInjector = Guice.createInjector(new AbstractModule() {
    322       @Override
    323       protected void configure() {
    324         install(ThrowingProviderBinder.forModule(this));
    325        }
    326 
    327        @SuppressWarnings("unused")
    328        @CheckedProvides(RemoteProvider.class)
    329        @Named("a")
    330        Foo throwOrGet() throws RemoteException, BindException {
    331          return mockRemoteProviderA.get();
    332        }
    333 
    334        @SuppressWarnings("unused")
    335        @CheckedProvides(RemoteProvider.class)
    336        Foo throwOrGet2() throws RemoteException, BindException {
    337          return mockRemoteProviderB.get();
    338        }
    339     });
    340     tAnnotations(providesInjector, mockRemoteProviderA, mockRemoteProviderB);
    341   }
    342 
    343   private void tAnnotations(Injector injector, MockRemoteProvider<Foo> mockA,
    344       MockRemoteProvider<Foo> mockB) throws Exception {
    345     mockA.setNextToReturn(new SimpleFoo("A"));
    346     mockB.setNextToReturn(new SimpleFoo("B"));
    347     assertEquals("A",
    348         injector.getInstance(Key.get(remoteProviderOfFoo, Names.named("a"))).get().s());
    349 
    350     assertEquals("B",
    351         injector.getInstance(Key.get(remoteProviderOfFoo)).get().s());
    352   }
    353 
    354   public void testAnnotations_Cxtor() throws Exception {
    355     cxtorInjector = Guice.createInjector(new AbstractModule() {
    356       @Override
    357       protected void configure() {
    358         ThrowingProviderBinder.create(binder())
    359             .bind(RemoteProvider.class, Foo.class)
    360             .annotatedWith(Names.named("a"))
    361             .providing(MockFoo.class);
    362 
    363         ThrowingProviderBinder.create(binder())
    364             .bind(RemoteProvider.class, Foo.class)
    365             .providing(AnotherMockFoo.class);
    366       }
    367     });
    368     MockFoo.nextToReturn = "A";
    369     AnotherMockFoo.nextToReturn = "B";
    370     assertEquals("A",
    371         cxtorInjector.getInstance(Key.get(remoteProviderOfFoo, Names.named("a"))).get().s());
    372 
    373     assertEquals("B",
    374         cxtorInjector.getInstance(Key.get(remoteProviderOfFoo)).get().s());
    375   }
    376 
    377   public void testUndeclaredExceptions_Bind() throws Exception {
    378     tUndeclaredExceptions(bindInjector);
    379   }
    380 
    381   public void testUndeclaredExceptions_Provides() throws Exception {
    382     tUndeclaredExceptions(providesInjector);
    383   }
    384 
    385   public void testUndeclaredExceptions_Cxtor() throws Exception {
    386     tUndeclaredExceptions(cxtorInjector);
    387   }
    388 
    389   private void tUndeclaredExceptions(Injector injector) throws Exception {
    390     RemoteProvider<Foo> remoteProvider =
    391         injector.getInstance(Key.get(remoteProviderOfFoo));
    392     mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("A"));
    393     MockFoo.nextToThrow = new IndexOutOfBoundsException("A");
    394     try {
    395       remoteProvider.get();
    396       fail();
    397     } catch (RuntimeException e) {
    398       assertEquals("A", e.getCause().getMessage());
    399     }
    400 
    401     // undeclared exceptions shouldn't be scoped
    402     mockRemoteProvider.throwOnNextGet(new IndexOutOfBoundsException("B"));
    403     MockFoo.nextToThrow = new IndexOutOfBoundsException("B");
    404     try {
    405       remoteProvider.get();
    406       fail();
    407     } catch (RuntimeException e) {
    408       assertEquals("B", e.getCause().getMessage());
    409     }
    410   }
    411 
    412   public void testThrowingProviderSubclassing() throws Exception {
    413     final SubMockRemoteProvider aProvider = new SubMockRemoteProvider();
    414     aProvider.setNextToReturn(new SimpleFoo("A"));
    415 
    416     bindInjector = Guice.createInjector(new AbstractModule() {
    417       @Override
    418       protected void configure() {
    419         ThrowingProviderBinder.create(binder())
    420             .bind(RemoteProvider.class, Foo.class)
    421             .to(aProvider);
    422       }
    423     });
    424 
    425     assertEquals("A",
    426         bindInjector.getInstance(Key.get(remoteProviderOfFoo)).get().s());
    427   }
    428 
    429   static class SubMockRemoteProvider extends MockRemoteProvider<Foo> { }
    430 
    431   public void testBindingToNonInterfaceType_Bind() throws Exception {
    432     try {
    433       Guice.createInjector(new AbstractModule() {
    434         @Override
    435         protected void configure() {
    436           ThrowingProviderBinder.create(binder())
    437               .bind(MockRemoteProvider.class, Foo.class)
    438               .to(mockRemoteProvider);
    439         }
    440       });
    441       fail();
    442     } catch (CreationException expected) {
    443       assertEquals(MockRemoteProvider.class.getName() + " must be an interface",
    444           Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    445     }
    446   }
    447 
    448   public void testBindingToNonInterfaceType_Provides() throws Exception {
    449     try {
    450       Guice.createInjector(new AbstractModule() {
    451         @Override
    452         protected void configure() {
    453           install(ThrowingProviderBinder.forModule(this));
    454         }
    455 
    456         @SuppressWarnings("unused")
    457         @CheckedProvides(MockRemoteProvider.class)
    458         Foo foo() {
    459           return null;
    460         }
    461       });
    462       fail();
    463     } catch (CreationException expected) {
    464       assertEquals(MockRemoteProvider.class.getName() + " must be an interface",
    465           Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    466     }
    467   }
    468 
    469   public void testBindingToSubSubInterface_Bind() throws Exception {
    470     try {
    471       bindInjector = Guice.createInjector(new AbstractModule() {
    472         @Override
    473         protected void configure() {
    474           ThrowingProviderBinder.create(binder())
    475               .bind(SubRemoteProvider.class, Foo.class);
    476         }
    477       });
    478       fail();
    479     } catch (CreationException expected) {
    480       assertEquals(SubRemoteProvider.class.getName() + " must extend CheckedProvider (and only CheckedProvider)",
    481           Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    482     }
    483   }
    484 
    485   public void testBindingToSubSubInterface_Provides() throws Exception {
    486     try {
    487       Guice.createInjector(new AbstractModule() {
    488         @Override
    489         protected void configure() {
    490           install(ThrowingProviderBinder.forModule(this));
    491         }
    492 
    493         @SuppressWarnings("unused")
    494         @CheckedProvides(SubRemoteProvider.class)
    495         Foo foo() {
    496           return null;
    497         }
    498       });
    499       fail();
    500     } catch (CreationException expected) {
    501       assertEquals(SubRemoteProvider.class.getName() + " must extend CheckedProvider (and only CheckedProvider)",
    502           Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    503     }
    504   }
    505 
    506   interface SubRemoteProvider extends RemoteProvider<String> { }
    507 
    508   public void testBindingToInterfaceWithExtraMethod_Bind() throws Exception {
    509     try {
    510       bindInjector = Guice.createInjector(new AbstractModule() {
    511         @Override
    512         protected void configure() {
    513           ThrowingProviderBinder.create(binder())
    514               .bind(RemoteProviderWithExtraMethod.class, Foo.class);
    515         }
    516       });
    517       fail();
    518     } catch (CreationException expected) {
    519       assertEquals(RemoteProviderWithExtraMethod.class.getName() + " may not declare any new methods, but declared "
    520           + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(),
    521           Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    522     }
    523   }
    524 
    525   public void testBindingToInterfaceWithExtraMethod_Provides() throws Exception {
    526     try {
    527       Guice.createInjector(new AbstractModule() {
    528         @Override
    529         protected void configure() {
    530           install(ThrowingProviderBinder.forModule(this));
    531         }
    532 
    533         @SuppressWarnings("unused")
    534         @CheckedProvides(RemoteProviderWithExtraMethod.class)
    535         Foo foo() {
    536           return null;
    537         }
    538       });
    539       fail();
    540     } catch (CreationException expected) {
    541       assertEquals(RemoteProviderWithExtraMethod.class.getName() + " may not declare any new methods, but declared "
    542           + RemoteProviderWithExtraMethod.class.getDeclaredMethods()[0].toGenericString(),
    543           Iterables.getOnlyElement(expected.getErrorMessages()).getMessage());
    544     }
    545   }
    546 
    547   public void testDependencies_Bind() {
    548     bindInjector = Guice.createInjector(new AbstractModule() {
    549       @Override
    550       protected void configure() {
    551         bind(String.class).toInstance("Foo");
    552         bind(Integer.class).toInstance(5);
    553         bind(Double.class).toInstance(5d);
    554         bind(Long.class).toInstance(5L);
    555         ThrowingProviderBinder.create(binder())
    556             .bind(RemoteProvider.class, Foo.class)
    557             .to(DependentRemoteProvider.class);
    558       }
    559     });
    560 
    561     HasDependencies hasDependencies =
    562         (HasDependencies)bindInjector.getBinding(Key.get(remoteProviderOfFoo));
    563     hasDependencies =
    564         (HasDependencies)bindInjector.getBinding(
    565             Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
    566     // Make sure that that is dependent on DependentRemoteProvider.
    567     assertEquals(Dependency.get(Key.get(DependentRemoteProvider.class)),
    568         Iterables.getOnlyElement(hasDependencies.getDependencies()));
    569     // And make sure DependentRemoteProvider has the proper dependencies.
    570     hasDependencies = (HasDependencies)bindInjector.getBinding(DependentRemoteProvider.class);
    571     Set<Key<?>> dependencyKeys = ImmutableSet.copyOf(
    572         Iterables.transform(hasDependencies.getDependencies(), DEPENDENCY_TO_KEY));
    573     assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class),
    574         Key.get(Long.class), Key.get(Double.class)), dependencyKeys);
    575   }
    576 
    577   public void testDependencies_Provides() {
    578     providesInjector = Guice.createInjector(new AbstractModule() {
    579       @Override
    580       protected void configure() {
    581         bind(String.class).toInstance("Foo");
    582         bind(Integer.class).toInstance(5);
    583         bind(Double.class).toInstance(5d);
    584         bind(Long.class).toInstance(5L);
    585         install(ThrowingProviderBinder.forModule(this));
    586       }
    587 
    588       @SuppressWarnings("unused")
    589       @CheckedProvides(RemoteProvider.class)
    590       Foo foo(String s, Integer i, Double d, Long l) {
    591         return null;
    592       }
    593     });
    594 
    595     HasDependencies hasDependencies =
    596         (HasDependencies) providesInjector.getBinding(Key.get(remoteProviderOfFoo));
    597     // RemoteProvider<String> is dependent on the provider method..
    598     hasDependencies = (HasDependencies) providesInjector.getBinding(
    599         Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
    600     // And the provider method has our real dependencies..
    601     hasDependencies = (HasDependencies)providesInjector.getBinding(
    602         Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey());
    603     Set<Key<?>> dependencyKeys = ImmutableSet.copyOf(
    604         Iterables.transform(hasDependencies.getDependencies(), DEPENDENCY_TO_KEY));
    605     assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class),
    606         Key.get(Long.class), Key.get(Double.class)), dependencyKeys);
    607   }
    608 
    609   public void testDependencies_Cxtor() {
    610     cxtorInjector = Guice.createInjector(new AbstractModule() {
    611       @Override
    612       protected void configure() {
    613         bind(String.class).toInstance("Foo");
    614         bind(Integer.class).toInstance(5);
    615         bind(Double.class).toInstance(5d);
    616         bind(Long.class).toInstance(5L);
    617         ThrowingProviderBinder.create(binder())
    618             .bind(RemoteProvider.class, Foo.class)
    619             .providing(DependentMockFoo.class);
    620       }
    621     });
    622 
    623     Key<?> key = Key.get(remoteProviderOfFoo);
    624 
    625     // RemoteProvider<String> is dependent on Result.
    626     HasDependencies hasDependencies = (HasDependencies) cxtorInjector.getBinding(key);
    627     key = Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey();
    628     assertEquals(Result.class, key.getTypeLiteral().getRawType());
    629 
    630     // Result is dependent on the fake CheckedProvider impl
    631     hasDependencies = (HasDependencies) cxtorInjector.getBinding(key);
    632     key = Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey();
    633     assertTrue(CheckedProvider.class.isAssignableFrom(key.getTypeLiteral().getRawType()));
    634 
    635     // And the CheckedProvider is dependent on DependentMockFoo...
    636     hasDependencies = (HasDependencies) cxtorInjector.getBinding(key);
    637     key = Iterables.getOnlyElement(hasDependencies.getDependencies()).getKey();
    638     assertEquals(DependentMockFoo.class, key.getTypeLiteral().getRawType());
    639 
    640     // And DependentMockFoo is dependent on the goods.
    641     hasDependencies = (HasDependencies) cxtorInjector.getBinding(key);
    642     Set<Key<?>> dependencyKeys = ImmutableSet.copyOf(
    643         Iterables.transform(hasDependencies.getDependencies(), DEPENDENCY_TO_KEY));
    644     assertEquals(ImmutableSet.<Key<?>>of(Key.get(String.class), Key.get(Integer.class),
    645         Key.get(Long.class), Key.get(Double.class)), dependencyKeys);
    646   }
    647 
    648   interface RemoteProviderWithExtraMethod<T> extends CheckedProvider<T> {
    649     T get(T defaultValue) throws RemoteException, BindException;
    650   }
    651 
    652   interface RemoteProvider<T> extends CheckedProvider<T> {
    653     public T get() throws RemoteException, BindException;
    654   }
    655 
    656   static class DependentMockFoo implements Foo {
    657     @Inject double foo;
    658 
    659     @ThrowingInject public DependentMockFoo(String foo, int bar) {
    660     }
    661 
    662     @Inject void initialize(long foo) {}
    663 
    664     @Override
    665     public String s() {
    666       return null;
    667     }
    668   }
    669 
    670   static class DependentRemoteProvider<T> implements RemoteProvider<T> {
    671     @Inject double foo;
    672 
    673     @Inject public DependentRemoteProvider(String foo, int bar) {
    674     }
    675 
    676     @Inject void initialize(long foo) {}
    677 
    678     public T get() {
    679       return null;
    680     }
    681   }
    682 
    683   interface Foo {
    684     String s();
    685   }
    686 
    687   static class SimpleFoo implements Foo {
    688     private String s;
    689 
    690     SimpleFoo(String s) {
    691       this.s = s;
    692     }
    693 
    694     @Override
    695     public String s() {
    696       return s;
    697     }
    698 
    699     @Override
    700     public String toString() {
    701       return s;
    702     }
    703   }
    704 
    705   static class MockFoo implements Foo {
    706     static Exception nextToThrow;
    707     static String nextToReturn;
    708 
    709     @ThrowingInject
    710     MockFoo() throws RemoteException, BindException {
    711       if (nextToThrow instanceof RemoteException) {
    712         throw (RemoteException) nextToThrow;
    713       } else if (nextToThrow instanceof BindException) {
    714         throw (BindException) nextToThrow;
    715       } else if (nextToThrow instanceof RuntimeException) {
    716         throw (RuntimeException) nextToThrow;
    717       } else if (nextToThrow == null) {
    718         // Do nothing, return this.
    719       } else {
    720         throw new AssertionError("nextToThrow must be a runtime or remote exception");
    721       }
    722     }
    723 
    724     @Override
    725     public String s() {
    726       return nextToReturn;
    727     }
    728 
    729     @Override
    730     public String toString() {
    731       return nextToReturn;
    732     }
    733   }
    734 
    735   static class AnotherMockFoo implements Foo {
    736     static Exception nextToThrow;
    737     static String nextToReturn;
    738 
    739     @ThrowingInject
    740     AnotherMockFoo() throws RemoteException, BindException {
    741       if (nextToThrow instanceof RemoteException) {
    742         throw (RemoteException) nextToThrow;
    743       } else if (nextToThrow instanceof BindException) {
    744         throw (BindException) nextToThrow;
    745       } else if (nextToThrow instanceof RuntimeException) {
    746         throw (RuntimeException) nextToThrow;
    747       } else if (nextToThrow == null) {
    748         // Do nothing, return this.
    749       } else {
    750         throw new AssertionError("nextToThrow must be a runtime or remote exception");
    751       }
    752     }
    753 
    754     @Override
    755     public String s() {
    756       return nextToReturn;
    757     }
    758 
    759     @Override
    760     public String toString() {
    761       return nextToReturn;
    762     }
    763   }
    764 
    765   static class MockRemoteProvider<T> implements RemoteProvider<T> {
    766     Exception nextToThrow;
    767     T nextToReturn;
    768 
    769     public void throwOnNextGet(Exception nextToThrow) {
    770       this.nextToThrow = nextToThrow;
    771     }
    772 
    773     public void setNextToReturn(T nextToReturn) {
    774       this.nextToReturn = nextToReturn;
    775     }
    776 
    777     public T get() throws RemoteException, BindException {
    778       if (nextToThrow instanceof RemoteException) {
    779         throw (RemoteException) nextToThrow;
    780       } else if (nextToThrow instanceof BindException) {
    781         throw (BindException) nextToThrow;
    782       } else if (nextToThrow instanceof RuntimeException) {
    783         throw (RuntimeException) nextToThrow;
    784       } else if (nextToThrow == null) {
    785         return nextToReturn;
    786       } else {
    787         throw new AssertionError("nextToThrow must be a runtime or remote exception");
    788       }
    789     }
    790   }
    791 
    792   public void testBindingToInterfaceWithBoundValueType_Bind() throws RemoteException {
    793     bindInjector = Guice.createInjector(new AbstractModule() {
    794       @Override
    795       protected void configure() {
    796         ThrowingProviderBinder.create(binder())
    797             .bind(StringRemoteProvider.class, String.class)
    798             .to(new StringRemoteProvider() {
    799               public String get() {
    800                 return "A";
    801               }
    802             });
    803       }
    804     });
    805 
    806     assertEquals("A", bindInjector.getInstance(StringRemoteProvider.class).get());
    807   }
    808 
    809   public void testBindingToInterfaceWithBoundValueType_Provides() throws RemoteException {
    810     providesInjector = Guice.createInjector(new AbstractModule() {
    811       @Override
    812       protected void configure() {
    813         install(ThrowingProviderBinder.forModule(this));
    814       }
    815 
    816       @SuppressWarnings("unused")
    817       @CheckedProvides(StringRemoteProvider.class)
    818       String foo() throws RemoteException {
    819           return "A";
    820       }
    821     });
    822 
    823     assertEquals("A", providesInjector.getInstance(StringRemoteProvider.class).get());
    824   }
    825 
    826   interface StringRemoteProvider extends CheckedProvider<String> {
    827     @Override String get() throws RemoteException;
    828   }
    829 
    830   @SuppressWarnings("deprecation")
    831   public void testBindingToInterfaceWithGeneric_Bind() throws Exception {
    832     bindInjector = Guice.createInjector(new AbstractModule() {
    833       @Override
    834       protected void configure() {
    835         ThrowingProviderBinder.create(binder())
    836             .bind(RemoteProvider.class, new TypeLiteral<List<String>>() { }.getType())
    837             .to(new RemoteProvider<List<String>>() {
    838               public List<String> get() {
    839                 return Arrays.asList("A", "B");
    840               }
    841             });
    842       }
    843     });
    844 
    845     Key<RemoteProvider<List<String>>> key
    846         = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
    847     assertEquals(Arrays.asList("A", "B"), bindInjector.getInstance(key).get());
    848   }
    849 
    850   public void testBindingToInterfaceWithGeneric_BindUsingTypeLiteral() throws Exception {
    851     bindInjector = Guice.createInjector(new AbstractModule() {
    852       @Override
    853       protected void configure() {
    854         ThrowingProviderBinder.create(binder())
    855             .bind(RemoteProvider.class, new TypeLiteral<List<String>>() {})
    856             .to(new RemoteProvider<List<String>>() {
    857               public List<String> get() {
    858                 return Arrays.asList("A", "B");
    859               }
    860             });
    861       }
    862     });
    863 
    864     Key<RemoteProvider<List<String>>> key
    865         = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
    866     assertEquals(Arrays.asList("A", "B"), bindInjector.getInstance(key).get());
    867   }
    868 
    869   public void testBindingToInterfaceWithGeneric_Provides() throws Exception {
    870     providesInjector = Guice.createInjector(new AbstractModule() {
    871       @Override
    872       protected void configure() {
    873         install(ThrowingProviderBinder.forModule(this));
    874       }
    875 
    876       @SuppressWarnings("unused")
    877       @CheckedProvides(RemoteProvider.class)
    878       List<String> foo() throws RemoteException {
    879           return Arrays.asList("A", "B");
    880       }
    881     });
    882 
    883     Key<RemoteProvider<List<String>>> key
    884         = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
    885     assertEquals(Arrays.asList("A", "B"), providesInjector.getInstance(key).get());
    886   }
    887 
    888   public void testBindingToInterfaceWithGeneric_Cxtor() throws Exception {
    889     cxtorInjector = Guice.createInjector(new AbstractModule() {
    890       @Override
    891       protected void configure() {
    892         ThrowingProviderBinder.create(binder())
    893         .bind(RemoteProvider.class, new TypeLiteral<List<String>>() {})
    894         .providing(new TypeLiteral<ThrowingArrayList<String>>() {});
    895       }
    896     });
    897 
    898     Key<RemoteProvider<List<String>>> key
    899         = Key.get(new TypeLiteral<RemoteProvider<List<String>>>() { });
    900     assertEquals(Arrays.asList(), cxtorInjector.getInstance(key).get());
    901   }
    902 
    903   private static class ThrowingArrayList<T> extends ArrayList<T> {
    904     @SuppressWarnings("unused")
    905     @ThrowingInject
    906     ThrowingArrayList() {}
    907   }
    908 
    909   public void testProviderMethodWithWrongException() {
    910     try {
    911       Guice.createInjector(new AbstractModule() {
    912         @Override
    913         protected void configure() {
    914           install(ThrowingProviderBinder.forModule(this));
    915         }
    916 
    917         @SuppressWarnings("unused")
    918         @CheckedProvides(RemoteProvider.class)
    919         String foo() throws InterruptedException {
    920             return null;
    921         }
    922       });
    923       fail();
    924     } catch(CreationException ce) {
    925       assertEquals(InterruptedException.class.getName()
    926           + " is not compatible with the exceptions (["
    927           + RemoteException.class + ", " + BindException.class
    928           + "]) declared in the CheckedProvider interface ("
    929           + RemoteProvider.class.getName()
    930           + ")",
    931           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
    932     }
    933   }
    934 
    935   public void testCxtorWithWrongException() {
    936     try {
    937       Guice.createInjector(new AbstractModule() {
    938         @Override
    939         protected void configure() {
    940           ThrowingProviderBinder.create(binder())
    941               .bind(RemoteProvider.class, Foo.class)
    942               .providing(WrongExceptionFoo.class);
    943         }
    944       });
    945       fail();
    946     } catch (CreationException ce) {
    947       assertEquals(InterruptedException.class.getName()
    948           + " is not compatible with the exceptions (["
    949           + RemoteException.class + ", " + BindException.class
    950           + "]) declared in the CheckedProvider interface ("
    951           + RemoteProvider.class.getName()
    952           + ")",
    953           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
    954     }
    955   }
    956 
    957   static class WrongExceptionFoo implements Foo {
    958     @SuppressWarnings("unused")
    959     @ThrowingInject
    960     public WrongExceptionFoo() throws InterruptedException {
    961     }
    962 
    963     @Override
    964     public String s() { return null; }
    965   }
    966 
    967   public void testProviderMethodWithSubclassOfExceptionIsOk() throws Exception {
    968     providesInjector = Guice.createInjector(new AbstractModule() {
    969       @Override
    970       protected void configure() {
    971         install(ThrowingProviderBinder.forModule(this));
    972       }
    973 
    974       @SuppressWarnings("unused")
    975       @CheckedProvides(RemoteProvider.class)
    976       Foo foo() throws AccessException {
    977         throw new AccessException("boo!");
    978       }
    979     });
    980 
    981     RemoteProvider<Foo> remoteProvider =
    982       providesInjector.getInstance(Key.get(remoteProviderOfFoo));
    983 
    984     try {
    985       remoteProvider.get();
    986       fail();
    987     } catch (RemoteException expected) {
    988       assertTrue(expected instanceof AccessException);
    989       assertEquals("boo!", expected.getMessage());
    990     }
    991   }
    992 
    993   public void testCxtorWithSubclassOfExceptionIsOk() throws Exception {
    994     cxtorInjector = Guice.createInjector(new AbstractModule() {
    995         @Override
    996         protected void configure() {
    997           ThrowingProviderBinder.create(binder())
    998               .bind(RemoteProvider.class, Foo.class)
    999               .providing(SubclassExceptionFoo.class);
   1000         }
   1001       });
   1002 
   1003     RemoteProvider<Foo> remoteProvider =
   1004         cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
   1005 
   1006       try {
   1007         remoteProvider.get();
   1008         fail();
   1009       } catch (RemoteException expected) {
   1010         assertTrue(expected instanceof AccessException);
   1011         assertEquals("boo!", expected.getMessage());
   1012       }
   1013   }
   1014 
   1015   static class SubclassExceptionFoo implements Foo {
   1016     @ThrowingInject
   1017     public SubclassExceptionFoo() throws AccessException {
   1018       throw new AccessException("boo!");
   1019     }
   1020 
   1021     @Override
   1022     public String s() { return null; }
   1023   }
   1024 
   1025   public void testProviderMethodWithSuperclassExceptionFails() {
   1026     try {
   1027       Guice.createInjector(new AbstractModule() {
   1028         @Override
   1029         protected void configure() {
   1030           install(ThrowingProviderBinder.forModule(this));
   1031         }
   1032 
   1033         @SuppressWarnings("unused")
   1034         @CheckedProvides(RemoteProvider.class)
   1035         Foo foo() throws IOException {
   1036             return null;
   1037         }
   1038       });
   1039       fail();
   1040     } catch(CreationException ce) {
   1041       assertEquals(IOException.class.getName()
   1042           + " is not compatible with the exceptions (["
   1043           + RemoteException.class + ", " + BindException.class
   1044           + "]) declared in the CheckedProvider interface ("
   1045           + RemoteProvider.class.getName()
   1046           + ")",
   1047           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1048     }
   1049   }
   1050 
   1051   public void testCxtorWithSuperclassExceptionFails() {
   1052     try {
   1053       Guice.createInjector(new AbstractModule() {
   1054         @Override
   1055         protected void configure() {
   1056           ThrowingProviderBinder.create(binder())
   1057               .bind(RemoteProvider.class, Foo.class)
   1058               .providing(SuperclassExceptionFoo.class);
   1059         }
   1060       });
   1061       fail();
   1062     } catch (CreationException ce) {
   1063       assertEquals(IOException.class.getName()
   1064           + " is not compatible with the exceptions (["
   1065           + RemoteException.class + ", " + BindException.class
   1066           + "]) declared in the CheckedProvider interface ("
   1067           + RemoteProvider.class.getName()
   1068           + ")",
   1069           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1070     }
   1071   }
   1072 
   1073   static class SuperclassExceptionFoo implements Foo {
   1074     @SuppressWarnings("unused")
   1075     @ThrowingInject
   1076     public SuperclassExceptionFoo() throws IOException {
   1077     }
   1078 
   1079     @Override
   1080     public String s() { return null; }
   1081   }
   1082 
   1083   public void testProviderMethodWithRuntimeExceptionsIsOk() throws Exception {
   1084     providesInjector = Guice.createInjector(new AbstractModule() {
   1085       @Override
   1086       protected void configure() {
   1087         install(ThrowingProviderBinder.forModule(this));
   1088       }
   1089 
   1090       @SuppressWarnings("unused")
   1091       @CheckedProvides(RemoteProvider.class)
   1092       Foo foo() throws RuntimeException {
   1093         throw new RuntimeException("boo!");
   1094       }
   1095     });
   1096 
   1097     RemoteProvider<Foo> remoteProvider =
   1098       providesInjector.getInstance(Key.get(remoteProviderOfFoo));
   1099 
   1100     try {
   1101       remoteProvider.get();
   1102       fail();
   1103     } catch (RuntimeException expected) {
   1104       assertEquals("boo!", expected.getCause().getMessage());
   1105     }
   1106   }
   1107 
   1108   public void testCxtorWithRuntimeExceptionsIsOk() throws Exception {
   1109     cxtorInjector = Guice.createInjector(new AbstractModule() {
   1110       @Override
   1111       protected void configure() {
   1112         ThrowingProviderBinder.create(binder())
   1113             .bind(RemoteProvider.class, Foo.class)
   1114             .providing(RuntimeExceptionFoo.class);
   1115       }
   1116     });
   1117 
   1118     RemoteProvider<Foo> remoteProvider =
   1119         cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
   1120 
   1121     try {
   1122       remoteProvider.get();
   1123       fail();
   1124     } catch (RuntimeException expected) {
   1125       assertEquals("boo!", expected.getCause().getMessage());
   1126     }
   1127   }
   1128 
   1129   static class RuntimeExceptionFoo implements Foo {
   1130     @ThrowingInject
   1131     public RuntimeExceptionFoo() throws RuntimeException {
   1132       throw new RuntimeException("boo!");
   1133     }
   1134 
   1135     @Override
   1136     public String s() { return null; }
   1137   }
   1138 
   1139   private static class SubBindException extends BindException {}
   1140 
   1141   public void testProviderMethodWithManyExceptions() {
   1142     try {
   1143       Guice.createInjector(new AbstractModule() {
   1144         @Override
   1145         protected void configure() {
   1146           install(ThrowingProviderBinder.forModule(this));
   1147         }
   1148 
   1149         @SuppressWarnings("unused")
   1150         @CheckedProvides(RemoteProvider.class)
   1151         String foo() throws InterruptedException, RuntimeException, RemoteException,
   1152                             AccessException, TooManyListenersException,
   1153                             BindException, SubBindException {
   1154             return null;
   1155         }
   1156       });
   1157       fail();
   1158     } catch(CreationException ce) {
   1159       // The only two that should fail are Interrupted & TooManyListeners.. the rest are OK.
   1160       List<Message> errors = ImmutableList.copyOf(ce.getErrorMessages());
   1161       assertEquals(InterruptedException.class.getName()
   1162           + " is not compatible with the exceptions (["
   1163           + RemoteException.class + ", " + BindException.class
   1164           + "]) declared in the CheckedProvider interface ("
   1165           + RemoteProvider.class.getName()
   1166           + ")",
   1167           errors.get(0).getMessage());
   1168       assertEquals(TooManyListenersException.class.getName()
   1169           + " is not compatible with the exceptions (["
   1170           + RemoteException.class + ", " + BindException.class
   1171           + "]) declared in the CheckedProvider interface ("
   1172           + RemoteProvider.class.getName()
   1173           + ")",
   1174           errors.get(1).getMessage());
   1175       assertEquals(2, errors.size());
   1176     }
   1177   }
   1178 
   1179   public void testCxtorWithManyExceptions() {
   1180     try {
   1181       Guice.createInjector(new AbstractModule() {
   1182         @Override
   1183         protected void configure() {
   1184           ThrowingProviderBinder.create(binder())
   1185               .bind(RemoteProvider.class, Foo.class)
   1186               .providing(ManyExceptionFoo.class);
   1187         }
   1188       });
   1189       fail();
   1190     } catch (CreationException ce) {
   1191       // The only two that should fail are Interrupted & TooManyListeners.. the rest are OK.
   1192       List<Message> errors = ImmutableList.copyOf(ce.getErrorMessages());
   1193       assertEquals(InterruptedException.class.getName()
   1194           + " is not compatible with the exceptions (["
   1195           + RemoteException.class + ", " + BindException.class
   1196           + "]) declared in the CheckedProvider interface ("
   1197           + RemoteProvider.class.getName()
   1198           + ")",
   1199           errors.get(0).getMessage());
   1200       assertEquals(TooManyListenersException.class.getName()
   1201           + " is not compatible with the exceptions (["
   1202           + RemoteException.class + ", " + BindException.class
   1203           + "]) declared in the CheckedProvider interface ("
   1204           + RemoteProvider.class.getName()
   1205           + ")",
   1206           errors.get(1).getMessage());
   1207       assertEquals(2, errors.size());
   1208     }
   1209   }
   1210 
   1211   static class ManyExceptionFoo implements Foo {
   1212     @SuppressWarnings("unused")
   1213     @ThrowingInject
   1214     public ManyExceptionFoo()
   1215         throws InterruptedException,
   1216         RuntimeException,
   1217         RemoteException,
   1218         AccessException,
   1219         TooManyListenersException,
   1220         BindException,
   1221         SubBindException {
   1222     }
   1223 
   1224     @Override
   1225     public String s() { return null; }
   1226   }
   1227 
   1228   public void testMoreTypeParameters() {
   1229     try {
   1230       Guice.createInjector(new AbstractModule() {
   1231         @Override
   1232         protected void configure() {
   1233           install(ThrowingProviderBinder.forModule(this));
   1234         }
   1235 
   1236         @SuppressWarnings("unused")
   1237         @CheckedProvides(TooManyTypeParameters.class)
   1238         String foo() {
   1239             return null;
   1240         }
   1241       });
   1242       fail();
   1243     } catch(CreationException ce) {
   1244       assertEquals(TooManyTypeParameters.class.getName() + " has more than one generic type parameter: [T, P]",
   1245           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1246     }
   1247   }
   1248 
   1249   public void testWrongThrowingProviderType() {
   1250     try {
   1251       Guice.createInjector(new AbstractModule() {
   1252         @Override
   1253         protected void configure() {
   1254           install(ThrowingProviderBinder.forModule(this));
   1255         }
   1256 
   1257         @SuppressWarnings("unused")
   1258         @CheckedProvides(WrongThrowingProviderType.class)
   1259         String foo() {
   1260             return null;
   1261         }
   1262       });
   1263       fail();
   1264     } catch(CreationException ce) {
   1265       assertEquals(WrongThrowingProviderType.class.getName()
   1266           + " does not properly extend CheckedProvider, the first type parameter of CheckedProvider "
   1267           + "(java.lang.String) is not a generic type",
   1268           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1269     }
   1270   }
   1271 
   1272   public void testOneMethodThatIsntGet() {
   1273     try {
   1274       Guice.createInjector(new AbstractModule() {
   1275         @Override
   1276         protected void configure() {
   1277           install(ThrowingProviderBinder.forModule(this));
   1278         }
   1279 
   1280         @SuppressWarnings("unused")
   1281         @CheckedProvides(OneNoneGetMethod.class)
   1282         String foo() {
   1283             return null;
   1284         }
   1285       });
   1286       fail();
   1287     } catch(CreationException ce) {
   1288       assertEquals(OneNoneGetMethod.class.getName()
   1289           + " may not declare any new methods, but declared " + Classes.toString(OneNoneGetMethod.class.getDeclaredMethods()[0]),
   1290           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1291     }
   1292   }
   1293 
   1294   public void testManyMethods() {
   1295     try {
   1296       Guice.createInjector(new AbstractModule() {
   1297         @Override
   1298         protected void configure() {
   1299           install(ThrowingProviderBinder.forModule(this));
   1300         }
   1301 
   1302         @SuppressWarnings("unused")
   1303         @CheckedProvides(ManyMethods.class)
   1304         String foo() {
   1305             return null;
   1306         }
   1307       });
   1308       fail();
   1309     } catch(CreationException ce) {
   1310       assertEquals(ManyMethods.class.getName()
   1311           + " may not declare any new methods, but declared " + Arrays.asList(ManyMethods.class.getDeclaredMethods()),
   1312           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1313     }
   1314   }
   1315 
   1316   public void testIncorrectPredefinedType_Bind() {
   1317     try {
   1318       Guice.createInjector(new AbstractModule() {
   1319         @Override
   1320         protected void configure() {
   1321           ThrowingProviderBinder.create(binder())
   1322               .bind(StringRemoteProvider.class, Integer.class)
   1323               .to(new StringRemoteProvider() {
   1324                 public String get() {
   1325                   return "A";
   1326                 }
   1327               });
   1328         }
   1329       });
   1330       fail();
   1331     } catch(CreationException ce) {
   1332       assertEquals(StringRemoteProvider.class.getName()
   1333           + " expects the value type to be java.lang.String, but it was java.lang.Integer",
   1334           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1335     }
   1336   }
   1337 
   1338   public void testIncorrectPredefinedType_Provides() {
   1339     try {
   1340       Guice.createInjector(new AbstractModule() {
   1341         @Override
   1342         protected void configure() {
   1343           install(ThrowingProviderBinder.forModule(this));
   1344         }
   1345 
   1346         @SuppressWarnings("unused")
   1347         @CheckedProvides(StringRemoteProvider.class)
   1348         Integer foo() {
   1349             return null;
   1350         }
   1351       });
   1352       fail();
   1353     } catch(CreationException ce) {
   1354       assertEquals(StringRemoteProvider.class.getName()
   1355           + " expects the value type to be java.lang.String, but it was java.lang.Integer",
   1356           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1357     }
   1358   }
   1359 
   1360   private static interface TooManyTypeParameters<T, P> extends CheckedProvider<T> {
   1361   }
   1362 
   1363   private static interface WrongThrowingProviderType<T> extends CheckedProvider<String> {
   1364   }
   1365 
   1366   private static interface OneNoneGetMethod<T> extends CheckedProvider<T> {
   1367     T bar();
   1368   }
   1369 
   1370   private static interface ManyMethods<T> extends CheckedProvider<T> {
   1371     T bar();
   1372     String baz();
   1373   }
   1374 
   1375   public void testResultSerializes() throws Exception {
   1376     Result result = Result.forValue("foo");
   1377     result = Asserts.reserialize(result);
   1378     assertEquals("foo", result.getOrThrow());
   1379   }
   1380 
   1381   public void testResultExceptionSerializes() throws Exception {
   1382     Result result = Result.forException(new Exception("boo"));
   1383     result = Asserts.reserialize(result);
   1384     try {
   1385       result.getOrThrow();
   1386       fail();
   1387     } catch(Exception ex) {
   1388       assertEquals("boo", ex.getMessage());
   1389     }
   1390   }
   1391 
   1392   public void testEarlyBindingError() {
   1393     try {
   1394       Guice.createInjector(new AbstractModule() {
   1395         @Override
   1396         protected void configure() {
   1397           ThrowingProviderBinder.create(binder())
   1398               .bind(StringRemoteProvider.class, String.class)
   1399               .to(FailingProvider.class);
   1400         }
   1401       });
   1402       fail();
   1403     } catch(CreationException ce) {
   1404       assertEquals("Could not find a suitable constructor in " + FailingProvider.class.getName()
   1405           + ". Classes must have either one (and only one) constructor annotated with @Inject"
   1406           + " or a zero-argument constructor that is not private.",
   1407           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1408     }
   1409   }
   1410 
   1411   private static class FailingProvider implements StringRemoteProvider {
   1412     // no @Inject.
   1413     @SuppressWarnings("unused")
   1414     FailingProvider(Integer foo) {}
   1415 
   1416     public String get() {
   1417       return null;
   1418     }
   1419   }
   1420 
   1421   public void testNoInjectionPointForUsing() {
   1422     try {
   1423       Guice.createInjector(new AbstractModule() {
   1424         @Override
   1425         protected void configure() {
   1426           ThrowingProviderBinder.create(binder())
   1427               .bind(RemoteProvider.class, Foo.class)
   1428               .providing(InvalidFoo.class);
   1429         }
   1430       });
   1431       fail();
   1432     } catch (CreationException ce) {
   1433       assertEquals("Could not find a suitable constructor in " + InvalidFoo.class.getName()
   1434           + ". Classes must have either one (and only one) constructor annotated with "
   1435           + "@ThrowingInject.",
   1436           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1437     }
   1438   }
   1439 
   1440   static class InvalidFoo implements Foo {
   1441     public InvalidFoo(String dep) {
   1442     }
   1443 
   1444     @Override public String s() { return null; }
   1445   }
   1446 
   1447   public void testNoThrowingInject() {
   1448     try {
   1449       Guice.createInjector(new AbstractModule() {
   1450         @Override
   1451         protected void configure() {
   1452           ThrowingProviderBinder.create(binder())
   1453               .bind(RemoteProvider.class, Foo.class)
   1454               .providing(NormalInjectableFoo.class);
   1455         }
   1456       });
   1457       fail();
   1458     } catch (CreationException ce) {
   1459       assertEquals("Could not find a suitable constructor in " + NormalInjectableFoo.class.getName()
   1460           + ". Classes must have either one (and only one) constructor annotated with "
   1461           + "@ThrowingInject.",
   1462           Iterables.getOnlyElement(ce.getErrorMessages()).getMessage());
   1463     }
   1464   }
   1465 
   1466   static class NormalInjectableFoo implements Foo {
   1467     @Inject
   1468     public NormalInjectableFoo() {
   1469     }
   1470 
   1471     @Override public String s() { return null; }
   1472   }
   1473 
   1474   public void testProvisionExceptionOnDependenciesOfCxtor() throws Exception {
   1475     Injector injector = Guice.createInjector(new AbstractModule() {
   1476         @Override
   1477         protected void configure() {
   1478           ThrowingProviderBinder.create(binder())
   1479               .bind(RemoteProvider.class, Foo.class)
   1480               .providing(ProvisionExceptionFoo.class);
   1481           bindScope(BadScope.class, new Scope() {
   1482             @Override
   1483             public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
   1484               return new Provider<T>() {
   1485                 @Override
   1486                 public T get() {
   1487                   throw new OutOfScopeException("failure");
   1488                 }
   1489               };
   1490             }
   1491           });
   1492         }
   1493       });
   1494 
   1495     try {
   1496       injector.getInstance(Key.get(remoteProviderOfFoo)).get();
   1497       fail();
   1498     } catch(ProvisionException pe) {
   1499       assertEquals(2, pe.getErrorMessages().size());
   1500       List<Message> messages = Lists.newArrayList(pe.getErrorMessages());
   1501       assertEquals("Error in custom provider, com.google.inject.OutOfScopeException: failure",
   1502           messages.get(0).getMessage());
   1503       assertEquals("Error in custom provider, com.google.inject.OutOfScopeException: failure",
   1504           messages.get(1).getMessage());
   1505     }
   1506   }
   1507 
   1508   @ScopeAnnotation
   1509   @Target(ElementType.TYPE)
   1510   @Retention(RetentionPolicy.RUNTIME)
   1511   private @interface BadScope { }
   1512 
   1513   @BadScope private static class Unscoped1 {}
   1514   @BadScope private static class Unscoped2 {}
   1515 
   1516   static class ProvisionExceptionFoo implements Foo {
   1517     @ThrowingInject
   1518     public ProvisionExceptionFoo(Unscoped1 a, Unscoped2 b) {
   1519     }
   1520 
   1521     @Override public String s() { return null; }
   1522   }
   1523 
   1524   public void testUsingDoesntClashWithBindingsOfSameType() throws Exception {
   1525     cxtorInjector = Guice.createInjector(new AbstractModule() {
   1526         @Override
   1527         protected void configure() {
   1528           ThrowingProviderBinder.create(binder())
   1529               .bind(RemoteProvider.class, Foo.class)
   1530               .providing(MockFoo.class);
   1531           bind(Foo.class).to(MockFoo.class);
   1532           bind(MockFoo.class).to(SubMockFoo.class);
   1533         }
   1534       });
   1535 
   1536     RemoteProvider<Foo> remoteProvider =
   1537         cxtorInjector.getInstance(Key.get(remoteProviderOfFoo));
   1538     Foo providerGot = remoteProvider.get();
   1539     Foo fooGot = cxtorInjector.getInstance(Foo.class);
   1540     Foo mockGot = cxtorInjector.getInstance(MockFoo.class);
   1541 
   1542     assertEquals(MockFoo.class, providerGot.getClass());
   1543     assertEquals(SubMockFoo.class, fooGot.getClass());
   1544     assertEquals(SubMockFoo.class, mockGot.getClass());
   1545   }
   1546 
   1547   static class SubMockFoo extends MockFoo {
   1548     public SubMockFoo() throws RemoteException, BindException {
   1549     }
   1550 
   1551   }
   1552 }
   1553