Search in sources :

Example 1 with QuarkusClassWriter

use of io.quarkus.deployment.QuarkusClassWriter in project quarkus by quarkusio.

the class JunitTestRunner method discoverTestClasses.

private DiscoveryResult discoverTestClasses() {
    // maven has a lot of rules around this and is configurable
    // for now this is out of scope, we are just going to do annotation based discovery
    // we will need to fix this sooner rather than later though
    // we also only run tests from the current module, which we can also revisit later
    Indexer indexer = new Indexer();
    moduleInfo.getTest().ifPresent(test -> {
        try (Stream<Path> files = Files.walk(Paths.get(test.getClassesPath()))) {
            files.filter(s -> s.getFileName().toString().endsWith(".class")).forEach(s -> {
                try (InputStream in = Files.newInputStream(s)) {
                    indexer.index(in);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    });
    Index index = indexer.complete();
    // we now have all the classes by name
    // these tests we never run
    Set<String> integrationTestClasses = new HashSet<>();
    for (DotName intAnno : Arrays.asList(QUARKUS_INTEGRATION_TEST, NATIVE_IMAGE_TEST)) {
        for (AnnotationInstance i : index.getAnnotations(intAnno)) {
            DotName name = i.target().asClass().name();
            integrationTestClasses.add(name.toString());
            for (ClassInfo clazz : index.getAllKnownSubclasses(name)) {
                integrationTestClasses.add(clazz.name().toString());
            }
        }
    }
    Set<String> quarkusTestClasses = new HashSet<>();
    for (var a : Arrays.asList(QUARKUS_TEST, QUARKUS_MAIN_TEST)) {
        for (AnnotationInstance i : index.getAnnotations(a)) {
            DotName name = i.target().asClass().name();
            quarkusTestClasses.add(name.toString());
            for (ClassInfo clazz : index.getAllKnownSubclasses(name)) {
                if (!integrationTestClasses.contains(clazz.name().toString())) {
                    quarkusTestClasses.add(clazz.name().toString());
                }
            }
        }
    }
    Set<DotName> allTestAnnotations = collectTestAnnotations(index);
    Set<DotName> allTestClasses = new HashSet<>();
    Map<DotName, DotName> enclosingClasses = new HashMap<>();
    for (DotName annotation : allTestAnnotations) {
        for (AnnotationInstance instance : index.getAnnotations(annotation)) {
            if (instance.target().kind() == AnnotationTarget.Kind.METHOD) {
                ClassInfo classInfo = instance.target().asMethod().declaringClass();
                allTestClasses.add(classInfo.name());
                if (classInfo.classAnnotation(NESTED) != null) {
                    var enclosing = classInfo.enclosingClass();
                    if (enclosing != null) {
                        enclosingClasses.put(classInfo.name(), enclosing);
                    }
                }
            } else if (instance.target().kind() == AnnotationTarget.Kind.FIELD) {
                ClassInfo classInfo = instance.target().asField().declaringClass();
                allTestClasses.add(classInfo.name());
                if (classInfo.classAnnotation(NESTED) != null) {
                    var enclosing = classInfo.enclosingClass();
                    if (enclosing != null) {
                        enclosingClasses.put(classInfo.name(), enclosing);
                    }
                }
            }
        }
    }
    // now we have all the classes with @Test
    // figure out which ones we want to actually run
    Set<String> unitTestClasses = new HashSet<>();
    for (DotName testClass : allTestClasses) {
        String name = testClass.toString();
        if (integrationTestClasses.contains(name) || quarkusTestClasses.contains(name)) {
            continue;
        }
        var enclosing = enclosingClasses.get(testClass);
        if (enclosing != null) {
            if (integrationTestClasses.contains(enclosing.toString())) {
                integrationTestClasses.add(name);
                continue;
            } else if (quarkusTestClasses.contains(enclosing.toString())) {
                quarkusTestClasses.add(name);
                continue;
            }
        }
        ClassInfo clazz = index.getClassByName(testClass);
        if (Modifier.isAbstract(clazz.flags())) {
            continue;
        }
        unitTestClasses.add(name);
    }
    List<Class<?>> itClasses = new ArrayList<>();
    List<Class<?>> utClasses = new ArrayList<>();
    for (String i : quarkusTestClasses) {
        try {
            itClasses.add(Thread.currentThread().getContextClassLoader().loadClass(i));
        } catch (ClassNotFoundException e) {
            log.warnf("Failed to load test class %s (possibly as it was added after the test run started), it will not be executed this run.", i);
        }
    }
    itClasses.sort(Comparator.comparing(new Function<Class<?>, String>() {

        @Override
        public String apply(Class<?> aClass) {
            ClassInfo def = index.getClassByName(DotName.createSimple(aClass.getName()));
            AnnotationInstance testProfile = def.classAnnotation(TEST_PROFILE);
            if (testProfile == null) {
                return "$$" + aClass.getName();
            }
            return testProfile.value().asClass().name().toString() + "$$" + aClass.getName();
        }
    }));
    QuarkusClassLoader cl = null;
    if (!unitTestClasses.isEmpty()) {
        // we need to work the unit test magic
        // this is a lot more complex
        // we need to transform the classes to make the tracing magic work
        QuarkusClassLoader deploymentClassLoader = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader();
        Set<String> classesToTransform = new HashSet<>(deploymentClassLoader.getLocalClassNames());
        Map<String, byte[]> transformedClasses = new HashMap<>();
        for (String i : classesToTransform) {
            try {
                byte[] classData = IoUtil.readBytes(deploymentClassLoader.getResourceAsStream(i.replace('.', '/') + ".class"));
                ClassReader cr = new ClassReader(classData);
                ClassWriter writer = new QuarkusClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
                cr.accept(new TestTracingProcessor.TracingClassVisitor(writer, i), 0);
                transformedClasses.put(i.replace('.', '/') + ".class", writer.toByteArray());
            } catch (Exception e) {
                log.error("Failed to instrument " + i + " for usage tracking", e);
            }
        }
        cl = testApplication.createDeploymentClassLoader();
        cl.reset(Collections.emptyMap(), transformedClasses);
        for (String i : unitTestClasses) {
            try {
                utClasses.add(cl.loadClass(i));
            } catch (ClassNotFoundException exception) {
                log.warnf("Failed to load test class %s (possibly as it was added after the test run started), it will not be executed this run.", i);
            }
        }
    }
    if (testType == TestType.ALL) {
        // run unit style tests first
        // before the quarkus tests have started
        // which stops quarkus interfering with WireMock
        List<Class<?>> ret = new ArrayList<>(utClasses.size() + itClasses.size());
        ret.addAll(utClasses);
        ret.addAll(itClasses);
        return new DiscoveryResult(cl, ret);
    } else if (testType == TestType.UNIT) {
        return new DiscoveryResult(cl, utClasses);
    } else {
        return new DiscoveryResult(cl, itClasses);
    }
}
Also used : Arrays(java.util.Arrays) RepeatedTest(org.junit.jupiter.api.RepeatedTest) TestFactory(org.junit.jupiter.api.TestFactory) ReportEntry(org.junit.platform.engine.reporting.ReportEntry) TestPlan(org.junit.platform.launcher.TestPlan) QuarkusClassWriter(io.quarkus.deployment.QuarkusClassWriter) ClassInfo(org.jboss.jandex.ClassInfo) Map(java.util.Map) AnnotationTarget(org.jboss.jandex.AnnotationTarget) LauncherFactory(org.junit.platform.launcher.core.LauncherFactory) Tag(org.junit.jupiter.api.Tag) DiscoverySelectors(org.junit.platform.engine.discovery.DiscoverySelectors) Method(java.lang.reflect.Method) Path(java.nio.file.Path) MethodSource(org.junit.platform.engine.support.descriptor.MethodSource) IoUtil(io.quarkus.deployment.util.IoUtil) PostDiscoveryFilter(org.junit.platform.launcher.PostDiscoveryFilter) TestSource(org.junit.platform.engine.TestSource) Set(java.util.Set) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) Collectors(java.util.stream.Collectors) LauncherConfig(org.junit.platform.launcher.core.LauncherConfig) Objects(java.util.Objects) Test(org.junit.jupiter.api.Test) TestIdentifier(org.junit.platform.launcher.TestIdentifier) List(java.util.List) Stream(java.util.stream.Stream) LauncherDiscoveryRequestBuilder(org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder) ClassReader(org.objectweb.asm.ClassReader) ClassScanResult(io.quarkus.deployment.dev.ClassScanResult) AnnotationInstance(org.jboss.jandex.AnnotationInstance) Modifier(java.lang.reflect.Modifier) Optional(java.util.Optional) Tags(org.junit.jupiter.api.Tags) Testable(org.junit.platform.commons.annotation.Testable) Pattern(java.util.regex.Pattern) TestDescriptor(org.junit.platform.engine.TestDescriptor) AnnotatedElement(java.lang.reflect.AnnotatedElement) TestExecutionListener(org.junit.platform.launcher.TestExecutionListener) ClassWriter(org.objectweb.asm.ClassWriter) CuratedApplication(io.quarkus.bootstrap.app.CuratedApplication) DevModeContext(io.quarkus.deployment.dev.DevModeContext) Launcher(org.junit.platform.launcher.Launcher) Logger(org.jboss.logging.Logger) DotName(org.jboss.jandex.DotName) FilterResult(org.junit.platform.engine.FilterResult) TracingHandler(io.quarkus.dev.testing.TracingHandler) HashMap(java.util.HashMap) Deque(java.util.Deque) TestExecutionResult(org.junit.platform.engine.TestExecutionResult) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) Nested(org.junit.jupiter.api.Nested) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) ClassSource(org.junit.platform.engine.support.descriptor.ClassSource) Indexer(org.jboss.jandex.Indexer) TestTemplate(org.junit.jupiter.api.TestTemplate) QuarkusConsole(io.quarkus.dev.console.QuarkusConsole) Index(org.jboss.jandex.Index) Files(java.nio.file.Files) EngineFilter(org.junit.platform.launcher.EngineFilter) LauncherDiscoveryRequest(org.junit.platform.launcher.LauncherDiscoveryRequest) IOException(java.io.IOException) UniqueId(org.junit.platform.engine.UniqueId) Consumer(java.util.function.Consumer) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) Paths(java.nio.file.Paths) LinkedBlockingDeque(java.util.concurrent.LinkedBlockingDeque) Comparator(java.util.Comparator) Collections(java.util.Collections) InputStream(java.io.InputStream) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Index(org.jboss.jandex.Index) DotName(org.jboss.jandex.DotName) Function(java.util.function.Function) Indexer(org.jboss.jandex.Indexer) QuarkusClassWriter(io.quarkus.deployment.QuarkusClassWriter) HashSet(java.util.HashSet) Path(java.nio.file.Path) InputStream(java.io.InputStream) IOException(java.io.IOException) QuarkusClassLoader(io.quarkus.bootstrap.classloading.QuarkusClassLoader) QuarkusClassWriter(io.quarkus.deployment.QuarkusClassWriter) ClassWriter(org.objectweb.asm.ClassWriter) IOException(java.io.IOException) ClassReader(org.objectweb.asm.ClassReader) AnnotationInstance(org.jboss.jandex.AnnotationInstance) ClassInfo(org.jboss.jandex.ClassInfo)

