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
17package com.google.inject.throwingproviders;
18
19import static java.lang.annotation.ElementType.METHOD;
20import static java.lang.annotation.RetentionPolicy.RUNTIME;
21
22import com.google.common.base.Function;
23import com.google.common.collect.ImmutableList;
24import com.google.common.collect.ImmutableSet;
25import com.google.common.collect.Iterables;
26import com.google.common.collect.Lists;
27import com.google.inject.AbstractModule;
28import com.google.inject.Asserts;
29import com.google.inject.BindingAnnotation;
30import com.google.inject.CreationException;
31import com.google.inject.Guice;
32import com.google.inject.Inject;
33import com.google.inject.Injector;
34import com.google.inject.Key;
35import com.google.inject.OutOfScopeException;
36import com.google.inject.Provider;
37import com.google.inject.ProvisionException;
38import com.google.inject.Scope;
39import com.google.inject.ScopeAnnotation;
40import com.google.inject.TypeLiteral;
41import com.google.inject.internal.util.Classes;
42import com.google.inject.name.Named;
43import com.google.inject.name.Names;
44import com.google.inject.spi.Dependency;
45import com.google.inject.spi.HasDependencies;
46import com.google.inject.spi.Message;
47import com.google.inject.throwingproviders.ThrowingProviderBinder.Result;
48
49import junit.framework.TestCase;
50
51import java.io.IOException;
52import java.lang.annotation.Annotation;
53import java.lang.annotation.ElementType;
54import java.lang.annotation.Retention;
55import java.lang.annotation.RetentionPolicy;
56import java.lang.annotation.Target;
57import java.net.BindException;
58import java.rmi.AccessException;
59import java.rmi.RemoteException;
60import java.util.ArrayList;
61import java.util.Arrays;
62import java.util.List;
63import java.util.Set;
64import java.util.TooManyListenersException;
65
66/**
67 * @author jmourits@google.com (Jerome Mourits)
68 * @author jessewilson@google.com (Jesse Wilson)
69 * @author sameb@google.com (Sam Berlin)
70 */
71public 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