com.undefinedlabs.scope.rules.JUnit5ScopeAgentRule Maven / Gradle / Ivy
package com.undefinedlabs.scope.rules;
import com.undefinedlabs.scope.rules.frameworks.model.junit5.*;
import com.undefinedlabs.scope.ScopeGlobalTracer;
import com.undefinedlabs.scope.ScopeSpanContainer;
import com.undefinedlabs.scope.coverage.*;
import com.undefinedlabs.scope.coverage.extractor.model.ScopeCoverageData;
import com.undefinedlabs.scope.events.EventFieldsFactory;
import com.undefinedlabs.scope.events.exception.ThrowableEvent;
import com.undefinedlabs.scope.jdk.OptionalMapper;
import com.undefinedlabs.scope.rules.transformer.ScopeAgentAdvicedTransformer;
import com.undefinedlabs.scope.settings.ScopeSettings;
import com.undefinedlabs.scope.settings.ScopeSettingsResolver;
import com.undefinedlabs.scope.statistics.Statistics;
import com.undefinedlabs.scope.utils.SpanUtils;
import com.undefinedlabs.scope.utils.baggage.BaggageKeys;
import com.undefinedlabs.scope.utils.baggage.BaggageValues;
import com.undefinedlabs.scope.utils.event.EventValues;
import com.undefinedlabs.scope.utils.props.SystemProps;
import com.undefinedlabs.scope.utils.sourcecode.ExceptionSourceCodeFactory;
import com.undefinedlabs.scope.utils.sourcecode.ExceptionSourceCodeFrame;
import com.undefinedlabs.scope.utils.sourcecode.SourceCodeFrame;
import com.undefinedlabs.scope.utils.sourcecode.SourceCodeFrameFactory;
import com.undefinedlabs.scope.utils.tag.TagKeys;
import com.undefinedlabs.scope.utils.tag.TagValues;
import io.opentracing.Span;
import io.opentracing.Tracer;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import com.undefinedlabs.scope.deps.org.apache.commons.lang3.StringUtils;
import org.junit.platform.engine.TestExecutionResult;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestIdentifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static net.bytebuddy.matcher.ElementMatchers.*;
public class JUnit5ScopeAgentRule extends AbstractScopeAgentRule {
public static final Map TEST_IDENTIFIER_WAS_STARTED = new HashMap<>();
public static final Map TEST_IDENTIFIER_WAS_FINISHED = new HashMap<>();
public static final Map TEST_IDENTIFIER_WAS_SKIPPED = new HashMap<>();
public static final Map SCOPE_SPAN_CONTAINER_BY_TEST_IDENTIFIER_MAP = new ConcurrentHashMap<>();
@Override
protected Iterable extends AgentBuilder> transformers() {
final Class instrumentedClass = lookUp("org.junit.platform.launcher.TestExecutionListener");
return (instrumentedClass != null) ?
Collections.singleton(newAgentBuilder()
.type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(JUnit5StartedTestAdvice.class, named("executionStarted")))
.type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(JUnit5FinishedTestAdvice.class, named("executionFinished")))
.type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(JUnit5DisabledTestAdvice.class, named("executionSkipped"))))
: emptyCollection();
}
public static class JUnit5StartedTestAdvice {
@Advice.OnMethodExit
public static void exit(@Advice.This TestExecutionListener thiz, @Advice.Argument(0) TestIdentifier testIdentifier) {
if (testIdentifier.isContainer()) {
return;
}
synchronized (TEST_IDENTIFIER_WAS_STARTED) {
if (Boolean.TRUE.equals(TEST_IDENTIFIER_WAS_STARTED.get(testIdentifier))) {
return;
}
final Tracer tracer = ScopeGlobalTracer.get();
final JUnit5ScopeUniqueId jUnit5ScopeUniqueId = new JUnit5ScopeUniqueId(testIdentifier.getUniqueId());
final SourceCodeFrame sourceCodeFrame = SourceCodeFrameFactory.INSTANCE.createFrame(jUnit5ScopeUniqueId.getClassName(), jUnit5ScopeUniqueId.getMethodName());
if (StringUtils.isEmpty(jUnit5ScopeUniqueId.getClassName()) || StringUtils.isEmpty(jUnit5ScopeUniqueId.getMethodName())) {
return;
}
final String testName = jUnit5ScopeUniqueId.getMethodName() + (StringUtils.isNotEmpty(jUnit5ScopeUniqueId.getInvocation()) ? "[" + jUnit5ScopeUniqueId.getInvocation() + "]" : "");
final Span span = tracer.buildSpan(testName)
.ignoreActiveSpan()
.withTag(TagKeys.COMPONENT, TagValues.Test.Framework.JUNIT_5)
.withTag(TagKeys.SPAN_KIND, TagValues.SPAN_KIND_TEST)
.withTag(TagKeys.Test.TEST_NAME, StringUtils.isNotEmpty(testName) ? testName : "")
.withTag(TagKeys.Test.TEST_SUITE, StringUtils.isNotEmpty(jUnit5ScopeUniqueId.getClassName()) ? jUnit5ScopeUniqueId.getClassName() : "")
.withTag(TagKeys.Test.TEST_FRAMEWORK, TagValues.Test.Framework.JUNIT_5)
.withTag(TagKeys.Test.TEST_LANGUAGE, TagValues.Test.Language.JAVA)
.start();
if(StringUtils.isNotEmpty(sourceCodeFrame.getLinkPathWithMethodBoundaries())) {
span.setTag(TagKeys.Test.TEST_CODE, sourceCodeFrame.getLinkPathWithMethodBoundaries());
} else if(StringUtils.isNotEmpty(sourceCodeFrame.getLinkPathWithMethodLine())) {
span.setTag(TagKeys.SOURCE, sourceCodeFrame.getLinkPathWithMethodLine());
}
if ((boolean) ScopeSettingsResolver.INSTANCE.get().getSetting(ScopeSettings.SCOPE_CODE_PATH_ENABLED)) {
GlobalCoverageReporter.get().startSession();
span.setBaggageItem(BaggageKeys.COVERAGE_ENABLED, String.valueOf(true));
}
Statistics.INSTANCE.registerStartedTestSpan(span);
span.setBaggageItem(BaggageKeys.TRACE_KIND, BaggageValues.TRACE_KIND_TEST);
tracer.activateSpan(span);
SCOPE_SPAN_CONTAINER_BY_TEST_IDENTIFIER_MAP.put(testIdentifier, new ScopeSpanContainer(span, tracer.activateSpan(span)));
TEST_IDENTIFIER_WAS_STARTED.put(testIdentifier, Boolean.TRUE);
}
}
}
public static class JUnit5FinishedTestAdvice {
@Advice.OnMethodExit
public static void exit(@Advice.This TestExecutionListener thiz, @Advice.Argument(0) TestIdentifier testIdentifier, @Advice.Argument(1) TestExecutionResult testExecutionResult) {
if (testIdentifier.isContainer()) {
return;
}
synchronized (TEST_IDENTIFIER_WAS_FINISHED) {
if (Boolean.TRUE.equals(TEST_IDENTIFIER_WAS_FINISHED.get(testIdentifier))) {
return;
}
final ScopeSpanContainer scopeSpanContainer = SCOPE_SPAN_CONTAINER_BY_TEST_IDENTIFIER_MAP.get(testIdentifier);
if (scopeSpanContainer == null) {
return;
}
final Span span = scopeSpanContainer.getSpan();
span.setTag(TagKeys.ERROR, !TestExecutionResult.Status.SUCCESSFUL.equals(testExecutionResult.getStatus()));
final Throwable throwable = (Throwable) OptionalMapper.getByMethod(testExecutionResult, "getThrowable");
if (throwable != null) {
final ExceptionSourceCodeFrame exceptionSourceCodeFrame = ExceptionSourceCodeFactory.INSTANCE.createFrame(throwable);
final ThrowableEvent.Builder throwableEventBuilder = ThrowableEvent.newBuilder();
throwableEventBuilder
.withEventType((exceptionSourceCodeFrame.getUserThrowable() instanceof AssertionError) ? EventValues.Test.TEST_FAILURE : EventValues.Test.TEST_ERROR)
.withThrowable(exceptionSourceCodeFrame.getUserThrowable())
.withSource(exceptionSourceCodeFrame.getSourceCodeFrame().getLinkPathWithMethodLine())
.build();
span.setTag(TagKeys.Test.TEST_STATUS, TagValues.Test.TEST_FAIL);
span.log(EventFieldsFactory.INSTANCE.createFields(throwableEventBuilder.build()));
} else {
span.setTag(TagKeys.Test.TEST_STATUS, TagValues.Test.TEST_PASS);
}
if ((boolean) ScopeSettingsResolver.INSTANCE.get().getSetting(ScopeSettings.SCOPE_CODE_PATH_ENABLED)) {
final CoverageSession coverageSession = GlobalCoverageReporter.get().activeSession();
if (coverageSession != null) {
final CoverageSessionInfo coverageSessionInfo = coverageSession.close();
final CoverageSessionReport coverageReport = CoverageSessionAnalyzer.INSTANCE.analyze(coverageSessionInfo);
final ScopeCoverageData coverageData = ScopeCoverageExtractor.INSTANCE.analyze(coverageReport);
SpanUtils.INSTANCE.setTagObject(span, TagKeys.Test.TEST_COVERAGE, coverageData);
}
}
span.finish();
Statistics.INSTANCE.registerFinishedTestSpan(span);
scopeSpanContainer.getScope().close();
SCOPE_SPAN_CONTAINER_BY_TEST_IDENTIFIER_MAP.remove(testIdentifier);
TEST_IDENTIFIER_WAS_FINISHED.put(testIdentifier, Boolean.TRUE);
}
}
}
public static class JUnit5DisabledTestAdvice {
@Advice.OnMethodExit
public static void exit(@Advice.This TestExecutionListener thiz, @Advice.Argument(0) TestIdentifier testIdentifier, @Advice.Argument(1) String reason) {
if (testIdentifier.isContainer()) {
return;
}
synchronized (TEST_IDENTIFIER_WAS_SKIPPED) {
if (Boolean.TRUE.equals(TEST_IDENTIFIER_WAS_SKIPPED.get(testIdentifier))) {
return;
}
final Tracer tracer = ScopeGlobalTracer.get();
final JUnit5ScopeUniqueId jUnit5ScopeUniqueId = new JUnit5ScopeUniqueId(testIdentifier.getUniqueId());
final JUnit5ScopeTestDisplayName jUnit5ScopeTestDisplayName = new JUnit5ScopeTestDisplayName(testIdentifier.getDisplayName());
final SourceCodeFrame sourceCodeFrame = SourceCodeFrameFactory.INSTANCE.createFrame(jUnit5ScopeUniqueId.getClassName(), jUnit5ScopeUniqueId.getMethodName());
if (StringUtils.isEmpty(jUnit5ScopeUniqueId.getClassName()) || StringUtils.isEmpty(jUnit5ScopeTestDisplayName.get())) {
return;
}
final Span span = tracer.buildSpan(jUnit5ScopeTestDisplayName.get())
.ignoreActiveSpan()
.withTag(TagKeys.COMPONENT, TagValues.Test.Framework.JUNIT_5)
.withTag(TagKeys.SPAN_KIND, TagValues.SPAN_KIND_TEST)
.withTag(TagKeys.Test.TEST_NAME, StringUtils.isNotEmpty(jUnit5ScopeTestDisplayName.get()) ? jUnit5ScopeTestDisplayName.get() : "")
.withTag(TagKeys.Test.TEST_SUITE, StringUtils.isNotEmpty(jUnit5ScopeUniqueId.getClassName()) ? jUnit5ScopeUniqueId.getClassName() : "")
.withTag(TagKeys.Test.TEST_FRAMEWORK, TagValues.Test.Framework.JUNIT_5)
.withTag(TagKeys.Test.TEST_LANGUAGE, TagValues.Test.Language.JAVA)
.withTag(TagKeys.Test.TEST_STATUS, TagValues.Test.TEST_SKIP)
.withTag(TagKeys.ERROR, false)
.start();
if(StringUtils.isNotEmpty(sourceCodeFrame.getLinkPathWithMethodBoundaries())) {
span.setTag(TagKeys.Test.TEST_CODE, sourceCodeFrame.getLinkPathWithMethodBoundaries());
} else if(StringUtils.isNotEmpty(sourceCodeFrame.getLinkPathWithMethodLine())) {
span.setTag(TagKeys.SOURCE, sourceCodeFrame.getLinkPathWithMethodLine());
}
Statistics.INSTANCE.registerStartedTestSpan(span);
span.setBaggageItem(BaggageKeys.TRACE_KIND, BaggageValues.TRACE_KIND_TEST);
span.finish(SpanUtils.INSTANCE.getStartTimestampMicros(span));
Statistics.INSTANCE.registerFinishedTestSpan(span);
TEST_IDENTIFIER_WAS_SKIPPED.put(testIdentifier, Boolean.TRUE);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy