1 package com.google.inject.spi;
2 
3 import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
4 import static java.lang.annotation.RetentionPolicy.RUNTIME;
5 
6 import com.google.inject.AbstractModule;
7 import com.google.inject.Binder;
8 import com.google.inject.Binding;
9 import com.google.inject.BindingAnnotation;
10 import com.google.inject.Module;
11 
12 import junit.framework.TestCase;
13 
14 import java.lang.annotation.Annotation;
15 import java.lang.annotation.ElementType;
16 import java.lang.annotation.Retention;
17 import java.lang.annotation.Target;
18 import java.util.List;
19 
20 /**
21  * Tests for {@link ElementSource}.
22  */
23 public class ElementSourceTest extends TestCase {
24 
25   private static final StackTraceElement BINDER_INSTALL =
26       new StackTraceElement("com.google.inject.spi.Elements$RecordingBinder", "install",
27           "Unknown Source", 234 /* line number*/);
28 
testCallStackSize()29   public void testCallStackSize() {
30     ModuleSource moduleSource = createModuleSource();
31     StackTraceElement[] bindingCallStack = new StackTraceElement[3];
32     bindingCallStack[0] = new StackTraceElement(
33         "com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 200);
34     bindingCallStack[1] = new StackTraceElement(
35         "com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 100);
36     bindingCallStack[2] = new StackTraceElement(
37         "com.google.inject.spi.moduleSourceTest$C", "configure", "Unknown Source", 100);
38     ElementSource elementSource = new ElementSource(
39         null /* No original element source */, "" /* Don't care */, moduleSource, bindingCallStack);
40     assertEquals(10 /* call stack size */, elementSource.getStackTrace().length);
41   }
42 
testGetCallStack_IntegrationTest()43   public void testGetCallStack_IntegrationTest() throws Exception {
44     List<Element> elements = Elements.getElements(new A());
45     for (Element element : elements) {
46       if (element instanceof Binding) {
47         Binding<?> binding = (Binding<?>) element;
48         Class<? extends Annotation> annotationType = binding.getKey().getAnnotationType();
49         if (annotationType != null && annotationType.equals(SampleAnnotation.class)) {
50           ElementSource elementSource = (ElementSource) binding.getSource();
51           List<String> moduleClassNames = elementSource.getModuleClassNames();
52           // Check module class names
53           // Module C
54           assertEquals("com.google.inject.spi.ElementSourceTest$C", moduleClassNames.get(0));
55           // Module B
56           assertEquals("com.google.inject.spi.ElementSourceTest$B", moduleClassNames.get(1));
57           // Module A
58           assertEquals("com.google.inject.spi.ElementSourceTest$A", moduleClassNames.get(2));
59           StackTraceElement[] callStack = elementSource.getStackTrace();
60           switch(getIncludeStackTraceOption()) {
61             case OFF:
62               // Check declaring source
63               StackTraceElement stackTraceElement =
64                   (StackTraceElement) elementSource.getDeclaringSource();
65               assertEquals(new StackTraceElement(
66                   "com.google.inject.spi.ElementSourceTest$C", "configure", null, -1),
67                   stackTraceElement);
68               // Check call stack
69               assertEquals(0, callStack.length);
70               return;
71             case ONLY_FOR_DECLARING_SOURCE:
72                 // Check call stack
73                 assertEquals(0, callStack.length);
74                 return;
75             case COMPLETE:
76               // Check call stack
77               int skippedCallStackSize = new Throwable().getStackTrace().length - 1;
78               assertEquals(skippedCallStackSize + 15, elementSource.getStackTrace().length);
79               assertEquals("com.google.inject.spi.Elements$RecordingBinder",
80                   callStack[0].getClassName());
81               assertEquals("com.google.inject.spi.Elements$RecordingBinder",
82                   callStack[1].getClassName());
83               assertEquals("com.google.inject.AbstractModule",
84                   callStack[2].getClassName());
85               // Module C
86               assertEquals("com.google.inject.spi.ElementSourceTest$C",
87                   callStack[3].getClassName());
88               assertEquals("configure",
89                   callStack[3].getMethodName());
90               assertEquals("Unknown Source",
91                   callStack[3].getFileName());
92               assertEquals("com.google.inject.AbstractModule",
93                   callStack[4].getClassName());
94               assertEquals("com.google.inject.spi.Elements$RecordingBinder",
95                   callStack[5].getClassName());
96               // Module B
97               assertEquals("com.google.inject.spi.ElementSourceTest$B",
98                   callStack[6].getClassName());
99               assertEquals("com.google.inject.spi.Elements$RecordingBinder",
100                   callStack[7].getClassName());
101               // Module A
102               assertEquals("com.google.inject.AbstractModule",
103                   callStack[8].getClassName());
104               assertEquals("com.google.inject.spi.ElementSourceTest$A",
105                   callStack[9].getClassName());
106               assertEquals("com.google.inject.AbstractModule",
107                   callStack[10].getClassName());
108               assertEquals("com.google.inject.spi.Elements$RecordingBinder",
109                   callStack[11].getClassName());
110               assertEquals("com.google.inject.spi.Elements",
111                   callStack[12].getClassName());
112               assertEquals("com.google.inject.spi.Elements",
113                   callStack[13].getClassName());
114               assertEquals("com.google.inject.spi.ElementSourceTest",
115                   callStack[14].getClassName());
116               // Check modules index
117               List<Integer> indexes = elementSource.getModuleConfigurePositionsInStackTrace();
118               assertEquals((int) indexes.get(0), 4);
119               assertEquals((int) indexes.get(1), 6);
120               assertEquals((int) indexes.get(2), 10);
121               return;
122           }
123         }
124       }
125     }
126     fail("The test should not reach this line.");
127   }
128 
createModuleSource()129   private ModuleSource createModuleSource() {
130     // First module
131     StackTraceElement[] partialCallStack = new StackTraceElement[1];
132     partialCallStack[0] = BINDER_INSTALL;
133     ModuleSource moduleSource = new ModuleSource(new A(), partialCallStack);
134     // Second module
135     partialCallStack = new StackTraceElement[2];
136     partialCallStack[0] = BINDER_INSTALL;
137     partialCallStack[1] = new StackTraceElement(
138         "com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100);
139     moduleSource = moduleSource.createChild(new B(), partialCallStack);
140     // Third module
141     partialCallStack = new StackTraceElement[4];
142     partialCallStack[0] = BINDER_INSTALL;
143     partialCallStack[1] = new StackTraceElement("class1", "method1", "Class1.java", 1);
144     partialCallStack[2] = new StackTraceElement("class2", "method2", "Class2.java", 2);
145     partialCallStack[3] = new StackTraceElement(
146         "com.google.inject.spi.moduleSourceTest$B", "configure", "Unknown Source", 200);
147     return moduleSource.createChild(new C(), partialCallStack);
148   }
149 
150   private static class A extends AbstractModule {
151     @Override
configure()152     public void configure() {
153       install(new B());
154     }
155   }
156 
157   private static class B implements Module {
158     @Override
configure(Binder binder)159     public void configure(Binder binder) {
160       binder.install(new C());
161     }
162   }
163 
164   @Retention(RUNTIME)
165   @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
166   @BindingAnnotation
167   @interface SampleAnnotation { }
168 
169   private static class C extends AbstractModule {
170     @Override
configure()171     public void configure() {
172       bind(String.class).annotatedWith(SampleAnnotation.class).toInstance("the value");
173     }
174   }
175 }
176