Allow Unit tests to skip expected instrumentation errors.
MODIFY
dd-java-agent/instrumentation/akka-http-10.0/src/lagomTest/groovy/LagomTest.groovy
dd-java-agent/instrumentation/akka-http-10.0/src/lagomTest/groovy/LagomTest.groovy
import akka.NotUsed
import akka.stream.javadsl.Source
import akka.stream.testkit.javadsl.TestSink
+import net.bytebuddy.utility.JavaModule
import org.junit.After
import static java.util.concurrent.TimeUnit.SECONDS
@@ -23,17 +24,19 @@ class LagomTest extends AgentTestRunner {
@Shared
private TestServer server
-
- @After
@Override
- void afterTest() {
+ protected boolean onInstrumentationError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
+ if (throwable.getMessage().contains('Cannot resolve type description for akka.stream.impl.VirtualProcessor$WrappedSubscription$$SubscriptionState')) {
// 'akka/stream/impl/VirtualProcessor$WrappedSubscription$PassThrough$.class' declares
// itself an implementation of 'VirtualProcessor$WrappedSubscription$$SubscriptionState',
// but this interface does not exist on the classpath.
// The closest thing on the classpath is 'VirtualProcessor$WrappedSubscription$SubscriptionState' (only one $).
// Looks like a compiler/packaging issue on akka's end. Or maybe this interface is dynamically generated.
- // Either way, we're going to error out.
+ return false
+ } else {
+ return super.onInstrumentationError(typeName, classLoader, module, loaded, throwable)
+ }
}
def setupSpec() {
@@ -130,5 +133,4 @@ class LagomTest extends AgentTestRunner {
}
}
}
-
}

MODIFY
dd-java-agent/testing/src/main/java/datadog/trace/agent/test/AgentTestRunner.java
dd-java-agent/testing/src/main/java/datadog/trace/agent/test/AgentTestRunner.java
@@ -15,6 +15,7 @@ import java.lang.instrument.Instrumentation;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicInteger;
import net.bytebuddy.agent.ByteBuddyAgent;
@@ -63,6 +64,7 @@ public abstract class AgentTestRunner extends Specification {
protected static final Set<String> TRANSFORMED_CLASSES = Sets.newConcurrentHashSet();
private static final AtomicInteger INSTRUMENTATION_ERROR_COUNT = new AtomicInteger();
+ private static final ErrorCountingListener ERROR_LISTENER = new ErrorCountingListener();
private static final Instrumentation instrumentation;
private static volatile ClassFileTransformer activeTransformer = null;
@@ -97,6 +99,25 @@ public abstract class AgentTestRunner extends Specification {
return TEST_WRITER;
}
+ /**
+ * Invoked when Bytebuddy encounters an instrumentation error. Fails the test by default.
+ *
+ * <p>Override to skip specific expected errors.
+ *
+ * @return true if the test should fail because of this error.
+ */
+ protected boolean onInstrumentationError(
+ final String typeName,
+ final ClassLoader classLoader,
+ final JavaModule module,
+ final boolean loaded,
+ final Throwable throwable) {
+ System.err.println(
+ "Unexpected instrumentation error when instrumenting " + typeName + " on " + classLoader);
+ throwable.printStackTrace();
+ return true;
+ }
+
@BeforeClass
public static synchronized void agentSetup() throws Exception {
if (null != activeTransformer) {
@@ -107,8 +128,7 @@ public abstract class AgentTestRunner extends Specification {
try {
Thread.currentThread().setContextClassLoader(AgentTestRunner.class.getClassLoader());
assert ServiceLoader.load(Instrumenter.class).iterator().hasNext();
- activeTransformer =
- AgentInstaller.installBytebuddyAgent(instrumentation, new ErrorCountingListener());
+ activeTransformer = AgentInstaller.installBytebuddyAgent(instrumentation, ERROR_LISTENER);
} finally {
Thread.currentThread().setContextClassLoader(contextLoader);
}
@@ -118,11 +138,13 @@ public abstract class AgentTestRunner extends Specification {
public void beforeTest() {
TEST_WRITER.start();
INSTRUMENTATION_ERROR_COUNT.set(0);
+ ERROR_LISTENER.activateTest(this);
assert getTestTracer().activeSpan() == null;
}
@After
public void afterTest() {
+ ERROR_LISTENER.deactivateTest(this);
assert INSTRUMENTATION_ERROR_COUNT.get() == 0;
}
@@ -135,6 +157,16 @@ public abstract class AgentTestRunner extends Specification {
}
public static class ErrorCountingListener implements AgentBuilder.Listener {
+ private static final List<AgentTestRunner> activeTests = new CopyOnWriteArrayList<>();
+
+ public void activateTest(AgentTestRunner testRunner) {
+ activeTests.add(testRunner);
+ }
+
+ public void deactivateTest(AgentTestRunner testRunner) {
+ activeTests.remove(testRunner);
+ }
+
@Override
public void onDiscovery(
final String typeName,
@@ -166,9 +198,12 @@ public abstract class AgentTestRunner extends Specification {
final JavaModule module,
final boolean loaded,
final Throwable throwable) {
- // System.err.println("failed to instrument " + typeName);
- // throwable.printStackTrace();
+ for (AgentTestRunner testRunner : activeTests) {
+ if (testRunner.onInstrumentationError(typeName, classLoader, module, loaded, throwable)) {
INSTRUMENTATION_ERROR_COUNT.incrementAndGet();
+ break;
+ }
+ }
}
@Override

Allow Unit tests to skip expected instrumentation errors.