Skip to content

Commit d82d824

Browse files
authored
Codegen test refactoring (#453)
1 parent b1827fe commit d82d824

File tree

31 files changed

+909
-1231
lines changed

31 files changed

+909
-1231
lines changed

annotation-processor-common/src/testFixtures/java/ru/tinkoff/kora/annotation/processor/common/AbstractAnnotationProcessorTest.java

Lines changed: 51 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,21 @@
1010
import org.reactivestreams.Publisher;
1111
import reactor.core.publisher.Flux;
1212
import reactor.core.publisher.Mono;
13-
import ru.tinkoff.kora.annotation.processor.common.TestUtils.ProcessorOptions;
14-
import ru.tinkoff.kora.annotation.processor.common.compile.ByteArrayJavaFileObject;
15-
import ru.tinkoff.kora.annotation.processor.common.compile.KoraCompileTestJavaFileManager;
1613
import ru.tinkoff.kora.application.graph.*;
1714

1815
import javax.annotation.processing.Processor;
19-
import javax.tools.Diagnostic;
20-
import javax.tools.JavaCompiler;
21-
import javax.tools.JavaFileObject;
22-
import javax.tools.ToolProvider;
2316
import java.io.IOException;
24-
import java.io.StringWriter;
2517
import java.lang.reflect.Constructor;
2618
import java.lang.reflect.InvocationTargetException;
2719
import java.lang.reflect.Method;
2820
import java.nio.charset.StandardCharsets;
2921
import java.nio.file.Files;
22+
import java.nio.file.Path;
3023
import java.nio.file.Paths;
31-
import java.util.*;
24+
import java.util.ArrayList;
25+
import java.util.Comparator;
26+
import java.util.List;
27+
import java.util.Map;
3228
import java.util.concurrent.CompletionStage;
3329
import java.util.concurrent.ExecutionException;
3430
import java.util.concurrent.Flow;
@@ -39,9 +35,6 @@
3935

4036
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
4137
public abstract class AbstractAnnotationProcessorTest {
42-
43-
private final JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
44-
4538
protected TestInfo testInfo;
4639
protected CompileResult compileResult;
4740

@@ -83,71 +76,58 @@ protected String commonImports() {
8376
}
8477

8578
protected CompileResult compile(List<Processor> processors, @Language("java") String... sources) {
86-
return compile(processors, Collections.emptyList(), sources);
87-
}
88-
89-
protected CompileResult compile(List<Processor> processors, List<ProcessorOptions> processorOptions, @Language("java") String... sources) {
90-
var w = new StringWriter();
91-
var diagnostic = new ArrayList<Diagnostic<? extends JavaFileObject>>();
9279
var testPackage = testPackage();
9380
var testClass = this.testInfo.getTestClass().get();
9481
var testMethod = this.testInfo.getTestMethod().get();
9582
var commonImports = this.commonImports();
96-
var sourceList = Arrays.stream(sources).map(s -> "package %s;\n%s\n/**\n* @see %s#%s \n*/\n".formatted(testPackage, commonImports, testClass.getCanonicalName(), testMethod.getName()) + s)
97-
.map(s -> {
98-
var prefixes = List.of("class ", "interface ", "@interface ", "record ", "enum ");
99-
var firstClass = prefixes.stream()
100-
.map(p -> Map.entry(s.indexOf(p), p.length()))
101-
.filter(e -> e.getKey() >= 0)
102-
.map(e -> e.getKey() + e.getValue())
103-
.min(Comparator.comparing(Function.identity()))
104-
.map(classStart -> {
105-
var firstSpace = s.indexOf(" ", classStart + 1);
106-
var firstBracket = s.indexOf("(", classStart + 1);
107-
var firstSquareBracket = s.indexOf("{", classStart + 1);
108-
var classEnd = IntStream.of(firstSpace, firstBracket, firstSquareBracket)
109-
.filter(i -> i >= 0)
110-
.min()
111-
.getAsInt();
112-
var className = s.substring(classStart, classEnd).trim();
113-
int generic = className.indexOf('<');
114-
if (generic == -1) {
115-
return className;
116-
} else {
117-
return className.substring(0, generic);
118-
}
119-
})
120-
.get();
121-
122-
return new ByteArrayJavaFileObject(JavaFileObject.Kind.SOURCE, testPackage + "." + firstClass, s.getBytes(StandardCharsets.UTF_8));
123-
})
124-
.toList();
125-
126-
try (var delegate = javaCompiler.getStandardFileManager(diagnostic::add, Locale.US, StandardCharsets.UTF_8);
127-
var manager = new KoraCompileTestJavaFileManager(this.testInfo, delegate, sourceList.toArray(ByteArrayJavaFileObject[]::new))) {
128-
129-
var defaultOptions = new LinkedHashSet<>(List.of("--release", "17", "-XprintRounds"));
130-
defaultOptions.addAll(processorOptions.stream().map(o -> o.value).toList());
131-
132-
var task = javaCompiler.getTask(
133-
w,
134-
manager,
135-
diagnostic::add,
136-
defaultOptions,
137-
null,
138-
sourceList
139-
);
140-
task.setProcessors(processors);
141-
task.setLocale(Locale.US);
142-
task.call();
143-
w.close();
144-
return this.compileResult = new CompileResult(testPackage, diagnostic, manager);
145-
} catch (RuntimeException e) {
146-
if (e.getCause() instanceof RuntimeException er) {
147-
throw er;
83+
var sourceList = new ArrayList<Path>();
84+
for (var source : sources) {
85+
var string = "package %s;\n%s\n/**\n* @see %s#%s \n*/\n".formatted(testPackage, commonImports, testClass.getCanonicalName(), testMethod.getName()) + source;
86+
var prefixes = List.of("class ", "interface ", "@interface ", "record ", "enum ");
87+
var firstClass = prefixes.stream()
88+
.map(p -> Map.entry(string.indexOf(p), p.length()))
89+
.filter(e -> e.getKey() >= 0)
90+
.map(e -> e.getKey() + e.getValue())
91+
.min(Comparator.comparing(Function.identity()))
92+
.map(classStart -> {
93+
var firstSpace = string.indexOf(" ", classStart + 1);
94+
var firstBracket = string.indexOf("(", classStart + 1);
95+
var firstSquareBracket = string.indexOf("{", classStart + 1);
96+
var classEnd = IntStream.of(firstSpace, firstBracket, firstSquareBracket)
97+
.filter(i -> i >= 0)
98+
.min()
99+
.getAsInt();
100+
var className = string.substring(classStart, classEnd).trim();
101+
int generic = className.indexOf('<');
102+
if (generic == -1) {
103+
return className;
104+
} else {
105+
return className.substring(0, generic);
106+
}
107+
})
108+
.get();
109+
var className = testPackage + "." + firstClass;
110+
var path = Paths.get(".", "build", "in-test-generated", "sources").resolve(className.replace('.', '/') + ".java");
111+
try {
112+
Files.createDirectories(path.getParent());
113+
Files.write(path, string.getBytes(StandardCharsets.UTF_8));
114+
} catch (IOException e) {
115+
throw new RuntimeException(e);
148116
}
117+
sourceList.add(path);
118+
}
119+
120+
try {
121+
var jc = new JavaCompilation()
122+
.withSources(sourceList)
123+
.withProcessors(processors);
124+
var cl = jc.compile();
125+
return this.compileResult = new CompileResult(testPackage, jc.diagnostics(), cl);
126+
} catch (TestUtils.CompilationErrorException e) {
127+
return this.compileResult = new CompileResult(testPackage, e.diagnostics, null);
128+
} catch (RuntimeException e) {
149129
throw e;
150-
} catch (IOException e) {
130+
} catch (Exception e) {
151131
throw new RuntimeException(e);
152132
}
153133
}

annotation-processor-common/src/testFixtures/java/ru/tinkoff/kora/annotation/processor/common/CompileResult.java

Lines changed: 23 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package ru.tinkoff.kora.annotation.processor.common;
22

33

4-
import ru.tinkoff.kora.annotation.processor.common.compile.KoraCompileTestJavaFileManager;
5-
64
import javax.tools.Diagnostic;
7-
import javax.tools.FileObject;
85
import javax.tools.JavaFileObject;
9-
import javax.tools.StandardLocation;
106
import java.io.IOException;
11-
import java.io.StringWriter;
7+
import java.nio.file.Files;
8+
import java.nio.file.Path;
9+
import java.nio.file.Paths;
1210
import java.util.*;
1311
import java.util.stream.Collectors;
1412

15-
public record CompileResult(String testPackage, List<Diagnostic<? extends JavaFileObject>> diagnostic, KoraCompileTestJavaFileManager manager) {
13+
public record CompileResult(String testPackage, List<Diagnostic<? extends JavaFileObject>> diagnostic, ClassLoader cl) {
1614
public boolean isFailed() {
1715
return this.diagnostic.stream()
1816
.anyMatch(d -> d.getKind() == Diagnostic.Kind.ERROR);
@@ -37,13 +35,9 @@ public List<Diagnostic<? extends JavaFileObject>> errors() {
3735
}
3836

3937

40-
public FileObject generatedSourceFile(String className) throws IOException {
41-
return this.manager.getFileForInput(StandardLocation.SOURCE_OUTPUT, this.testPackage, className);
42-
}
43-
4438
public Class<?> loadClass(String className) {
4539
try {
46-
return this.manager.getClassLoader(StandardLocation.CLASS_OUTPUT).loadClass(this.testPackage + "." + className);
40+
return cl.loadClass(this.testPackage + "." + className);
4741
} catch (ClassNotFoundException e) {
4842
throw new IllegalStateException(e);
4943
}
@@ -56,21 +50,23 @@ public CompilationFailedException(String message) {
5650
}
5751

5852
public RuntimeException compilationException() {
59-
var diagnosticMap = new IdentityHashMap<JavaFileObject, Map<Long, List<Diagnostic<? extends JavaFileObject>>>>();
53+
var diagnosticMap = new HashMap<Path, Map<Long, List<Diagnostic<? extends JavaFileObject>>>>();
6054
for (var d : this.diagnostic) {
61-
var map = diagnosticMap.computeIfAbsent(d.getSource(), o -> new HashMap<>());
55+
var map = diagnosticMap.computeIfAbsent(Path.of(d.getSource().toUri()).toAbsolutePath(), o -> new HashMap<>());
6256
map.computeIfAbsent(d.getLineNumber(), l -> new ArrayList<>()).add(d);
6357
}
6458

6559
try {
6660
var j = new StringJoiner("\n", "\n", "\n");
67-
for (var javaFileObject : this.manager.list(StandardLocation.SOURCE_OUTPUT, "", Set.of(JavaFileObject.Kind.SOURCE), true)) {
68-
var diagnostic = diagnosticMap.getOrDefault(javaFileObject, Map.of());
69-
j.add(javaFileObject.getName()).add(javaFileToString(javaFileObject, diagnostic));
61+
var generatedSources = Files.walk(Path.of("build/in-test-generated/sources")).filter(Files::isRegularFile).toList();
62+
for (var src : generatedSources) {
63+
var diagnostic = diagnosticMap.getOrDefault(src.toAbsolutePath(), Map.of());
64+
j.add(src.toString()).add(javaFileToString(src, diagnostic));
7065
}
71-
for (var javaFileObject : this.manager.list(StandardLocation.SOURCE_PATH, "", Set.of(JavaFileObject.Kind.SOURCE), true)) {
66+
var sources = Files.walk(Paths.get(".", "build", "in-test-generated", "sources")).filter(Files::isRegularFile).toList();
67+
for (var javaFileObject : sources) {
7268
var diagnostic = diagnosticMap.getOrDefault(javaFileObject, Map.of());
73-
j.add(javaFileObject.getName()).add(javaFileToString(javaFileObject, diagnostic));
69+
j.add(javaFileObject.toString()).add(javaFileToString(javaFileObject, diagnostic));
7470
}
7571

7672
var errors = this.diagnostic.stream()
@@ -84,20 +80,16 @@ public RuntimeException compilationException() {
8480

8581
}
8682

87-
private static String javaFileToString(JavaFileObject object, Map<Long, List<Diagnostic<? extends JavaFileObject>>> diagnostic) throws IOException {
83+
private static String javaFileToString(Path object, Map<Long, List<Diagnostic<? extends JavaFileObject>>> diagnostic) throws IOException {
8884
var j = new StringJoiner("\n", "\n", "\n");
89-
try (var r = object.openReader(true);
90-
var sw = new StringWriter()) {
91-
r.transferTo(sw);
92-
sw.flush();
93-
var lines = sw.toString().lines().toList();
94-
for (int i = 0; i < lines.size(); i++) {
95-
var lineDiagnostic = diagnostic.getOrDefault((long) i + 1, List.of());
96-
j.add("%03d | %s".formatted(i, lines.get(i)));
97-
for (var d : lineDiagnostic) {
98-
var diagnosticString = " ".repeat(((int) d.getColumnNumber()) - 1) + "^ " + d.getMessage(Locale.US);
99-
j.add(diagnosticString.indent(6));
100-
}
85+
var lines = Files.readAllLines(object);
86+
87+
for (int i = 0; i < lines.size(); i++) {
88+
var lineDiagnostic = diagnostic.getOrDefault((long) i + 1, List.of());
89+
j.add("%03d | %s".formatted(i, lines.get(i)));
90+
for (var d : lineDiagnostic) {
91+
var diagnosticString = " ".repeat(((int) d.getColumnNumber()) - 1) + "^ " + d.getMessage(Locale.US);
92+
j.add(diagnosticString.indent(6));
10193
}
10294
}
10395
return j.toString();

0 commit comments

Comments
 (0)