package rocks.inspectit.agent.java.instrumentation.asm; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import info.novatec.inspectit.org.objectweb.asm.ClassReader; import info.novatec.inspectit.org.objectweb.asm.ClassWriter; import info.novatec.inspectit.org.objectweb.asm.MethodVisitor; import info.novatec.inspectit.org.objectweb.asm.Opcodes; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.Collections; import org.mockito.ArgumentCaptor; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.springframework.asm.Type; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import rocks.inspectit.agent.java.IAgent; import rocks.inspectit.agent.java.hooking.IHookDispatcher; import rocks.inspectit.agent.java.instrumentation.InstrumenterFactory; import rocks.inspectit.shared.all.instrumentation.config.IMethodInstrumentationPoint; import rocks.inspectit.shared.all.instrumentation.config.impl.MethodInstrumentationConfig; import rocks.inspectit.shared.all.instrumentation.config.impl.SensorInstrumentationPoint; import rocks.inspectit.shared.all.instrumentation.config.impl.SpecialInstrumentationPoint; /** * Tests all instrumenters that we have in connection to the {@link ClassInstrumenter}. * * @author Ivan Senic * */ @SuppressWarnings("PMD") public class ClassInstrumenterTest extends AbstractInstrumentationTest { protected static final String TEST_CLASS_FQN = "rocks.inspectit.agent.java.instrumentation.asm.InstrumentationTestClass"; protected static final String EXCEPTION_TEST_CLASS_FQN = "rocks.inspectit.agent.java.instrumentation.asm.InstrumentationExceptionTestClass"; protected static final String TEST_CLASS_LOADER_FQN = "rocks.inspectit.agent.java.instrumentation.asm.MyTestClassLoader"; protected static final Answer<MethodVisitor> METHOD_INSTRUMENTER_ANSWER = new Answer<MethodVisitor>() { @Override public MethodVisitor answer(InvocationOnMock invocation) throws Throwable { Object[] arguments = invocation.getArguments(); SensorInstrumentationPoint sip = (SensorInstrumentationPoint) arguments[0]; return getMethodInstrumenter((MethodVisitor) arguments[1], (Integer) arguments[2], (String) arguments[3], (String) arguments[4], sip.getId(), (Boolean) arguments[5]); } }; protected static final Answer<MethodVisitor> CONSTRUCTOR_INSTRUMENTER_ANSWER = new Answer<MethodVisitor>() { @Override public MethodVisitor answer(InvocationOnMock invocation) throws Throwable { Object[] arguments = invocation.getArguments(); SensorInstrumentationPoint sip = (SensorInstrumentationPoint) arguments[0]; return getConstructorInstrumenter((MethodVisitor) arguments[1], (Integer) arguments[2], (String) arguments[3], (String) arguments[4], sip.getId(), (Boolean) arguments[5]); } }; public static IHookDispatcher dispatcher; public static IAgent a; @Mock IHookDispatcher hookDispatcher; @Mock IAgent agent; @Mock InstrumenterFactory instrumenterFactory; @Mock SensorInstrumentationPoint sip; @Mock SensorInstrumentationPoint sip2; @Mock MethodInstrumentationConfig config; @Mock MethodInstrumentationConfig config2; @Mock SpecialInstrumentationPoint specIP; @Mock SpecialInstrumentationPoint specIP2; ClassInstrumenter classInstrumenter; LoaderAwareClassWriter classWriter; @BeforeMethod public void init() { dispatcher = hookDispatcher; a = agent; } public class Instrument extends ClassInstrumenterTest { // no instrumentation @Test public void noInstrumenatation() throws Exception { String methodName = "stringNullParameter"; long methodId = 3L; when(sip.getId()).thenReturn(methodId); when(config.getTargetMethodName()).thenReturn("nonExistingMethod"); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(false)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); // call this method via reflection as we would get a class cast // exception by casting to the concrete class. this.callMethod(testClass, methodName, null); verifyZeroInteractions(hookDispatcher); } // return, params, static @Test public void methodHookNoStatic() throws Exception { String methodName = "stringNullParameter"; long methodId = 3L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); // call this method via reflection as we would get a class cast // exception by casting to the concrete class. this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], "stringNullParameter"); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], "stringNullParameter"); verifyNoMoreInteractions(hookDispatcher); } @Test public void methodHookStatic() throws Exception { String methodName = "voidNullParameterStatic"; long methodId = 7L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); // call this method via reflection as we would get a class cast // exception by casting to the concrete class. this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, null, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, null, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, null, new Object[0], null); verifyNoMoreInteractions(hookDispatcher); } @Test public void stringNullParameter() throws Exception { String methodName = "stringNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], "stringNullParameter"); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], "stringNullParameter"); verifyNoMoreInteractions(hookDispatcher); } @Test public void intNullParameter() throws Exception { String methodName = "intNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], 3); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], 3); verifyNoMoreInteractions(hookDispatcher); } @Test public void doubleNullParameter() throws Exception { String methodName = "doubleNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], 5.3D); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], 5.3D); verifyNoMoreInteractions(hookDispatcher); } @Test public void floatNullParameter() throws Exception { String methodName = "floatNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], Float.MAX_VALUE); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], Float.MAX_VALUE); verifyNoMoreInteractions(hookDispatcher); } @Test public void byteNullParameter() throws Exception { String methodName = "byteNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], (byte) 127); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], (byte) 127); verifyNoMoreInteractions(hookDispatcher); } @Test public void shortNullParameter() throws Exception { String methodName = "shortNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], (short) 16345); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], (short) 16345); verifyNoMoreInteractions(hookDispatcher); } @Test public void booleanNullParameter() throws Exception { String methodName = "booleanNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], false); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], false); verifyNoMoreInteractions(hookDispatcher); } @Test public void charNullParameter() throws Exception { String methodName = "charNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], '\u1234'); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], '\u1234'); verifyNoMoreInteractions(hookDispatcher); } @Test public void voidNullParameterStatic() throws Exception { String methodName = "voidNullParameterStatic"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, null, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, null, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, null, new Object[0], null); verifyNoMoreInteractions(hookDispatcher); } @Test public void stringNullParameterStatic() throws Exception { String methodName = "stringNullParameterStatic"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, null, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, null, new Object[0], "stringNullParameterStatic"); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, null, new Object[0], "stringNullParameterStatic"); verifyNoMoreInteractions(hookDispatcher); } @Test public void voidOneParameter() throws Exception { String methodName = "voidOneParameter"; Object[] parameters = { "java.lang.String" }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName, String.class); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, parameters); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, parameters); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, parameters, null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, parameters, null); verifyNoMoreInteractions(hookDispatcher); } @Test public void stringOneParameter() throws Exception { String methodName = "stringOneParameter"; Object[] parameters = { "java.lang.String" }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName, String.class); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, parameters); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, parameters); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, parameters, "stringOneParameter"); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, parameters, "stringOneParameter"); verifyNoMoreInteractions(hookDispatcher); } @Test public void voidTwoParameters() throws Exception { String methodName = "voidTwoParameters"; Object[] parameters = { "java.lang.String", "java.lang.Object" }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName, String.class, Object.class); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, parameters); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, parameters); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, parameters, null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, parameters, null); verifyNoMoreInteractions(hookDispatcher); } @Test public void mixedTwoParameters() throws Exception { String methodName = "mixedTwoParameters"; Object[] parameters = { "int", "boolean" }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName, int.class, boolean.class); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, parameters); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, parameters); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, parameters, null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, parameters, null); verifyNoMoreInteractions(hookDispatcher); } @Test public void intArrayNullParameter() throws Exception { String methodName = "intArrayNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], new int[] { 1, 2, 3 }); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], new int[] { 1, 2, 3 }); verifyNoMoreInteractions(hookDispatcher); } @Test public void stringArrayNullParameter() throws Exception { String methodName = "stringArrayNullParameter"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], new String[] { "test123", "bla" }); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], new String[] { "test123", "bla" }); verifyNoMoreInteractions(hookDispatcher); } // exception no enhanced @Test public void unexpectedExceptionTrowingNoEnhanced() throws Exception { String methodName = "unexpectedExceptionThrowing"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); try { this.callMethod(testClass, methodName, null); } catch (Throwable t) { } verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], null); verifyNoMoreInteractions(hookDispatcher); } @Test public void unexpectedExceptionNotTrowingNoEnhanced() throws Exception { String methodName = "unexpectedExceptionNotThrowing"; Object[] parameters = { "java.lang.Object" }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName, Object.class); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); try { this.callMethod(testClass, methodName, parameters); } catch (Throwable t) { } verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, parameters); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, parameters, null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, parameters, null); verifyNoMoreInteractions(hookDispatcher); } @Test public void exceptionHandledResultReturned() throws Exception { String methodName = "exceptionHandledResultReturned"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], 3); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], 3); verifyNoMoreInteractions(hookDispatcher); } // constructors @Test public void staticConstructor() throws Exception { long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockConstructor(config, InstrumentationTestClass.class, true); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // create instance this.createInstance(TEST_CLASS_FQN, b); verify(hookDispatcher).dispatchConstructorBeforeBody(methodId, new Object[0]); verify(hookDispatcher).dispatchConstructorAfterBody(methodId, null, new Object[0]); verifyNoMoreInteractions(hookDispatcher); } @Test public void constructorNullParameter() throws Exception { long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockConstructor(config, InstrumentationTestClass.class, false); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // create instance Object instance = this.createInstance(TEST_CLASS_FQN, b); verify(hookDispatcher).dispatchConstructorBeforeBody(methodId, new Object[0]); verify(hookDispatcher).dispatchConstructorAfterBody(methodId, instance, new Object[0]); verifyNoMoreInteractions(hookDispatcher); } @Test public void constructorStringOneParameter() throws Exception { Object[] parameters = { "java.lang.String" }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockConstructor(config, InstrumentationTestClass.class, false, String.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); Class<?> clazz = createClass(TEST_CLASS_FQN, b); Constructor<?> constructor = clazz.getConstructor(new Class[] { String.class }); Object instance = constructor.newInstance(parameters); verify(hookDispatcher).dispatchConstructorBeforeBody(methodId, parameters); verify(hookDispatcher).dispatchConstructorAfterBody(methodId, instance, parameters); verifyNoMoreInteractions(hookDispatcher); } @Test public void nestedConstructorBooleanOneParameter() throws Exception { Object[] parameters = { Boolean.TRUE }; Object[] nestedParameters = { "delegate" }; long methodId = 9L; long nestedMethodId = 13L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockConstructor(config, InstrumentationTestClass.class, false, boolean.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); when(sip2.getId()).thenReturn(nestedMethodId); prepareConfigurationMockConstructor(config2, InstrumentationTestClass.class, false, String.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip2), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config2.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip2)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config, config2); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); Class<?> clazz = createClass(TEST_CLASS_FQN, b); Constructor<?> constructor = clazz.getConstructor(new Class[] { Boolean.TYPE }); Object testClass = constructor.newInstance(parameters); verify(hookDispatcher).dispatchConstructorBeforeBody(nestedMethodId, nestedParameters); verify(hookDispatcher).dispatchConstructorAfterBody(nestedMethodId, testClass, nestedParameters); verify(hookDispatcher).dispatchConstructorBeforeBody(methodId, parameters); verify(hookDispatcher).dispatchConstructorAfterBody(methodId, testClass, parameters); verifyNoMoreInteractions(hookDispatcher); } // constructor exception no enhanced @Test public void constructorUnexpectedExceptionTrowingNoEnhanced() throws Exception { Object[] parameters = { 3 }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockConstructor(config, InstrumentationTestClass.class, false, int.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); Class<?> clazz = createClass(TEST_CLASS_FQN, b); Constructor<?> constructor = clazz.getConstructor(new Class[] { int.class }); try { constructor.newInstance(parameters); } catch (Throwable t) { } verify(hookDispatcher).dispatchConstructorBeforeBody(methodId, parameters); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchConstructorAfterBody(eq(methodId), captor.capture(), eq(parameters)); assertThat(captor.getValue().getClass().getName(), is(TEST_CLASS_FQN)); verifyNoMoreInteractions(hookDispatcher); } @Test public void constructorUnexpectedExceptionNotTrowingNoEnhanced() throws Exception { Object[] parameters = { "test" }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockConstructor(config, InstrumentationTestClass.class, false, Object.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); Class<?> clazz = createClass(TEST_CLASS_FQN, b); Constructor<?> constructor = clazz.getConstructor(new Class[] { Object.class }); try { constructor.newInstance(parameters); } catch (Throwable t) { } verify(hookDispatcher).dispatchConstructorBeforeBody(methodId, parameters); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchConstructorAfterBody(eq(methodId), captor.capture(), eq(parameters)); assertThat(captor.getValue().getClass().getName(), is(TEST_CLASS_FQN)); verifyNoMoreInteractions(hookDispatcher); } @Test public void constructorExceptionHandledResultReturned() throws Exception { Object[] parameters = { 11L }; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockConstructor(config, InstrumentationTestClass.class, false, long.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(TEST_CLASS_FQN); prepareWriter(cr, null, false, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); Class<?> clazz = createClass(TEST_CLASS_FQN, b); Constructor<?> constructor = clazz.getConstructor(new Class[] { long.class }); Object instance = constructor.newInstance(parameters); verify(hookDispatcher).dispatchConstructorBeforeBody(methodId, parameters); verify(hookDispatcher).dispatchConstructorAfterBody(methodId, instance, parameters); verifyNoMoreInteractions(hookDispatcher); } // exception enhanced @Test public void exceptionThrowerIsInstrumented() throws Exception { String methodName = "throwsAndHandlesException"; long methodId = 9L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationExceptionTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(EXCEPTION_TEST_CLASS_FQN); prepareWriter(cr, null, true, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(EXCEPTION_TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], null); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchBeforeCatch(eq(methodId), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verifyNoMoreInteractions(hookDispatcher); } @Test public void exceptionThrowerIsInstrumentedWhenConstructor() throws Exception { Object[] params = { "test" }; long constructorId = 11L; when(sip.getId()).thenReturn(constructorId); prepareConfigurationMockConstructor(config, InstrumentationExceptionTestClass.class, false, String.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); ClassReader cr = new ClassReader(EXCEPTION_TEST_CLASS_FQN); prepareWriter(cr, null, true, config); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); Class<?> clazz = createClass(EXCEPTION_TEST_CLASS_FQN, b); Constructor<?> constructor = clazz.getConstructor(new Class[] { String.class }); Object instance = constructor.newInstance(params); verify(hookDispatcher).dispatchConstructorBeforeBody(constructorId, params); verify(hookDispatcher).dispatchConstructorAfterBody(constructorId, instance, params); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchConstructorBeforeCatch(eq(constructorId), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verifyNoMoreInteractions(hookDispatcher); } @Test public void exceptionThrowerAndHandlerAreInstrumented() throws Exception { String methodName = "callsMethodWithException"; String innerMethodName = "throwsAnException"; long methodId = 9L; long innerMethodId = 11L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationExceptionTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); when(sip2.getId()).thenReturn(innerMethodId); prepareConfigurationMockMethod(config2, InstrumentationExceptionTestClass.class, innerMethodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip2), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config2.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip2)); ClassReader cr = new ClassReader(EXCEPTION_TEST_CLASS_FQN); prepareWriter(cr, null, true, config, config2); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(EXCEPTION_TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); // first method verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], null); // inner method verify(hookDispatcher).dispatchMethodBeforeBody(innerMethodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(innerMethodId, testClass, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(innerMethodId, testClass, new Object[0], null); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchOnThrowInBody(eq(innerMethodId), eq(testClass), (Object[]) anyObject(), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verify(hookDispatcher).dispatchBeforeCatch(eq(methodId), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verifyNoMoreInteractions(hookDispatcher); } @Test public void exceptionThrowerAndHandlerAreInstrumentedStatic() throws Exception { String methodName = "callsStaticMethodWithException"; String innerMethodName = "staticThrowsAnException"; long methodId = 9L; long innerMethodId = 11L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationExceptionTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); when(sip2.getId()).thenReturn(innerMethodId); prepareConfigurationMockMethod(config2, InstrumentationExceptionTestClass.class, innerMethodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip2), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config2.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip2)); ClassReader cr = new ClassReader(EXCEPTION_TEST_CLASS_FQN); prepareWriter(cr, null, true, config, config2); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(EXCEPTION_TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); // first method verify(hookDispatcher).dispatchMethodBeforeBody(methodId, null, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, null, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, null, new Object[0], null); // inner method verify(hookDispatcher).dispatchMethodBeforeBody(innerMethodId, null, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(innerMethodId, null, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(innerMethodId, null, new Object[0], null); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchOnThrowInBody(eq(innerMethodId), eq(null), (Object[]) anyObject(), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verify(hookDispatcher).dispatchBeforeCatch(eq(methodId), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verifyNoMoreInteractions(hookDispatcher); } @Test public void exceptionMethodThrowingConstructorPassing() throws Exception { Object[] params = new Object[] { 3 }; String innerMethodName = "throwsAnException"; long innerMethodId = 9L; long constructorId = 11L; when(sip.getId()).thenReturn(constructorId); prepareConfigurationMockConstructor(config, InstrumentationExceptionTestClass.class, false, int.class); doAnswer(CONSTRUCTOR_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); when(sip2.getId()).thenReturn(innerMethodId); prepareConfigurationMockMethod(config2, InstrumentationExceptionTestClass.class, innerMethodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip2), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config2.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip2)); ClassReader cr = new ClassReader(EXCEPTION_TEST_CLASS_FQN); prepareWriter(cr, null, true, config, config2); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Class<?> clazz = createClass(EXCEPTION_TEST_CLASS_FQN, b); Constructor<?> constructor = clazz.getConstructor(new Class[] { int.class }); try { constructor.newInstance(params); } catch (Throwable t) { } // first method verify(hookDispatcher).dispatchConstructorBeforeBody(constructorId, params); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchConstructorAfterBody(eq(constructorId), captor.capture(), eq(params)); assertThat(captor.getValue().getClass().getName(), is(EXCEPTION_TEST_CLASS_FQN)); // inner method verify(hookDispatcher).dispatchMethodBeforeBody(eq(innerMethodId), anyObject(), eq(new Object[0])); verify(hookDispatcher).dispatchFirstMethodAfterBody(eq(innerMethodId), anyObject(), eq(new Object[0]), eq(null)); verify(hookDispatcher).dispatchSecondMethodAfterBody(eq(innerMethodId), anyObject(), eq(new Object[0]), eq(null)); verify(hookDispatcher).dispatchOnThrowInBody(eq(innerMethodId), anyObject(), eq(new Object[0]), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verify(hookDispatcher).dispatchConstructorOnThrowInBody(eq(constructorId), anyObject(), eq(params), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verifyNoMoreInteractions(hookDispatcher); } @Test public void callsMethodWithExceptionAndTryCatchFinally() throws Exception { String methodName = "callsMethodWithExceptionAndTryCatchFinally"; String innerMethodName = "throwsAnException"; long methodId = 9L; long innerMethodId = 11L; when(sip.getId()).thenReturn(methodId); prepareConfigurationMockMethod(config, InstrumentationExceptionTestClass.class, methodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip)); when(sip2.getId()).thenReturn(innerMethodId); prepareConfigurationMockMethod(config2, InstrumentationExceptionTestClass.class, innerMethodName); doAnswer(METHOD_INSTRUMENTER_ANSWER).when(instrumenterFactory).getMethodVisitor(eq(sip2), Matchers.<MethodVisitor> any(), anyInt(), anyString(), anyString(), anyBoolean()); when(config2.getAllInstrumentationPoints()).thenReturn(Collections.<IMethodInstrumentationPoint> singleton(sip2)); ClassReader cr = new ClassReader(EXCEPTION_TEST_CLASS_FQN); prepareWriter(cr, null, true, config, config2); cr.accept(classInstrumenter, ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG); assertThat(classInstrumenter.isByteCodeAdded(), is(true)); byte b[] = classWriter.toByteArray(); // now call this method Object testClass = this.createInstance(EXCEPTION_TEST_CLASS_FQN, b); this.callMethod(testClass, methodName, null); verify(hookDispatcher).dispatchMethodBeforeBody(methodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(methodId, testClass, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(methodId, testClass, new Object[0], null); ArgumentCaptor<Object> captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchBeforeCatch(eq(methodId), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verify(hookDispatcher).dispatchMethodBeforeBody(innerMethodId, testClass, new Object[0]); verify(hookDispatcher).dispatchFirstMethodAfterBody(innerMethodId, testClass, new Object[0], null); verify(hookDispatcher).dispatchSecondMethodAfterBody(innerMethodId, testClass, new Object[0], null); captor = ArgumentCaptor.forClass(Object.class); verify(hookDispatcher).dispatchOnThrowInBody(eq(innerMethodId), eq(testClass), (Object[]) anyObject(), captor.capture()); assertThat(captor.getValue().getClass().getName(), is(equalTo(MyTestException.class.getName()))); verifyNoMoreInteractions(hookDispatcher); } } protected void prepareWriter(ClassReader cr, ClassLoader classLoader, boolean enhancedExceptionSensor, MethodInstrumentationConfig... configs) { classWriter = new LoaderAwareClassWriter(cr, ClassWriter.COMPUTE_FRAMES, classLoader); classInstrumenter = new ClassInstrumenter(instrumenterFactory, classWriter, Arrays.asList(configs), enhancedExceptionSensor); } protected static MethodInstrumenter getMethodInstrumenter(MethodVisitor superMethodVisitor, int access, String name, String desc, long id, boolean enhancedExceptionSensor) { return new MethodInstrumenter(superMethodVisitor, access, name, desc, id, enhancedExceptionSensor) { @Override protected void loadHookDispatcher() { mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(ClassInstrumenterTest.class), "dispatcher", Type.getDescriptor(IHookDispatcher.class)); } }; } protected static ConstructorInstrumenter getConstructorInstrumenter(MethodVisitor superMethodVisitor, int access, String name, String desc, long id, boolean enhancedExceptionSensor) { return new ConstructorInstrumenter(superMethodVisitor, access, name, desc, id, enhancedExceptionSensor) { @Override protected void loadHookDispatcher() { mv.visitFieldInsn(Opcodes.GETSTATIC, Type.getInternalName(ClassInstrumenterTest.class), "dispatcher", Type.getDescriptor(IHookDispatcher.class)); } }; } }