Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracing of setup and teardown actions in JUnit 4 #8030

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
apply from: "$rootDir/gradle/java.gradle"

muzzle {
pass {
group = 'junit'
module = 'junit'
versions = '[4.13,5)'
}
}

addTestSuiteForDir('latestDepTest', 'test')

dependencies {
implementation project(':dd-java-agent:instrumentation:junit-4.10')
compileOnly group: 'junit', name: 'junit', version: '4.13'

testImplementation testFixtures(project(':dd-java-agent:agent-ci-visibility'))

// version used below is not the minimum one that we support,
// but the tests need to use it in order to be compliant with Spock 2.x
testImplementation(group: 'junit', name: 'junit') {
version {
strictly '4.13.2'
}
}

latestDepTestImplementation group: 'junit', name: 'junit', version: '4.+'
}
228 changes: 228 additions & 0 deletions dd-java-agent/instrumentation/junit-4.10/junit-4.13/gradle.lockfile

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package datadog.trace.instrumentation.junit4;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.agent.tooling.InstrumenterModule;
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import net.bytebuddy.asm.Advice;
import org.junit.runner.manipulation.InvalidOrderingException;
import org.junit.runner.manipulation.Ordering;
import org.junit.runners.model.FrameworkMethod;

@AutoService(InstrumenterModule.class)
public class JUnit4BeforeAfterInstrumentation extends InstrumenterModule.CiVisibility
implements Instrumenter.ForKnownTypes {

public JUnit4BeforeAfterInstrumentation() {
super("ci-visibility", "junit-4", "setup-teardown");
}

@Override
public String[] knownMatchingTypes() {
return new String[] {
"org.junit.internal.runners.statements.RunBefores",
"org.junit.internal.runners.statements.RunAfters",
"org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters$RunBeforeParams",
"org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters$RunAfterParams",
};
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".JUnit4BeforeAfterOperationsTracer",
};
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
named("invokeMethod")
.and(takesArgument(0, named("org.junit.runners.model.FrameworkMethod"))),
JUnit4BeforeAfterInstrumentation.class.getName() + "$RunBeforesAftersAdvice");
}