Example 2 with QuarkusClassWriter

use of io.quarkus.deployment.QuarkusClassWriter in project quarkus by quarkusio.

the class ClassTransformingBuildStep method transformClass.

private byte[] transformClass(String className, List<BiFunction<String, ClassVisitor, ClassVisitor>> visitors, byte[] classData, List<BiFunction<String, byte[], byte[]>> preVisitFunctions, int classReaderOptions) {
    for (BiFunction<String, byte[], byte[]> i : preVisitFunctions) {
        classData = i.apply(className, classData);
        if (classData == null) {
            return null;
        }
    }
    byte[] data;
    if (!visitors.isEmpty()) {
        ClassReader cr = new ClassReader(classData);
        ClassWriter writer = new QuarkusClassWriter(cr, ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
        ClassVisitor visitor = writer;
        for (BiFunction<String, ClassVisitor, ClassVisitor> i : visitors) {
            visitor = i.apply(className, visitor);
            if (visitor instanceof QuarkusClassVisitor) {
                ((QuarkusClassVisitor) visitor).setOriginalClassReaderOptions(classReaderOptions);
            }
        }
        cr.accept(visitor, classReaderOptions);
        data = writer.toByteArray();
    } else {
        data = classData;
    }
    if (BootstrapDebug.DEBUG_TRANSFORMED_CLASSES_DIR != null) {
        File debugPath = new File(BootstrapDebug.DEBUG_TRANSFORMED_CLASSES_DIR);
        if (!debugPath.exists()) {
            debugPath.mkdir();
        }
        File classFile = new File(debugPath, className.replace('.', '/') + ".class");
        classFile.getParentFile().mkdirs();
        try (FileOutputStream classWriter = new FileOutputStream(classFile)) {
            classWriter.write(data);
        } catch (Exception e) {
            log.errorf(e, "Failed to write transformed class %s", className);
        }
        log.infof("Wrote transformed class to %s", classFile.getAbsolutePath());
    }
    return data;
}
Also used : FileOutputStream(java.io.FileOutputStream) ClassReader(org.objectweb.asm.ClassReader) QuarkusClassVisitor(io.quarkus.deployment.QuarkusClassVisitor) QuarkusClassWriter(io.quarkus.deployment.QuarkusClassWriter) QuarkusClassVisitor(io.quarkus.deployment.QuarkusClassVisitor) ClassVisitor(org.objectweb.asm.ClassVisitor) File(java.io.File) QuarkusClassWriter(io.quarkus.deployment.QuarkusClassWriter) ClassWriter(org.objectweb.asm.ClassWriter) IOException(java.io.IOException) ExecutionException(java.util.concurrent.ExecutionException)

Aggregations

QuarkusClassWriter (io.quarkus.deployment.QuarkusClassWriter)2 IOException (java.io.IOException)2 CuratedApplication (io.quarkus.bootstrap.app.CuratedApplication)1 QuarkusClassLoader (io.quarkus.bootstrap.classloading.QuarkusClassLoader)1 QuarkusClassVisitor (io.quarkus.deployment.QuarkusClassVisitor)1 ClassScanResult (io.quarkus.deployment.dev.ClassScanResult)1 DevModeContext (io.quarkus.deployment.dev.DevModeContext)1 IoUtil (io.quarkus.deployment.util.IoUtil)1 QuarkusConsole (io.quarkus.dev.console.QuarkusConsole)1 TracingHandler (io.quarkus.dev.testing.TracingHandler)1 File (java.io.File)1 FileOutputStream (java.io.FileOutputStream)1 InputStream (java.io.InputStream)1 AnnotatedElement (java.lang.reflect.AnnotatedElement)1 Method (java.lang.reflect.Method)1 Modifier (java.lang.reflect.Modifier)1 Files (java.nio.file.Files)1 Path (java.nio.file.Path)1 Paths (java.nio.file.Paths)1 ArrayList (java.util.ArrayList)1