• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.testing.fieldbinder;
18 
19 import static com.google.inject.Asserts.assertContains;
20 import static java.lang.annotation.RetentionPolicy.RUNTIME;
21 
22 import com.google.inject.BindingAnnotation;
23 import com.google.inject.ConfigurationException;
24 import com.google.inject.CreationException;
25 import com.google.inject.Guice;
26 import com.google.inject.Inject;
27 import com.google.inject.Injector;
28 import com.google.inject.Key;
29 import com.google.inject.Provider;
30 import com.google.inject.ProvisionException;
31 import com.google.inject.name.Named;
32 import com.google.inject.name.Names;
33 import com.google.inject.util.Providers;
34 import java.lang.annotation.Retention;
35 import java.util.Arrays;
36 import java.util.List;
37 import javax.inject.Qualifier;
38 import junit.framework.TestCase;
39 
40 /** Unit tests for {@link BoundFieldModule}. */
41 public class BoundFieldModuleTest extends TestCase {
testBindingNothing()42   public void testBindingNothing() {
43     Object instance = new Object() {};
44 
45     BoundFieldModule module = BoundFieldModule.of(instance);
46     Guice.createInjector(module);
47 
48     // If we didn't throw an exception, we succeeded.
49   }
50 
testBindingOnePrivate()51   public void testBindingOnePrivate() {
52     final Integer testValue = 1024;
53     Object instance =
54         new Object() {
55           @Bind private Integer anInt = testValue;
56         };
57 
58     BoundFieldModule module = BoundFieldModule.of(instance);
59     Injector injector = Guice.createInjector(module);
60 
61     assertEquals(testValue, injector.getInstance(Integer.class));
62   }
63 
testBindingOnePublic()64   public void testBindingOnePublic() {
65     final Integer testValue = 1024;
66     Object instance =
67         new Object() {
68           @Bind public Integer anInt = testValue;
69         };
70 
71     BoundFieldModule module = BoundFieldModule.of(instance);
72     Injector injector = Guice.createInjector(module);
73 
74     assertEquals(testValue, injector.getInstance(Integer.class));
75   }
76 
77   private static class FieldBindableClass {
78     @Bind Integer anInt;
79 
FieldBindableClass(Integer anInt)80     FieldBindableClass(Integer anInt) {
81       this.anInt = anInt;
82     }
83   }
84 
85   private static class FieldBindableSubclass extends FieldBindableClass {
FieldBindableSubclass(Integer anInt)86     FieldBindableSubclass(Integer anInt) {
87       super(anInt);
88     }
89   }
90 
testSuperTypeBinding()91   public void testSuperTypeBinding() {
92     FieldBindableSubclass instance = new FieldBindableSubclass(1024);
93 
94     BoundFieldModule module = BoundFieldModule.of(instance);
95     Injector injector = Guice.createInjector(module);
96 
97     assertEquals(instance.anInt, injector.getInstance(Integer.class));
98   }
99 
testBindingTwo()100   public void testBindingTwo() {
101     final Integer testValue = 1024;
102     final String testString = "Hello World!";
103     Object instance =
104         new Object() {
105           @Bind private Integer anInt = testValue;
106           @Bind private String aString = testString;
107         };
108 
109     BoundFieldModule module = BoundFieldModule.of(instance);
110     Injector injector = Guice.createInjector(module);
111 
112     assertEquals(testValue, injector.getInstance(Integer.class));
113     assertEquals(testString, injector.getInstance(String.class));
114   }
115 
testBindingSuperType()116   public void testBindingSuperType() {
117     final Integer testValue = 1024;
118     Object instance =
119         new Object() {
120           @Bind(to = Number.class)
121           private Integer anInt = testValue;
122         };
123 
124     BoundFieldModule module = BoundFieldModule.of(instance);
125     Injector injector = Guice.createInjector(module);
126 
127     assertEquals(testValue, injector.getInstance(Number.class));
128   }
129 
testBindingSuperTypeAccessSubType()130   public void testBindingSuperTypeAccessSubType() {
131     final Integer testValue = 1024;
132     Object instance =
133         new Object() {
134           @Bind(to = Number.class)
135           private Integer anInt = testValue;
136         };
137 
138     BoundFieldModule module = BoundFieldModule.of(instance);
139     Injector injector = Guice.createInjector(module);
140 
141     try {
142       injector.getInstance(Integer.class);
143       fail();
144     } catch (ConfigurationException e) {
145       assertContains(e.getMessage(), "Could not find a suitable constructor in java.lang.Integer");
146     }
147   }
148 
testBindingIncorrectTypeProviderFails()149   public void testBindingIncorrectTypeProviderFails() {
150     final Integer testValue = 1024;
151     Object instance =
152         new Object() {
153           @Bind(to = String.class)
154           private Provider<Integer> anIntProvider =
155               new Provider<Integer>() {
156                 @Override
157                 public Integer get() {
158                   return testValue;
159                 }
160               };
161         };
162 
163     BoundFieldModule module = BoundFieldModule.of(instance);
164 
165     try {
166       Guice.createInjector(module);
167       fail();
168     } catch (CreationException e) {
169       assertContains(
170           e.getMessage(),
171           "Requested binding type \"java.lang.String\" is not "
172               + "assignable from field binding type \"java.lang.Integer\"");
173     }
174   }
175 
176   @BindingAnnotation
177   @Retention(RUNTIME)
178   private static @interface SomeBindingAnnotation {}
179 
testBindingWithBindingAnnotation()180   public void testBindingWithBindingAnnotation() {
181     final Integer testValue1 = 1024, testValue2 = 2048;
182     Object instance =
183         new Object() {
184           @Bind private Integer anInt = testValue1;
185 
186           @Bind @SomeBindingAnnotation private Integer anotherInt = testValue2;
187         };
188 
189     BoundFieldModule module = BoundFieldModule.of(instance);
190     Injector injector = Guice.createInjector(module);
191 
192     assertEquals(testValue1, injector.getInstance(Integer.class));
193     assertEquals(
194         testValue2, injector.getInstance(Key.get(Integer.class, SomeBindingAnnotation.class)));
195   }
196 
197   @Qualifier
198   @Retention(RUNTIME)
199   private static @interface SomeQualifier {}
200 
testBindingWithQualifier()201   public void testBindingWithQualifier() {
202     final Integer testValue1 = 1024, testValue2 = 2048;
203     Object instance =
204         new Object() {
205           @Bind private Integer anInt = testValue1;
206 
207           @Bind @SomeQualifier private Integer anotherInt = testValue2;
208         };
209 
210     BoundFieldModule module = BoundFieldModule.of(instance);
211     Injector injector = Guice.createInjector(module);
212 
213     assertEquals(testValue1, injector.getInstance(Integer.class));
214     assertEquals(testValue2, injector.getInstance(Key.get(Integer.class, SomeQualifier.class)));
215   }
216 
testCanReuseBindingAnnotationsWithDifferentValues()217   public void testCanReuseBindingAnnotationsWithDifferentValues() {
218     final Integer testValue1 = 1024, testValue2 = 2048;
219     final String name1 = "foo", name2 = "bar";
220     Object instance =
221         new Object() {
222           @Bind
223           @Named(name1)
224           private Integer anInt = testValue1;
225 
226           @Bind
227           @Named(name2)
228           private Integer anotherInt = testValue2;
229         };
230 
231     BoundFieldModule module = BoundFieldModule.of(instance);
232     Injector injector = Guice.createInjector(module);
233 
234     assertEquals(testValue1, injector.getInstance(Key.get(Integer.class, Names.named(name1))));
235     assertEquals(testValue2, injector.getInstance(Key.get(Integer.class, Names.named(name2))));
236   }
237 
testBindingWithValuedBindingAnnotation()238   public void testBindingWithValuedBindingAnnotation() {
239     final Integer testValue1 = 1024, testValue2 = 2048;
240     final String name = "foo";
241     Object instance =
242         new Object() {
243           @Bind private Integer anInt = testValue1;
244 
245           @Bind
246           @Named(name)
247           private Integer anotherInt = testValue2;
248         };
249 
250     BoundFieldModule module = BoundFieldModule.of(instance);
251     Injector injector = Guice.createInjector(module);
252 
253     assertEquals(testValue1, injector.getInstance(Integer.class));
254     assertEquals(testValue2, injector.getInstance(Key.get(Integer.class, Names.named(name))));
255   }
256 
testBindingWithGenerics()257   public void testBindingWithGenerics() {
258     final List<Integer> testIntList = Arrays.asList(new Integer[] {1, 2, 3});
259     final List<Boolean> testBoolList = Arrays.asList(new Boolean[] {true, true, false});
260     Object instance =
261         new Object() {
262           @Bind private List<Integer> anIntList = testIntList;
263           @Bind private List<Boolean> aBoolList = testBoolList;
264         };
265 
266     BoundFieldModule module = BoundFieldModule.of(instance);
267     Injector injector = Guice.createInjector(module);
268 
269     assertEquals(testIntList, injector.getInstance(new Key<List<Integer>>() {}));
270     assertEquals(testBoolList, injector.getInstance(new Key<List<Boolean>>() {}));
271   }
272 
testBoundValueDoesntChange()273   public void testBoundValueDoesntChange() {
274     Integer testValue = 1024;
275     FieldBindableClass instance = new FieldBindableClass(testValue);
276 
277     BoundFieldModule module = BoundFieldModule.of(instance);
278     Injector injector = Guice.createInjector(module);
279 
280     assertEquals(testValue, injector.getInstance(Integer.class));
281     instance.anInt++;
282     assertEquals(testValue, injector.getInstance(Integer.class));
283   }
284 
testIncompatibleBindingType()285   public void testIncompatibleBindingType() {
286     final Integer testInt = 1024;
287     Object instance =
288         new Object() {
289           @Bind(to = String.class)
290           private Integer anInt = testInt;
291         };
292 
293     BoundFieldModule module = BoundFieldModule.of(instance);
294 
295     try {
296       Guice.createInjector(module);
297       fail();
298     } catch (CreationException e) {
299       assertContains(
300           e.getMessage(),
301           "Requested binding type \"java.lang.String\" is not assignable from field binding type "
302               + "\"java.lang.Integer\"");
303     }
304   }
305 
testFailureOnMultipleBindingAnnotations()306   public void testFailureOnMultipleBindingAnnotations() {
307     final Integer testInt = 1024;
308     Object instance =
309         new Object() {
310           @Bind
311           @Named("a")
312           @SomeBindingAnnotation
313           private Integer anInt = testInt;
314         };
315 
316     BoundFieldModule module = BoundFieldModule.of(instance);
317 
318     try {
319       Guice.createInjector(module);
320       fail();
321     } catch (CreationException e) {
322       assertContains(e.getMessage(), "More than one annotation is specified for this binding.");
323     }
324   }
325 
testBindingSuperTypeAndBindingAnnotation()326   public void testBindingSuperTypeAndBindingAnnotation() {
327     final Integer testValue = 1024;
328     Object instance =
329         new Object() {
330           @Bind(to = Number.class)
331           @Named("foo")
332           private Integer anInt = testValue;
333         };
334 
335     BoundFieldModule module = BoundFieldModule.of(instance);
336     Injector injector = Guice.createInjector(module);
337 
338     assertEquals(testValue, injector.getInstance(Key.get(Number.class, Names.named("foo"))));
339   }
340 
testBindingProvider()341   public void testBindingProvider() {
342     final Integer testValue = 1024;
343     Object instance =
344         new Object() {
345           @Bind
346           private Provider<Integer> anInt =
347               new Provider<Integer>() {
348                 @Override
349                 public Integer get() {
350                   return testValue;
351                 }
352               };
353         };
354 
355     BoundFieldModule module = BoundFieldModule.of(instance);
356     Injector injector = Guice.createInjector(module);
357 
358     assertEquals(testValue, injector.getInstance(Integer.class));
359   }
360 
testBindingJavaxProvider()361   public void testBindingJavaxProvider() {
362     final Integer testValue = 1024;
363     Object instance =
364         new Object() {
365           @Bind
366           private javax.inject.Provider<Integer> anInt =
367               new javax.inject.Provider<Integer>() {
368                 @Override
369                 public Integer get() {
370                   return testValue;
371                 }
372               };
373         };
374 
375     BoundFieldModule module = BoundFieldModule.of(instance);
376     Injector injector = Guice.createInjector(module);
377 
378     assertEquals(testValue, injector.getInstance(Integer.class));
379   }
380 
testBindingNonNullableNullField()381   public void testBindingNonNullableNullField() {
382     Object instance =
383         new Object() {
384           @Bind private Integer anInt = null;
385         };
386 
387     BoundFieldModule module = BoundFieldModule.of(instance);
388 
389     try {
390       Guice.createInjector(module);
391       fail();
392     } catch (CreationException e) {
393       assertContains(
394           e.getMessage(),
395           "Binding to null values is only allowed for fields that are annotated @Nullable.");
396     }
397   }
398 
399   @Retention(RUNTIME)
400   private @interface Nullable {}
401 
testBindingNullableNullField()402   public void testBindingNullableNullField() {
403     Object instance =
404         new Object() {
405           @Bind @Nullable private Integer anInt = null;
406         };
407 
408     Injector injector = Guice.createInjector(BoundFieldModule.of(instance));
409     assertNull(injector.getInstance(Integer.class));
410   }
411 
testBindingNullProvider()412   public void testBindingNullProvider() {
413     Object instance =
414         new Object() {
415           @Bind private Provider<Integer> anIntProvider = null;
416         };
417 
418     BoundFieldModule module = BoundFieldModule.of(instance);
419 
420     try {
421       Guice.createInjector(module);
422       fail();
423     } catch (CreationException e) {
424       assertContains(
425           e.getMessage(),
426           "Binding to null is not allowed. Use Providers.of(null) if this is your intended "
427               + "behavior.");
428     }
429   }
430 
testBindingNullableNullProvider()431   public void testBindingNullableNullProvider() {
432     Object instance =
433         new Object() {
434           @Bind @Nullable private Provider<Integer> anIntProvider = null;
435         };
436 
437     BoundFieldModule module = BoundFieldModule.of(instance);
438 
439     try {
440       Guice.createInjector(module);
441       fail();
442     } catch (CreationException e) {
443       assertContains(
444           e.getMessage(),
445           "Binding to null is not allowed. Use Providers.of(null) if this is your intended "
446               + "behavior.");
447     }
448   }
449 
450   private static class IntegerProvider implements Provider<Integer> {
451     private final Integer value;
452 
IntegerProvider(Integer value)453     IntegerProvider(Integer value) {
454       this.value = value;
455     }
456 
457     @Override
get()458     public Integer get() {
459       return value;
460     }
461   }
462 
testProviderSubclassesBindToTheProviderItself()463   public void testProviderSubclassesBindToTheProviderItself() {
464     final IntegerProvider integerProvider = new IntegerProvider(1024);
465     Object instance =
466         new Object() {
467           @Bind private IntegerProvider anIntProvider = integerProvider;
468         };
469 
470     BoundFieldModule module = BoundFieldModule.of(instance);
471     Injector injector = Guice.createInjector(module);
472 
473     assertEquals(integerProvider, injector.getInstance(IntegerProvider.class));
474   }
475 
testProviderSubclassesDoNotBindParameterizedType()476   public void testProviderSubclassesDoNotBindParameterizedType() {
477     final Integer testValue = 1024;
478     Object instance =
479         new Object() {
480           @Bind private IntegerProvider anIntProvider = new IntegerProvider(testValue);
481         };
482 
483     BoundFieldModule module = BoundFieldModule.of(instance);
484     Injector injector = Guice.createInjector(module);
485 
486     try {
487       injector.getInstance(Integer.class);
488       fail();
489     } catch (ConfigurationException e) {
490       assertContains(e.getMessage(), "Could not find a suitable constructor in java.lang.Integer.");
491     }
492   }
493 
testNullableProviderSubclassesAllowNull()494   public void testNullableProviderSubclassesAllowNull() {
495     Object instance =
496         new Object() {
497           @Bind @Nullable private IntegerProvider anIntProvider = null;
498         };
499 
500     BoundFieldModule module = BoundFieldModule.of(instance);
501     Injector injector = Guice.createInjector(module);
502 
503     assertNull(injector.getInstance(IntegerProvider.class));
504   }
505 
506   private static class ParameterizedObject<T> {
ParameterizedObject(T instance)507     ParameterizedObject(T instance) {
508       this.instance = instance;
509     }
510 
511     @Bind private T instance;
512   }
513 
testBindParameterizedTypeFails()514   public void testBindParameterizedTypeFails() {
515     ParameterizedObject<Integer> instance = new ParameterizedObject<>(0);
516 
517     BoundFieldModule module = BoundFieldModule.of(instance);
518 
519     try {
520       Guice.createInjector(module);
521       fail();
522     } catch (CreationException e) {
523       assertContains(e.getMessage(), "T cannot be used as a key; It is not fully specified.");
524     }
525   }
526 
testBindSubclassOfParameterizedTypeSucceeds()527   public void testBindSubclassOfParameterizedTypeSucceeds() {
528     final Integer testValue = 1024;
529     ParameterizedObject<Integer> instance = new ParameterizedObject<Integer>(testValue) {};
530 
531     BoundFieldModule module = BoundFieldModule.of(instance);
532     Injector injector = Guice.createInjector(module);
533 
534     assertEquals(testValue, injector.getInstance(Integer.class));
535   }
536 
testBindArray()537   public void testBindArray() {
538     final Integer[] testArray = new Integer[] {1024, 2048};
539     Object instance =
540         new Object() {
541           @Bind private Integer[] anIntArray = testArray;
542         };
543 
544     BoundFieldModule module = BoundFieldModule.of(instance);
545     Injector injector = Guice.createInjector(module);
546 
547     assertEquals(testArray, injector.getInstance(Integer[].class));
548   }
549 
testRawProviderCannotBeBound()550   public void testRawProviderCannotBeBound() {
551     final Integer testValue = 1024;
552     Object instance =
553         new Object() {
554           @Bind
555           private Provider anIntProvider =
556               new Provider() {
557                 @Override
558                 public Object get() {
559                   return testValue;
560                 }
561               };
562         };
563 
564     BoundFieldModule module = BoundFieldModule.of(instance);
565 
566     try {
567       Guice.createInjector(module);
568       fail();
569     } catch (CreationException e) {
570       assertContains(
571           e.getMessage(),
572           "Non parameterized Provider fields must have an "
573               + "explicit binding class via @Bind(to = Foo.class)");
574     }
575   }
576 
testExplicitlyBoundRawProviderCanBeBound()577   public void testExplicitlyBoundRawProviderCanBeBound() {
578     final Integer testValue = 1024;
579     Object instance =
580         new Object() {
581           @Bind(to = Integer.class)
582           private Provider anIntProvider =
583               new Provider() {
584                 @Override
585                 public Object get() {
586                   return testValue;
587                 }
588               };
589         };
590 
591     BoundFieldModule module = BoundFieldModule.of(instance);
592     Injector injector = Guice.createInjector(module);
593 
594     assertEquals(testValue, injector.getInstance(Integer.class));
595   }
596 
testRawProviderCanBindToIncorrectType()597   public void testRawProviderCanBindToIncorrectType() {
598     final Integer testValue = 1024;
599     Object instance =
600         new Object() {
601           @Bind(to = String.class)
602           private Provider anIntProvider =
603               new Provider() {
604                 @Override
605                 public Object get() {
606                   return testValue;
607                 }
608               };
609         };
610 
611     BoundFieldModule module = BoundFieldModule.of(instance);
612     Injector injector = Guice.createInjector(module);
613 
614     assertEquals(testValue, injector.getInstance(String.class));
615   }
616 
testMultipleErrorsAreAggregated()617   public void testMultipleErrorsAreAggregated() {
618     Object instance =
619         new Object() {
620           @Bind private Provider aProvider;
621 
622           @Bind(to = String.class)
623           private Integer anInt;
624         };
625 
626     BoundFieldModule module = BoundFieldModule.of(instance);
627     try {
628       Guice.createInjector(module);
629       fail();
630     } catch (CreationException e) {
631       assertEquals(2, e.getErrorMessages().size());
632     }
633   }
634 
testBindingProviderWithProviderSubclassValue()635   public void testBindingProviderWithProviderSubclassValue() {
636     final Integer testValue = 1024;
637     Object instance =
638         new Object() {
639           @Bind private Provider<Integer> anIntProvider = new IntegerProvider(testValue);
640         };
641 
642     BoundFieldModule module = BoundFieldModule.of(instance);
643     Injector injector = Guice.createInjector(module);
644 
645     assertEquals(testValue, injector.getInstance(Integer.class));
646   }
647 
testBoundFieldsCannotBeInjected()648   public void testBoundFieldsCannotBeInjected() {
649     Object instance =
650         new Object() {
651           @Bind @Inject Integer anInt = 0;
652         };
653 
654     BoundFieldModule module = BoundFieldModule.of(instance);
655 
656     try {
657       Guice.createInjector(module);
658       fail();
659     } catch (CreationException e) {
660       assertContains(e.getMessage(), "Fields annotated with both @Bind and @Inject are illegal.");
661     }
662   }
663 
testIncrementingProvider()664   public void testIncrementingProvider() {
665     final Integer testBaseValue = 1024;
666     Object instance =
667         new Object() {
668           @Bind
669           private Provider<Integer> anIntProvider =
670               new Provider<Integer>() {
671                 private int value = testBaseValue;
672 
673                 @Override
674                 public Integer get() {
675                   return value++;
676                 }
677               };
678         };
679 
680     BoundFieldModule module = BoundFieldModule.of(instance);
681     Injector injector = Guice.createInjector(module);
682 
683     assertEquals(testBaseValue, injector.getInstance(Integer.class));
684     assertEquals((Integer) (testBaseValue + 1), injector.getInstance(Integer.class));
685     assertEquals((Integer) (testBaseValue + 2), injector.getInstance(Integer.class));
686   }
687 
testProviderDoesNotProvideDuringInjectorConstruction()688   public void testProviderDoesNotProvideDuringInjectorConstruction() {
689     Object instance =
690         new Object() {
691           @Bind
692           private Provider<Integer> myIntProvider =
693               new Provider<Integer>() {
694                 @Override
695                 public Integer get() {
696                   throw new UnsupportedOperationException();
697                 }
698               };
699         };
700 
701     BoundFieldModule module = BoundFieldModule.of(instance);
702     Guice.createInjector(module);
703 
704     // If we don't throw an exception, we succeeded.
705   }
706 
707   private static class InvalidBindableClass {
708     @Bind(to = String.class)
709     Integer anInt;
710   }
711 
testIncompatibleBindingTypeStackTraceHasUserFrame()712   public void testIncompatibleBindingTypeStackTraceHasUserFrame() {
713     Object instance = new InvalidBindableClass();
714 
715     BoundFieldModule module = BoundFieldModule.of(instance);
716 
717     try {
718       Guice.createInjector(module);
719       fail();
720     } catch (CreationException e) {
721       assertContains(e.getMessage(), "at " + InvalidBindableClass.class.getName() + " field anInt");
722     }
723   }
724 
725   private static class InjectedNumberProvider implements Provider<Number> {
726     @Inject Integer anInt;
727 
728     @Override
get()729     public Number get() {
730       return anInt;
731     }
732   }
733 
testBoundProvidersAreInjected()734   public void testBoundProvidersAreInjected() {
735     final Integer testValue = 1024;
736     Object instance =
737         new Object() {
738           @Bind private Integer anInt = testValue;
739           @Bind private Provider<Number> aNumberProvider = new InjectedNumberProvider();
740         };
741 
742     BoundFieldModule module = BoundFieldModule.of(instance);
743     Injector injector = Guice.createInjector(module);
744 
745     assertEquals(testValue, injector.getInstance(Number.class));
746   }
747 
testBoundInstancesAreInjected()748   public void testBoundInstancesAreInjected() {
749     final Integer testValue = 1024;
750     final InjectedNumberProvider testNumberProvider = new InjectedNumberProvider();
751     Object instance =
752         new Object() {
753           @Bind private Integer anInt = testValue;
754           @Bind private InjectedNumberProvider aNumberProvider = testNumberProvider;
755         };
756 
757     BoundFieldModule module = BoundFieldModule.of(instance);
758     Guice.createInjector(module);
759 
760     assertEquals(testValue, testNumberProvider.anInt);
761   }
762 
763   private static class InvalidBindableSubclass extends InvalidBindableClass {}
764 
testClassIsPrintedInErrorsWhenCauseIsSuperclass()765   public void testClassIsPrintedInErrorsWhenCauseIsSuperclass() {
766     Object instance = new InvalidBindableSubclass();
767 
768     BoundFieldModule module = BoundFieldModule.of(instance);
769 
770     try {
771       Guice.createInjector(module);
772       fail();
773     } catch (CreationException e) {
774       assertContains(
775           e.getMessage(),
776           "Requested binding type \"java.lang.String\" is not assignable from field binding type "
777               + "\"java.lang.Integer\"");
778     }
779   }
780 
781   private static class FieldBindableSubclass2 extends FieldBindableClass {
782     @Bind Number aNumber;
783 
FieldBindableSubclass2(Integer anInt, Number aNumber)784     FieldBindableSubclass2(Integer anInt, Number aNumber) {
785       super(anInt);
786       this.aNumber = aNumber;
787     }
788   }
789 
testFieldsAreBoundFromFullClassHierarchy()790   public void testFieldsAreBoundFromFullClassHierarchy() {
791     final Integer testValue1 = 1024, testValue2 = 2048;
792     FieldBindableSubclass2 instance = new FieldBindableSubclass2(testValue1, testValue2);
793 
794     BoundFieldModule module = BoundFieldModule.of(instance);
795     Injector injector = Guice.createInjector(module);
796 
797     assertEquals(testValue1, injector.getInstance(Integer.class));
798     assertEquals(testValue2, injector.getInstance(Number.class));
799   }
800 
801   static final class LazyClass {
802     @Bind(lazy = true)
803     Integer foo = 1;
804   }
805 
testFieldBound_lazy()806   public void testFieldBound_lazy() {
807     LazyClass asProvider = new LazyClass();
808     Injector injector = Guice.createInjector(BoundFieldModule.of(asProvider));
809     assertEquals(1, injector.getInstance(Integer.class).intValue());
810     asProvider.foo++;
811     assertEquals(2, injector.getInstance(Integer.class).intValue());
812   }
813 
testNonNullableFieldBound_lazy_rejectNull()814   public void testNonNullableFieldBound_lazy_rejectNull() {
815     LazyClass asProvider = new LazyClass();
816     Injector injector = Guice.createInjector(BoundFieldModule.of(asProvider));
817     assertEquals(1, injector.getInstance(Integer.class).intValue());
818     asProvider.foo = null;
819     try {
820       injector.getInstance(Integer.class);
821       fail();
822     } catch (ProvisionException e) {
823       assertContains(
824           e.getMessage(),
825           "Binding to null values is only allowed for fields that are annotated @Nullable.");
826     }
827   }
828 
829   static final class LazyClassNullable {
830     @Bind(lazy = true)
831     @Nullable
832     Integer foo = 1;
833   }
834 
testNullableFieldBound_lazy_allowNull()835   public void testNullableFieldBound_lazy_allowNull() {
836     LazyClassNullable asProvider = new LazyClassNullable();
837     Injector injector = Guice.createInjector(BoundFieldModule.of(asProvider));
838     assertEquals(1, injector.getInstance(Integer.class).intValue());
839     asProvider.foo = null;
840     assertNull(injector.getInstance(Integer.class));
841   }
842 
843   static final class LazyProviderClass {
844     @Bind(lazy = true)
845     Provider<Integer> foo = Providers.of(null);
846   }
847 
testFieldBoundAsProvider_lazy()848   public void testFieldBoundAsProvider_lazy() {
849     LazyProviderClass asProvider = new LazyProviderClass();
850     Provider<Integer> provider =
851         Guice.createInjector(BoundFieldModule.of(asProvider)).getProvider(Integer.class);
852     assertNull(provider.get());
853     asProvider.foo = Providers.of(1);
854     assertEquals(1, provider.get().intValue());
855     asProvider.foo =
856         new Provider<Integer>() {
857           @Override
858           public Integer get() {
859             throw new RuntimeException("boom");
860           }
861         };
862     try {
863       provider.get();
864       fail();
865     } catch (ProvisionException e) {
866       assertContains(e.getMessage(), "boom");
867     }
868   }
869 
870   private static final class LazyNonTransparentProvider {
871     @Bind(lazy = true)
872     @Nullable
873     private IntegerProvider anIntProvider = null;
874   }
875 
testFieldBoundAsNonTransparentProvider_lazy()876   public void testFieldBoundAsNonTransparentProvider_lazy() {
877     LazyNonTransparentProvider instance = new LazyNonTransparentProvider();
878     BoundFieldModule module = BoundFieldModule.of(instance);
879     Injector injector = Guice.createInjector(module);
880 
881     assertNull(injector.getInstance(IntegerProvider.class));
882     instance.anIntProvider = new IntegerProvider(3);
883     assertEquals(3, injector.getInstance(IntegerProvider.class).get().intValue());
884     try {
885       injector.getInstance(Integer.class);
886       fail();
887     } catch (ConfigurationException expected) {
888       // expected because we don't interpret IntegerProvider as a Provider<Integer>
889     }
890   }
891 }
892