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

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

package com.undefinedlabs.scope.rules;

import com.undefinedlabs.scope.ScopeGlobalTracer;
import com.undefinedlabs.scope.deps.org.apache.commons.lang3.reflect.MethodUtils;
import com.undefinedlabs.scope.rules.model.JUnit4ScopeFrameworkMethod;
import com.undefinedlabs.scope.rules.transformer.ScopeAgentAdvicedTransformer;
import com.undefinedlabs.scope.runner.ScopeGlobalRunner;
import com.undefinedlabs.scope.runner.ScopeRunner;
import com.undefinedlabs.scope.runner.TestStatus;
import com.undefinedlabs.scope.runner.rules.AbstractScopeRunnerAgentRule;
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.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 org.junit.Test;
import org.junit.internal.builders.IgnoredClassRunner;
import org.junit.runners.model.FrameworkMethod;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;
import static net.bytebuddy.matcher.ElementMatchers.isSystemClassLoader;
import static net.bytebuddy.matcher.ElementMatchers.named;

public class JUnit4ScopeRunnerAgentRule extends AbstractScopeRunnerAgentRule {

    @Override
    protected Iterable transformers() {

        return Collections.singleton(newAgentBuilder()
            .type(named("org.junit.runners.BlockJUnit4ClassRunner"), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(JUnit4RunnerAdvice.class, named("getChildren")))
            .type(named("org.junit.internal.builders.IgnoredClassRunner"), isSystemClassLoader()).transform(new ScopeAgentAdvicedTransformer(IgnoredClassRunnerAdvice.class, named("run")))
        );
    }

    public static class JUnit4RunnerAdvice {

        @Advice.OnMethodExit
        public static void exit(@Advice.This Object runnerObj, @Advice.AllArguments Object[] args, @Advice.Return(readOnly = false, typing = DYNAMIC) Object returnObj) {
            final ScopeRunner runner = ScopeGlobalRunner.get();
            final List tests = (List) returnObj;
            final List finalTests = new ArrayList<>();

            final Tracer tracer = ScopeGlobalTracer.get();
            final ScopeSettings settings = ScopeSettingsResolver.INSTANCE.get();

            for(final FrameworkMethod test : tests) {
                final JUnit4ScopeFrameworkMethod scopeTest = new JUnit4ScopeFrameworkMethod(test);
                if(!runner.getTestStatuses(scopeTest.getTestSuite(), scopeTest.getTestName()).contains(TestStatus.CACHED)) {
                    finalTests.add(test);
                } else {
                    if((boolean)settings.getSetting(ScopeSettings.SCOPE_RUNNER_SEND_SPANS)) {
                        final Span span = tracer.buildSpan(scopeTest.getTestName())
                                .ignoreActiveSpan()
                                .withTag(TagKeys.COMPONENT, TagValues.Test.Framework.JUNIT_4)
                                .withTag(TagKeys.SPAN_KIND, TagValues.SPAN_KIND_TEST)
                                .withTag(TagKeys.Test.TEST_NAME, scopeTest.getTestName())
                                .withTag(TagKeys.Test.TEST_SUITE, scopeTest.getTestSuite())
                                .withTag(TagKeys.Test.TEST_FRAMEWORK, TagValues.Test.Framework.JUNIT_4)
                                .withTag(TagKeys.Test.TEST_LANGUAGE, TagValues.Test.Language.JAVA)
                                .withTag(TagKeys.Test.TEST_STATUS, TagValues.Test.TEST_CACHE)
                                .withTag(TagKeys.ERROR, false)
                                .start();

                        span.setBaggageItem(BaggageKeys.TRACE_KIND, BaggageValues.TRACE_KIND_TEST);
                        span.finish(SpanUtils.INSTANCE.getStartTimestampMicros(span));
                    }

                    Statistics.INSTANCE.registerRunnerCachedTest(TagValues.Test.Framework.JUNIT_4, scopeTest.getTestSuite(), scopeTest.getTestName());
                }
            }

            returnObj = finalTests;
        }
    }

    private static Class findTestClassFromIgnoredClassRunner(final Object runnerObj, final String fieldName) {
        try {
            final IgnoredClassRunner junitRunner = (IgnoredClassRunner) runnerObj;
            final Field fTestClassField = junitRunner.getClass().getDeclaredField(fieldName);
            fTestClassField.setAccessible(true);
            return (Class) fTestClassField.get(junitRunner);
        } catch (Exception e) {
            return null;
        }
    }

    public static Class findTestClassFromIgnoredClassRunner(final Object runnerObj) {
        final Class testClass = findTestClassFromIgnoredClassRunner(runnerObj, "clazz");
        if(testClass == null) {
            return findTestClassFromIgnoredClassRunner(runnerObj, "fTestClass");
        }

        return testClass;
    }

    public static class IgnoredClassRunnerAdvice {

        @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
        public static Object enter(@Advice.This Object runnerObj) {

            try {
                final Class fTestClass = JUnit4ScopeRunnerAgentRule.findTestClassFromIgnoredClassRunner(runnerObj);
                if(fTestClass == null) {
                    return null;
                }

                final List testMethods = MethodUtils.getMethodsListWithAnnotation(fTestClass, Test.class);
                boolean hasCachedTests = false;
                final ScopeRunner scopeRunner = ScopeGlobalRunner.get();

                for (int i = 0; i < testMethods.size() && !hasCachedTests; i++) {
                    final Method testMethod = testMethods.get(i);
                    hasCachedTests = scopeRunner.getTestStatuses(fTestClass.getName(), testMethod.getName()).contains(TestStatus.CACHED);
                }

                if (hasCachedTests) {
                    final Tracer tracer = ScopeGlobalTracer.get();
                    for (final Method testMethod : testMethods) {
                        final Span span = tracer.buildSpan(testMethod.getName())
                                .ignoreActiveSpan()
                                .withTag(TagKeys.COMPONENT, TagValues.Test.Framework.JUNIT_4)
                                .withTag(TagKeys.SPAN_KIND, TagValues.SPAN_KIND_TEST)
                                .withTag(TagKeys.Test.TEST_NAME, fTestClass.getName())
                                .withTag(TagKeys.Test.TEST_SUITE, testMethod.getName())
                                .withTag(TagKeys.Test.TEST_FRAMEWORK, TagValues.Test.Framework.JUNIT_4)
                                .withTag(TagKeys.Test.TEST_LANGUAGE, TagValues.Test.Language.JAVA)
                                .withTag(TagKeys.Test.TEST_STATUS, TagValues.Test.TEST_CACHE)
                                .withTag(TagKeys.ERROR, false)
                                .start();

                        span.setBaggageItem(BaggageKeys.TRACE_KIND, BaggageValues.TRACE_KIND_TEST);
                        span.finish(SpanUtils.INSTANCE.getStartTimestampMicros(span));

                        Statistics.INSTANCE.registerRunnerCachedTest("Junit4", fTestClass.getName(), testMethod.getName());
                    }

                    return new Object();
                }

                return null;
            } catch (Exception e) {
                return null;
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy