All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.undefinedlabs.scope.rules.TestNGScopeAgentRule Maven / Gradle / Ivy

Go to download

Scope is a APM for tests to give engineering teams unprecedented visibility into their CI process to quickly identify, troubleshoot and fix failed builds. This artifact contains the classes to instrument the TestNG tests.

There is a newer version: 0.15.1-beta.2
Show newest version
package com.undefinedlabs.scope.rules;

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.rules.model.TestNGScopeDescription;
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.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.testng.ITestResult;

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 TestNGScopeAgentRule extends AbstractScopeAgentRule {

    public static final Map TEST_WAS_STARTED = new HashMap<>();
    public static final Map TEST_WAS_SUCCEED = new HashMap<>();
    public static final Map TEST_WAS_FAILED = new HashMap<>();
    public static final Map TEST_WAS_SKIPPED = new HashMap<>();
    public static final Map SCOPE_SPAN_CONTAINER_BY_TEST_METHOD_MAP = new ConcurrentHashMap<>();

    @Override
    protected Iterable transformers() {
        final Class instrumentedClass = lookUp("org.testng.ITestListener");
        return (instrumentedClass != null) ? Collections.singleton(newAgentBuilder()
                .type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(TestNGStartedTestAdvice.class, named("onTestStart")))
                .type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(TestNGSucceedTestAdvice.class, named("onTestSuccess")))
                .type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(TestNGFailedTestAdvice.class, named("onTestFailure")))
                .type(isSubTypeOf(instrumentedClass), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(TestNGSkippedTestAdvice.class, named("onTestSkipped"))))
                : emptyCollection();
    }

    public static class TestNGStartedTestAdvice {

        @Advice.OnMethodExit
        public static void exit(@Advice.Argument(0) ITestResult testResult) {
            synchronized (TEST_WAS_STARTED) {

                final TestNGScopeDescription testNGScopeDescription = new TestNGScopeDescription(testResult);
                if (Boolean.TRUE.equals(TEST_WAS_STARTED.get(testNGScopeDescription))) {
                    return;
                }

                final Tracer tracer = ScopeGlobalTracer.get();
                final SourceCodeFrame sourceCodeFrame = SourceCodeFrameFactory.INSTANCE.createFrame(testNGScopeDescription.getClassName(), testNGScopeDescription.getMethodName());

                final Span span = tracer.buildSpan(testNGScopeDescription.getMethodName() + (StringUtils.isNotEmpty(testNGScopeDescription.getHashedParameterValues()) ? " (" + testNGScopeDescription.getHashedParameterValues() + ")" : ""))
                        .ignoreActiveSpan()
                        .withTag(TagKeys.COMPONENT, TagValues.Test.Framework.TEST_NG)
                        .withTag(TagKeys.SPAN_KIND, TagValues.SPAN_KIND_TEST)
                        .withTag(TagKeys.Test.TEST_NAME, StringUtils.isNotEmpty(testNGScopeDescription.getMethodName()) ? testNGScopeDescription.getMethodName() + (StringUtils.isNotEmpty(testNGScopeDescription.getHashedParameterValues()) ? " (" + testNGScopeDescription.getHashedParameterValues() + ")" : "") : "")
                        .withTag(TagKeys.Test.TEST_SUITE, testNGScopeDescription.getClassName())
                        .withTag(TagKeys.Test.TEST_FRAMEWORK, TagValues.Test.Framework.TEST_NG)
                        .withTag(TagKeys.Test.TEST_LANGUAGE, TagValues.Test.Language.JAVA)
                        .withTag(StringUtils.isNotEmpty(sourceCodeFrame.getLinkPathWithMethodBoundaries()) ? TagKeys.Test.TEST_CODE : TagKeys.SOURCE,
                                StringUtils.isNotEmpty(sourceCodeFrame.getLinkPathWithMethodBoundaries()) ? sourceCodeFrame.getLinkPathWithMethodBoundaries() : sourceCodeFrame.getLinkPathWithMethodLine())
                        .start();

                SpanUtils.INSTANCE.setTagObject(span, TagKeys.Test.TEST_ARGUMENTS, !testNGScopeDescription.getParameters().isEmpty() ? testNGScopeDescription.getParameters() : null);
                final Long startTimestamp = SpanUtils.INSTANCE.getStartTimestampMicros(span);

                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);
                final ScopeSpanContainer spanContainer = new ScopeSpanContainer(span, tracer.activateSpan(span));
                spanContainer.setPropagationTimestamp(startTimestamp);

                SCOPE_SPAN_CONTAINER_BY_TEST_METHOD_MAP.put(testNGScopeDescription, spanContainer);
                TEST_WAS_STARTED.put(testNGScopeDescription, Boolean.TRUE);
            }

        }
    }

    public static class TestNGSucceedTestAdvice {
        @Advice.OnMethodExit
        public static void exit(@Advice.Argument(0) ITestResult testResult) {
            synchronized (TEST_WAS_SUCCEED) {
                final TestNGScopeDescription testNGScopeDescription = new TestNGScopeDescription(testResult);
                if (Boolean.TRUE.equals(TEST_WAS_SUCCEED.get(testNGScopeDescription))) {
                    return;
                }

                final ScopeSpanContainer scopeSpanContainer = SCOPE_SPAN_CONTAINER_BY_TEST_METHOD_MAP.get(testNGScopeDescription);
                if (scopeSpanContainer == null) {
                    return;
                }

                final Span span = scopeSpanContainer.getSpan();

                span.setTag(TagKeys.ERROR, false);
                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_METHOD_MAP.remove(testNGScopeDescription);
                TEST_WAS_SUCCEED.put(testNGScopeDescription, Boolean.TRUE);
            }
        }
    }

    public static class TestNGFailedTestAdvice {
        @Advice.OnMethodExit
        public static void exit(@Advice.Argument(0) ITestResult testResult) {
            synchronized (TEST_WAS_FAILED) {
                final TestNGScopeDescription testNGScopeDescription = new TestNGScopeDescription(testResult);
                if (Boolean.TRUE.equals(TEST_WAS_FAILED.get(testNGScopeDescription))) {
                    return;
                }

                final ScopeSpanContainer scopeSpanContainer = SCOPE_SPAN_CONTAINER_BY_TEST_METHOD_MAP.get(testNGScopeDescription);
                if (scopeSpanContainer == null) {
                    return;
                }

                final Span span = scopeSpanContainer.getSpan();
                final Throwable throwable = testResult.getThrowable();

                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());

                span.setTag(TagKeys.ERROR, true);
                span.setTag(TagKeys.Test.TEST_STATUS, TagValues.Test.TEST_FAIL);
                span.log(EventFieldsFactory.INSTANCE.createFields(throwableEventBuilder.build()));

                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_METHOD_MAP.remove(testNGScopeDescription);
                TEST_WAS_FAILED.put(testNGScopeDescription, Boolean.TRUE);
            }
        }
    }

    public static class TestNGSkippedTestAdvice {

        @Advice.OnMethodExit
        public static void exit(@Advice.Argument(0) ITestResult testResult) {
            synchronized (TEST_WAS_SKIPPED) {
                final TestNGScopeDescription testNGScopeDescription = new TestNGScopeDescription(testResult);
                if (Boolean.TRUE.equals(TEST_WAS_SKIPPED.get(testNGScopeDescription))) {
                    return;
                }

                final ScopeSpanContainer scopeSpanContainer = SCOPE_SPAN_CONTAINER_BY_TEST_METHOD_MAP.get(testNGScopeDescription);
                if (scopeSpanContainer == null) {
                    return;
                }

                final Span span = scopeSpanContainer.getSpan();
                final Long timestampMicros = scopeSpanContainer.getPropagationTimestamp();

                span.setTag(TagKeys.ERROR, false);
                span.setTag(TagKeys.Test.TEST_STATUS, TagValues.Test.TEST_SKIP);

                if (timestampMicros != null) {
                    span.finish(timestampMicros);
                } else {
                    span.finish();
                }

                Statistics.INSTANCE.registerFinishedTestSpan(span);
                scopeSpanContainer.getScope().close();
                SCOPE_SPAN_CONTAINER_BY_TEST_METHOD_MAP.remove(testNGScopeDescription);
                TEST_WAS_SKIPPED.put(testNGScopeDescription, Boolean.TRUE);
            }
        }

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy