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"); } } }