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);
}
}
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;
}
Aggregations