package net.bytebuddy.agent.builder; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.matcher.ElementMatchers; import net.bytebuddy.test.utility.MockitoRule; import net.bytebuddy.test.utility.ObjectPropertyAssertion; import net.bytebuddy.utility.JavaModule; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.mockito.Mock; import java.io.PrintStream; import java.lang.instrument.Instrumentation; import java.util.Collections; import java.util.List; import static net.bytebuddy.matcher.ElementMatchers.none; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; public class AgentBuilderListenerTest { private static final String FOO = "foo"; private static final boolean LOADED = true; @Rule public TestRule mockitoRule = new MockitoRule(this); @Mock private AgentBuilder.Listener first, second; @Mock private TypeDescription typeDescription; @Mock private ClassLoader classLoader; @Mock private JavaModule module; @Mock private DynamicType dynamicType; @Mock private Throwable throwable; @Before public void setUp() throws Exception { when(typeDescription.getName()).thenReturn(FOO); } @Test public void testNoOp() throws Exception { AgentBuilder.Listener.NoOp.INSTANCE.onDiscovery(FOO, classLoader, module, LOADED); AgentBuilder.Listener.NoOp.INSTANCE.onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); verifyZeroInteractions(dynamicType); AgentBuilder.Listener.NoOp.INSTANCE.onError(FOO, classLoader, module, LOADED, throwable); verifyZeroInteractions(throwable); AgentBuilder.Listener.NoOp.INSTANCE.onIgnored(typeDescription, classLoader, module, LOADED); AgentBuilder.Listener.NoOp.INSTANCE.onComplete(FOO, classLoader, module, LOADED); } @Test public void testPseudoAdapter() throws Exception { AgentBuilder.Listener listener = new PseudoAdapter(); listener.onDiscovery(FOO, classLoader, module, LOADED); listener.onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); verifyZeroInteractions(dynamicType); listener.onError(FOO, classLoader, module, LOADED, throwable); verifyZeroInteractions(throwable); listener.onIgnored(typeDescription, classLoader, module, LOADED); listener.onComplete(FOO, classLoader, module, LOADED); } @Test public void testCompoundOnDiscovery() throws Exception { new AgentBuilder.Listener.Compound(first, second).onDiscovery(FOO, classLoader, module, LOADED); verify(first).onDiscovery(FOO, classLoader, module, LOADED); verifyNoMoreInteractions(first); verify(second).onDiscovery(FOO, classLoader, module, LOADED); verifyNoMoreInteractions(second); } @Test public void testCompoundOnTransformation() throws Exception { new AgentBuilder.Listener.Compound(first, second).onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); verify(first).onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); verifyNoMoreInteractions(first); verify(second).onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); verifyNoMoreInteractions(second); } @Test public void testCompoundOnError() throws Exception { new AgentBuilder.Listener.Compound(first, second).onError(FOO, classLoader, module, LOADED, throwable); verify(first).onError(FOO, classLoader, module, LOADED, throwable); verifyNoMoreInteractions(first); verify(second).onError(FOO, classLoader, module, LOADED, throwable); verifyNoMoreInteractions(second); } @Test public void testCompoundOnIgnored() throws Exception { new AgentBuilder.Listener.Compound(first, second).onIgnored(typeDescription, classLoader, module, LOADED); verify(first).onIgnored(typeDescription, classLoader, module, LOADED); verifyNoMoreInteractions(first); verify(second).onIgnored(typeDescription, classLoader, module, LOADED); verifyNoMoreInteractions(second); } @Test public void testCompoundOnComplete() throws Exception { new AgentBuilder.Listener.Compound(first, second).onComplete(FOO, classLoader, module, LOADED); verify(first).onComplete(FOO, classLoader, module, LOADED); verifyNoMoreInteractions(first); verify(second).onComplete(FOO, classLoader, module, LOADED); verifyNoMoreInteractions(second); } @Test public void testStreamWritingOnDiscovery() throws Exception { PrintStream printStream = mock(PrintStream.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.StreamWriting(printStream); listener.onDiscovery(FOO, classLoader, module, LOADED); verify(printStream).printf("[Byte Buddy] DISCOVERY %s [%s, %s, loaded=%b]%n", FOO, classLoader, module, LOADED); verifyNoMoreInteractions(printStream); } @Test public void testStreamWritingOnTransformation() throws Exception { PrintStream printStream = mock(PrintStream.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.StreamWriting(printStream); listener.onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); verify(printStream).printf("[Byte Buddy] TRANSFORM %s [%s, %s, loaded=%b]%n", FOO, classLoader, module, LOADED); verifyNoMoreInteractions(printStream); } @Test public void testStreamWritingOnError() throws Exception { PrintStream printStream = mock(PrintStream.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.StreamWriting(printStream); listener.onError(FOO, classLoader, module, LOADED, throwable); verify(printStream).printf("[Byte Buddy] ERROR %s [%s, %s, loaded=%b]%n", FOO, classLoader, module, LOADED); verifyNoMoreInteractions(printStream); verify(throwable).printStackTrace(printStream); verifyNoMoreInteractions(throwable); } @Test public void testStreamWritingOnComplete() throws Exception { PrintStream printStream = mock(PrintStream.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.StreamWriting(printStream); listener.onComplete(FOO, classLoader, module, LOADED); verify(printStream).printf("[Byte Buddy] COMPLETE %s [%s, %s, loaded=%b]%n", FOO, classLoader, module, LOADED); verifyNoMoreInteractions(printStream); } @Test public void testStreamWritingOnIgnore() throws Exception { PrintStream printStream = mock(PrintStream.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.StreamWriting(printStream); listener.onIgnored(typeDescription, classLoader, module, LOADED); verify(printStream).printf("[Byte Buddy] IGNORE %s [%s, %s, loaded=%b]%n", FOO, classLoader, module, LOADED); verifyNoMoreInteractions(printStream); } @Test public void testStreamWritingStandardOutput() throws Exception { assertThat(AgentBuilder.Listener.StreamWriting.toSystemOut(), is((AgentBuilder.Listener) new AgentBuilder.Listener.StreamWriting(System.out))); } @Test public void testStreamWritingStandardError() throws Exception { assertThat(AgentBuilder.Listener.StreamWriting.toSystemError(), is((AgentBuilder.Listener) new AgentBuilder.Listener.StreamWriting(System.err))); } @Test public void testFilteringDoesNotMatch() throws Exception { AgentBuilder.Listener delegate = mock(AgentBuilder.Listener.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.Filtering(none(), delegate); listener.onDiscovery(FOO, classLoader, module, LOADED); listener.onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); listener.onError(FOO, classLoader, module, LOADED, throwable); listener.onIgnored(typeDescription, classLoader, module, LOADED); listener.onComplete(FOO, classLoader, module, LOADED); verifyZeroInteractions(delegate); } @Test public void testFilteringMatch() throws Exception { AgentBuilder.Listener delegate = mock(AgentBuilder.Listener.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.Filtering(ElementMatchers.any(), delegate); listener.onDiscovery(FOO, classLoader, module, LOADED); listener.onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); listener.onError(FOO, classLoader, module, LOADED, throwable); listener.onIgnored(typeDescription, classLoader, module, LOADED); listener.onComplete(FOO, classLoader, module, LOADED); verify(delegate).onDiscovery(FOO, classLoader, module, LOADED); verify(delegate).onTransformation(typeDescription, classLoader, module, LOADED, dynamicType); verify(delegate).onError(FOO, classLoader, module, LOADED, throwable); verify(delegate).onIgnored(typeDescription, classLoader, module, LOADED); verify(delegate).onComplete(FOO, classLoader, module, LOADED); verifyNoMoreInteractions(delegate); } @Test public void testReadEdgeAddingListenerNotSupported() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, false, Collections.<JavaModule>emptySet()); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), JavaModule.UNSUPPORTED, LOADED, mock(DynamicType.class)); } @Test public void testReadEdgeAddingListenerUnnamed() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); JavaModule source = mock(JavaModule.class), target = mock(JavaModule.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, false, Collections.singleton(target)); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), source, LOADED, mock(DynamicType.class)); verify(source).isNamed(); verifyNoMoreInteractions(source); verifyZeroInteractions(target); } @Test public void testReadEdgeAddingListenerCanRead() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); JavaModule source = mock(JavaModule.class), target = mock(JavaModule.class); when(source.isNamed()).thenReturn(true); when(source.canRead(target)).thenReturn(true); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, false, Collections.singleton(target)); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), source, LOADED, mock(DynamicType.class)); verify(source).isNamed(); verify(source).canRead(target); verifyNoMoreInteractions(source); verifyZeroInteractions(target); } @Test public void testReadEdgeAddingListenerNamedCannotRead() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); JavaModule source = mock(JavaModule.class), target = mock(JavaModule.class); when(source.isNamed()).thenReturn(true); when(source.canRead(target)).thenReturn(false); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, false, Collections.singleton(target)); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), source, LOADED, mock(DynamicType.class)); verify(source).isNamed(); verify(source).canRead(target); verify(source).addReads(instrumentation, target); verifyNoMoreInteractions(source); verifyZeroInteractions(target); } @Test public void testReadEdgeAddingListenerDuplexNotSupported() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, true, Collections.<JavaModule>emptySet()); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), JavaModule.UNSUPPORTED, LOADED, mock(DynamicType.class)); } @Test public void testReadEdgeAddingListenerDuplexUnnamed() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); JavaModule source = mock(JavaModule.class), target = mock(JavaModule.class); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, true, Collections.singleton(target)); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), source, LOADED, mock(DynamicType.class)); verify(source).isNamed(); verifyNoMoreInteractions(source); verifyZeroInteractions(target); } @Test public void testReadEdgeAddingListenerDuplexCanRead() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); JavaModule source = mock(JavaModule.class), target = mock(JavaModule.class); when(source.isNamed()).thenReturn(true); when(source.canRead(target)).thenReturn(true); when(target.canRead(source)).thenReturn(true); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, true, Collections.singleton(target)); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), source, LOADED, mock(DynamicType.class)); verify(source).isNamed(); verify(source).canRead(target); verifyNoMoreInteractions(source); verify(target).canRead(source); verifyNoMoreInteractions(target); } @Test public void testReadEdgeAddingListenerNamedDuplexCannotRead() throws Exception { Instrumentation instrumentation = mock(Instrumentation.class); JavaModule source = mock(JavaModule.class), target = mock(JavaModule.class); when(source.isNamed()).thenReturn(true); when(source.canRead(target)).thenReturn(false); when(target.canRead(source)).thenReturn(false); AgentBuilder.Listener listener = new AgentBuilder.Listener.ModuleReadEdgeCompleting(instrumentation, true, Collections.singleton(target)); listener.onTransformation(mock(TypeDescription.class), mock(ClassLoader.class), source, LOADED, mock(DynamicType.class)); verify(source).isNamed(); verify(source).canRead(target); verify(source).addReads(instrumentation, target); verifyNoMoreInteractions(source); verify(target).canRead(source); verify(target).addReads(instrumentation, source); verifyNoMoreInteractions(target); } @Test public void testObjectProperties() throws Exception { ObjectPropertyAssertion.of(AgentBuilder.Listener.NoOp.class).apply(); ObjectPropertyAssertion.of(AgentBuilder.Listener.StreamWriting.class).apply(); ObjectPropertyAssertion.of(AgentBuilder.Listener.Filtering.class).apply(); ObjectPropertyAssertion.of(AgentBuilder.Listener.Compound.class).create(new ObjectPropertyAssertion.Creator<List<?>>() { @Override public List<?> create() { return Collections.singletonList(mock(AgentBuilder.Listener.class)); } }).apply(); ObjectPropertyAssertion.of(AgentBuilder.Listener.ModuleReadEdgeCompleting.class).apply(); } private static class PseudoAdapter extends AgentBuilder.Listener.Adapter { /* empty */ } }