Added unit test for DefineClassInstrumentation (#12483)
Co-authored-by: Lauri Tulmin <ltulmin@splunk.com>
This commit is contained in:
parent
d3e1a2c504
commit
ff44af85ac
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.internal.classloader;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.ArgumentMatchers.same;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import io.opentelemetry.javaagent.bootstrap.DefineClassHelper;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.ProtectionDomain;
|
||||||
|
import org.apache.commons.compress.utils.IOUtils;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
class DefineClassInstrumentationTest {
|
||||||
|
|
||||||
|
public static class DefiningClassLoader extends ClassLoader {
|
||||||
|
|
||||||
|
public DefiningClassLoader() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suppressing warnings to force testing of deprecated method
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public Class<?> doDefineClass(byte[] b, int off, int len) {
|
||||||
|
return defineClass(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> doDefineClass(String name, byte[] b, int off, int len) {
|
||||||
|
return defineClass(name, b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> doDefineClass(
|
||||||
|
String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) {
|
||||||
|
return defineClass(name, b, off, len, protectionDomain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(ints = {3, 4, 5})
|
||||||
|
@SuppressWarnings("DirectInvocationOnMock")
|
||||||
|
void ensureDefineClassInstrumented(int argCount) throws IOException {
|
||||||
|
String className = Dummy.class.getName();
|
||||||
|
String resource = className.replace('.', '/') + ".class";
|
||||||
|
ByteArrayOutputStream byteCodeStream = new ByteArrayOutputStream();
|
||||||
|
try (InputStream classfile = getClass().getResourceAsStream("/" + resource)) {
|
||||||
|
IOUtils.copy(classfile, byteCodeStream, 1024);
|
||||||
|
}
|
||||||
|
byte[] bytecode = byteCodeStream.toByteArray();
|
||||||
|
|
||||||
|
DefiningClassLoader cl = new DefiningClassLoader();
|
||||||
|
|
||||||
|
DefineClassHelper.Handler mockHandler = Mockito.mock(DefineClassHelper.Handler.class);
|
||||||
|
// We need to initialize mockHandler by invoking it early, otherwise this leads to problems
|
||||||
|
mockHandler.beforeDefineClass(cl, className, bytecode, 0, bytecode.length);
|
||||||
|
Mockito.reset(mockHandler);
|
||||||
|
|
||||||
|
DefineClassHelper.Handler originalHandler =
|
||||||
|
DefineClassHelper.internalSetHandlerForTests(mockHandler);
|
||||||
|
String expectedClassName;
|
||||||
|
try {
|
||||||
|
switch (argCount) {
|
||||||
|
case 3:
|
||||||
|
expectedClassName = null;
|
||||||
|
cl.doDefineClass(bytecode, 0, bytecode.length);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
expectedClassName = className;
|
||||||
|
cl.doDefineClass(className, bytecode, 0, bytecode.length);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
expectedClassName = className;
|
||||||
|
cl.doDefineClass(className, bytecode, 0, bytecode.length, null);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
DefineClassHelper.internalSetHandlerForTests(originalHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(mockHandler)
|
||||||
|
.beforeDefineClass(
|
||||||
|
same(cl), eq(expectedClassName), eq(bytecode), eq(0), eq(bytecode.length));
|
||||||
|
verify(mockHandler).afterDefineClass(eq(null));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.instrumentation.internal.classloader;
|
||||||
|
|
||||||
|
class Dummy {}
|
|
@ -70,5 +70,18 @@ public class DefineClassHelper {
|
||||||
DefineClassHelper.handler = handler;
|
DefineClassHelper.handler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only for testing. In contrast to {@link #internalSetHandler(Handler)} allows replacing the
|
||||||
|
* handler if it already has been set.
|
||||||
|
*
|
||||||
|
* @param handler the handler to set
|
||||||
|
* @return the previously active handler
|
||||||
|
*/
|
||||||
|
public static Handler internalSetHandlerForTests(Handler handler) {
|
||||||
|
Handler oldHandler = DefineClassHelper.handler;
|
||||||
|
DefineClassHelper.handler = handler;
|
||||||
|
return oldHandler;
|
||||||
|
}
|
||||||
|
|
||||||
private DefineClassHelper() {}
|
private DefineClassHelper() {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue