package com.google.inject.spi;
import static com.google.inject.internal.InternalFlags.getIncludeStackTraceOption;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
import com.google.inject.Module;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;
import junit.framework.TestCase;
/** Tests for {@link ElementSource}. */
public class ElementSourceTest extends TestCase {
private static final StackTraceElement BINDER_INSTALL =
new StackTraceElement(
"com.google.inject.spi.Elements$RecordingBinder",
"install",
"Unknown Source",
234 /* line number*/);
public void testCallStackSize() {
ModuleSource moduleSource = createModuleSource();
StackTraceElement[] bindingCallStack = new StackTraceElement[3];
bindingCallStack[0] =
new StackTraceElement(
"com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 200);
bindingCallStack[1] =
new StackTraceElement(
"com.google.inject.spi.Elements$RecordingBinder", "bind", "Unknown Source", 100);
bindingCallStack[2] =
new StackTraceElement(
"com.google.inject.spi.moduleSourceTest$C", "configure", "Unknown Source", 100);
ElementSource elementSource =
new ElementSource(
null /* No original element source */,
"" /* Don't care */,
moduleSource,
bindingCallStack);
assertEquals(10 /* call stack size */, elementSource.getStackTrace().length);
}
public void testGetCallStack_IntegrationTest() throws Exception {
List<Element> elements = Elements.getElements(new A());
for (Element element : elements) {
if (element instanceof Binding) {
Binding<?> binding = (Binding<?>) element;
Class<? extends Annotation> annotationType = binding.getKey().getAnnotationType();
if (annotationType != null && annotationType.equals(SampleAnnotation.class)) {
ElementSource elementSource = (ElementSource) binding.getSource();
List<String> moduleClassNames = elementSource.getModuleClassNames();
// Check module class names
// Module C
assertEquals("com.google.inject.spi.ElementSourceTest$C", moduleClassNames.get(0));
// Module B
assertEquals("com.google.inject.spi.ElementSourceTest$B", moduleClassNames.get(1));
// Module A
assertEquals("com.google.inject.spi.ElementSourceTest$A", moduleClassNames.get(2));
StackTraceElement[] callStack = elementSource.getStackTrace();
switch (getIncludeStackTraceOption()) {
case OFF:
// Check declaring source
StackTraceElement stackTraceElement =
(StackTraceElement) elementSource.getDeclaringSource();
assertEquals(
new StackTraceElement(
"com.google.inject.spi.ElementSourceTest$C", "configure", null, -1),
stackTraceElement);
// Check call stack
assertEquals(0, callStack.length);
return;
case ONLY_FOR_DECLARING_SOURCE:
// Check call stack
assertEquals(0, callStack.length);
return;
case COMPLETE:
// Check call stack
int skippedCallStackSize = new Throwable().getStackTrace().length - 1;
assertEquals(skippedCallStackSize + 15, elementSource.getStackTrace().length);
assertEquals(
"com.google.inject.spi.Elements$RecordingBinder", callStack[0].getClassName());
assertEquals(
"com.google.inject.spi.Elements$RecordingBinder", callStack[1].getClassName());
assertEquals("com.google.inject.AbstractModule", callStack[2].getClassName());
// Module C
assertEquals(
"com.google.inject.spi.ElementSourceTest$C", callStack[3].getClassName());
assertEquals("configure", callStack[3].getMethodName());
assertEquals("Unknown Source", callStack[3].getFileName());
assertEquals("com.google.inject.AbstractModule", callStack[4].getClassName());
assertEquals(
"com.google.inject.spi.Elements$RecordingBinder", callStack[5].getClassName());
// Module B
assertEquals(
"com.google.inject.spi.ElementSourceTest$B", callStack[6].getClassName());
assertEquals(
"com.google.inject.spi.Elements$RecordingBinder", callStack[7].getClassName());
// Module A
assertEquals("com.google.inject.AbstractModule", callStack[8].getClassName());
assertEquals(
"com.google.inject.spi.ElementSourceTest$A", callStack[9].getClassName());
assertEquals("com.google.inject.AbstractModule", callStack[10].getClassName());
assertEquals(
"com.google.inject.spi.Elements$RecordingBinder", callStack[11].getClassName());
assertEquals("com.google.inject.spi.Elements", callStack[12].getClassName());
assertEquals("com.google.inject.spi.Elements", callStack[13].getClassName());
assertEquals("com.google.inject.spi.ElementSourceTest", callStack[14].getClassName());
// Check modules index
List<Integer> indexes = elementSource.getModuleConfigurePositionsInStackTrace();
assertEquals((int) indexes.get(0), 4);
assertEquals((int) indexes.get(1), 6);
assertEquals((int) indexes.get(2), 10);
return;
}
}
}
}
fail("The test should not reach this line.");
}
private ModuleSource createModuleSource() {
// First module
StackTraceElement[] partialCallStack = new StackTraceElement[1];
partialCallStack[0] = BINDER_INSTALL;
ModuleSource moduleSource = new ModuleSource(new A(), partialCallStack);
// Second module
partialCallStack = new StackTraceElement[2];
partialCallStack[0] = BINDER_INSTALL;
partialCallStack[1] =
new StackTraceElement(
"com.google.inject.spi.moduleSourceTest$A", "configure", "Unknown Source", 100);
moduleSource = moduleSource.createChild(new B(), partialCallStack);
// Third module
partialCallStack = new StackTraceElement[4];
partialCallStack[0] = BINDER_INSTALL;
partialCallStack[1] = new StackTraceElement("class1", "method1", "Class1.java", 1);
partialCallStack[2] = new StackTraceElement("class2", "method2", "Class2.java", 2);
partialCallStack[3] =
new StackTraceElement(
"com.google.inject.spi.moduleSourceTest$B", "configure", "Unknown Source", 200);
return moduleSource.createChild(new C(), partialCallStack);
}
private static class A extends AbstractModule {
@Override
public void configure() {
install(new B());
}
}
private static class B implements Module {
@Override
public void configure(Binder binder) {
binder.install(new C());
}
}
@Retention(RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@BindingAnnotation
@interface SampleAnnotation {}
private static class C extends AbstractModule {
@Override
public void configure() {
bind(String.class).annotatedWith(SampleAnnotation.class).toInstance("the value");
}
}
}