public static class RunBeforesAftersAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentScope startCallSpan(@Advice.Argument(0) final FrameworkMethod method) {
return JUnit4BeforeAfterOperationsTracer.startTrace(method.getMethod());
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void finishCallSpan(
@Advice.Enter final AgentScope scope, @Advice.Thrown final Throwable throwable) {
JUnit4BeforeAfterOperationsTracer.endTrace(scope, throwable);
}

// JUnit 4.13 and above
public static void muzzleCheck(final Ordering ord) {
try {
ord.apply(null);
} catch (InvalidOrderingException e) {
throw new RuntimeException(e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package datadog.trace.instrumentation.junit4;

import datadog.trace.bootstrap.instrumentation.api.AgentScope;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import datadog.trace.bootstrap.instrumentation.api.Tags;
import java.lang.reflect.Method;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.runners.Parameterized;

public class JUnit4BeforeAfterOperationsTracer {
public static AgentScope startTrace(final Method method) {
final AgentSpan span = AgentTracer.startSpan("junit", method.getName());
if (method.isAnnotationPresent(Before.class)) {
span.setTag(Tags.TEST_CALLBACK, "Before");
} else if (method.isAnnotationPresent(After.class)) {
span.setTag(Tags.TEST_CALLBACK, "After");
} else if (method.isAnnotationPresent(BeforeClass.class)) {
span.setTag(Tags.TEST_CALLBACK, "BeforeClass");
} else if (method.isAnnotationPresent(AfterClass.class)) {
span.setTag(Tags.TEST_CALLBACK, "AfterClass");
} else if (method.isAnnotationPresent(Parameterized.BeforeParam.class)) {
span.setTag(Tags.TEST_CALLBACK, "BeforeParam");
} else if (method.isAnnotationPresent(Parameterized.AfterParam.class)) {
span.setTag(Tags.TEST_CALLBACK, "AfterParam");
}
return AgentTracer.activateSpan(span);
}

public static void endTrace(final AgentScope scope, final Throwable throwable) {
final AgentSpan span = scope.span();
if (throwable != null) {
span.addThrowable(throwable);
}
scope.close();
span.finish();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import datadog.trace.api.DisableTestTrace
import datadog.trace.civisibility.CiVisibilityInstrumentationTest
import datadog.trace.instrumentation.junit4.TestEventsHandlerHolder
import junit.runner.Version
import org.example.TestFailedAfter
import org.example.TestFailedAfterClass
import org.example.TestFailedAfterParam
import org.example.TestFailedBefore
import org.example.TestFailedBeforeClass
import org.example.TestFailedBeforeParam
import org.example.TestSucceedBeforeAfter
import org.example.TestSucceedBeforeClassAfterClass
import org.example.TestSucceedBeforeParamAfterParam
import org.junit.runner.JUnitCore

@DisableTestTrace(reason = "avoid self-tracing")
class JUnit413Test extends CiVisibilityInstrumentationTest {

def runner = new JUnitCore()

def "test #testcaseName"() {
runTests(tests)

assertSpansData(testcaseName, expectedTracesCount)

where:
testcaseName | tests | expectedTracesCount
"test-succeed-before-after" | [TestSucceedBeforeAfter] | 3
"test-succeed-before-class-after-class" | [TestSucceedBeforeClassAfterClass] | 3
"test-succeed-before-param-after-param" | [TestSucceedBeforeParamAfterParam] | 2
"test-failed-before-class" | [TestFailedBeforeClass] | 1
"test-failed-after-class" | [TestFailedAfterClass] | 3
"test-failed-before" | [TestFailedBefore] | 3
"test-failed-after" | [TestFailedAfter] | 3
"test-failed-before-param" | [TestFailedBeforeParam] | 2
"test-failed-after-param" | [TestFailedAfterParam] | 2
}

private void runTests(Collection<Class<?>> tests) {
TestEventsHandlerHolder.start()
try {
Class[] array = tests.toArray(new Class[0])
runner.run(array)
} catch (Throwable ignored) {
// Ignored
}
TestEventsHandlerHolder.stop()
}

@Override
String instrumentedLibraryName() {
return "junit4"
}

@Override
String instrumentedLibraryVersion() {
return Version.id()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example;

import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.Test;

public class TestFailedAfter {
@After
public void tearDown() {
throw new RuntimeException("testcase teardown failed");
}

@Test
public void test_succeed() {
assertTrue(true);
}

@Test
public void another_test_succeed() {
assertTrue(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example;

import static org.junit.Assert.assertTrue;

import org.junit.AfterClass;
import org.junit.Test;

public class TestFailedAfterClass {
@AfterClass
public static void tearDown() {
throw new RuntimeException("suite teardown failed");
}

@Test
public void test_succeed() {
assertTrue(true);
}

@Test
public void another_test_succeed() {
assertTrue(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.example;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class TestFailedAfterParam {
private final int num1;
private final int num2;
private final int sum;

public TestFailedAfterParam(final int num1, final int num2, final int sum) {
this.num1 = num1;
this.num2 = num2;
this.sum = sum;
}

@Parameterized.BeforeParam
public static void setup() {}

@Parameterized.AfterParam
public static void tearDown() {
throw new RuntimeException("after param setup failed");
}

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {{0, 0, 0}, {1, 1, 2}});
}

@Test
public void parameterized_test_succeed() {
assertEquals(num1 + num2, sum);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example;

import static org.junit.Assert.assertTrue;

import org.junit.Before;
import org.junit.Test;

public class TestFailedBefore {
@Before
public void setup() {
throw new RuntimeException("testcase setup failed");
}

@Test
public void test_succeed() {
assertTrue(true);
}

@Test
public void another_test_succeed() {
assertTrue(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example;

import static org.junit.Assert.assertTrue;

import org.junit.BeforeClass;
import org.junit.Test;

public class TestFailedBeforeClass {
@BeforeClass
public static void setup() {
throw new RuntimeException("suite setup failed");
}

@Test
public void test_succeed() {
assertTrue(true);
}

@Test
public void another_test_succeed() {
assertTrue(true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.example;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.Collection;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class TestFailedBeforeParam {
private final int num1;
private final int num2;
private final int sum;

public TestFailedBeforeParam(final int num1, final int num2, final int sum) {
this.num1 = num1;
this.num2 = num2;
this.sum = sum;
}

@Parameterized.BeforeParam
public static void setup() {
throw new RuntimeException("before param setup failed");
}

@Parameterized.AfterParam
public static void tearDown() {}

@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {{0, 0, 0}, {1, 1, 2}});
}

@Test
public void parameterized_test_succeed() {
assertEquals(num1 + num2, sum);
}
}
Loading
Loading