Browse Source

Add support for specifying compiler options

This commit is a prerequisite to help suppressing deprecating warnings
by allowing tests to validate that the compiler does not encounter them.

See gh-29597
pull/31407/head
Stéphane Nicoll 1 year ago
parent
commit
4b14a0b42c
  1. 60
      spring-core-test/src/main/java/org/springframework/core/test/tools/TestCompiler.java
  2. 81
      spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java

60
spring-core-test/src/main/java/org/springframework/core/test/tools/TestCompiler.java

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import java.util.stream.Stream;
import javax.annotation.processing.Processor; import javax.annotation.processing.Processor;
import javax.tools.Diagnostic; import javax.tools.Diagnostic;
@ -41,6 +42,7 @@ import org.springframework.lang.Nullable;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Scott Frederick * @author Scott Frederick
* @author Stephane Nicoll
* @since 6.0 * @since 6.0
* @see #forSystem() * @see #forSystem()
*/ */
@ -59,10 +61,12 @@ public final class TestCompiler {
private final List<Processor> processors; private final List<Processor> processors;
private final List<String> compilerOptions;
private TestCompiler(@Nullable ClassLoader classLoader, JavaCompiler compiler, private TestCompiler(@Nullable ClassLoader classLoader, JavaCompiler compiler,
SourceFiles sourceFiles, ResourceFiles resourceFiles, ClassFiles classFiles, SourceFiles sourceFiles, ResourceFiles resourceFiles, ClassFiles classFiles,
List<Processor> processors) { List<Processor> processors, List<String> compilerOptions) {
this.classLoader = classLoader; this.classLoader = classLoader;
this.compiler = compiler; this.compiler = compiler;
@ -70,6 +74,7 @@ public final class TestCompiler {
this.resourceFiles = resourceFiles; this.resourceFiles = resourceFiles;
this.classFiles = classFiles; this.classFiles = classFiles;
this.processors = processors; this.processors = processors;
this.compilerOptions = compilerOptions;
} }
@ -88,7 +93,7 @@ public final class TestCompiler {
*/ */
public static TestCompiler forCompiler(JavaCompiler javaCompiler) { public static TestCompiler forCompiler(JavaCompiler javaCompiler) {
return new TestCompiler(null, javaCompiler, SourceFiles.none(), return new TestCompiler(null, javaCompiler, SourceFiles.none(),
ResourceFiles.none(), ClassFiles.none(), Collections.emptyList()); ResourceFiles.none(), ClassFiles.none(), Collections.emptyList(), Collections.emptyList());
} }
/** /**
@ -108,7 +113,7 @@ public final class TestCompiler {
public TestCompiler withSources(SourceFile... sourceFiles) { public TestCompiler withSources(SourceFile... sourceFiles) {
return new TestCompiler(this.classLoader, this.compiler, return new TestCompiler(this.classLoader, this.compiler,
this.sourceFiles.and(sourceFiles), this.resourceFiles, this.sourceFiles.and(sourceFiles), this.resourceFiles,
this.classFiles, this.processors); this.classFiles, this.processors, this.compilerOptions);
} }
/** /**
@ -119,7 +124,7 @@ public final class TestCompiler {
public TestCompiler withSources(Iterable<SourceFile> sourceFiles) { public TestCompiler withSources(Iterable<SourceFile> sourceFiles) {
return new TestCompiler(this.classLoader, this.compiler, return new TestCompiler(this.classLoader, this.compiler,
this.sourceFiles.and(sourceFiles), this.resourceFiles, this.sourceFiles.and(sourceFiles), this.resourceFiles,
this.classFiles, this.processors); this.classFiles, this.processors, this.compilerOptions);
} }
/** /**
@ -130,7 +135,7 @@ public final class TestCompiler {
public TestCompiler withSources(SourceFiles sourceFiles) { public TestCompiler withSources(SourceFiles sourceFiles) {
return new TestCompiler(this.classLoader, this.compiler, return new TestCompiler(this.classLoader, this.compiler,
this.sourceFiles.and(sourceFiles), this.resourceFiles, this.sourceFiles.and(sourceFiles), this.resourceFiles,
this.classFiles, this.processors); this.classFiles, this.processors, this.compilerOptions);
} }
/** /**
@ -140,7 +145,8 @@ public final class TestCompiler {
*/ */
public TestCompiler withResources(ResourceFile... resourceFiles) { public TestCompiler withResources(ResourceFile... resourceFiles) {
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors); this.resourceFiles.and(resourceFiles), this.classFiles, this.processors,
this.compilerOptions);
} }
/** /**
@ -150,7 +156,8 @@ public final class TestCompiler {
*/ */
public TestCompiler withResources(Iterable<ResourceFile> resourceFiles) { public TestCompiler withResources(Iterable<ResourceFile> resourceFiles) {
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors); this.resourceFiles.and(resourceFiles), this.classFiles, this.processors,
this.compilerOptions);
} }
/** /**
@ -160,7 +167,8 @@ public final class TestCompiler {
*/ */
public TestCompiler withResources(ResourceFiles resourceFiles) { public TestCompiler withResources(ResourceFiles resourceFiles) {
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles.and(resourceFiles), this.classFiles, this.processors); this.resourceFiles.and(resourceFiles), this.classFiles, this.processors,
this.compilerOptions);
} }
/** /**
@ -170,7 +178,8 @@ public final class TestCompiler {
*/ */
public TestCompiler withClasses(Iterable<ClassFile> classFiles) { public TestCompiler withClasses(Iterable<ClassFile> classFiles) {
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles, this.classFiles.and(classFiles), this.processors); this.resourceFiles, this.classFiles.and(classFiles), this.processors,
this.compilerOptions);
} }
/** /**
@ -182,7 +191,7 @@ public final class TestCompiler {
List<Processor> mergedProcessors = new ArrayList<>(this.processors); List<Processor> mergedProcessors = new ArrayList<>(this.processors);
mergedProcessors.addAll(Arrays.asList(processors)); mergedProcessors.addAll(Arrays.asList(processors));
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles, this.classFiles, mergedProcessors); this.resourceFiles, this.classFiles, mergedProcessors, this.compilerOptions);
} }
/** /**
@ -194,7 +203,32 @@ public final class TestCompiler {
List<Processor> mergedProcessors = new ArrayList<>(this.processors); List<Processor> mergedProcessors = new ArrayList<>(this.processors);
processors.forEach(mergedProcessors::add); processors.forEach(mergedProcessors::add);
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles, return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles, this.classFiles, mergedProcessors); this.resourceFiles, this.classFiles, mergedProcessors, this.compilerOptions);
}
/**
* Create a new {@link TestCompiler} instance with the additional compiler options.
* @param options the additional compiler options
* @return a new {@code TestCompiler} instance
* @since 6.1
*/
public TestCompiler withCompilerOptions(String... options) {
List<String> mergedCompilerOptions = Stream.concat(this.compilerOptions.stream(),
Arrays.stream(options)).distinct().toList();
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles, this.classFiles, this.processors, mergedCompilerOptions);
}
/**
* Create a new {@link TestCompiler} instance that fails if any warning is
* encountered. This sets the {@code -Xlint:all} and {@code -Werror} compiler
* options.
* @return a new {@code TestCompiler} instance
* @since 6.1
* @see #withCompilerOptions(String...)
*/
public TestCompiler failOnWarning() {
return withCompilerOptions("-Xlint:all", "-Werror");
} }
/** /**
@ -275,8 +309,8 @@ public final class TestCompiler {
standardFileManager, classLoaderToUse, this.classFiles, this.resourceFiles); standardFileManager, classLoaderToUse, this.classFiles, this.resourceFiles);
if (!this.sourceFiles.isEmpty()) { if (!this.sourceFiles.isEmpty()) {
Errors errors = new Errors(); Errors errors = new Errors();
CompilationTask task = this.compiler.getTask(null, fileManager, errors, null, CompilationTask task = this.compiler.getTask(null, fileManager, errors,
null, compilationUnits); this.compilerOptions, null, compilationUnits);
if (!this.processors.isEmpty()) { if (!this.processors.isEmpty()) {
task.setProcessors(this.processors); task.setProcessors(this.processors);
} }

81
spring-core-test/src/test/java/org/springframework/core/test/tools/TestCompilerTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -43,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Scott Frederick * @author Scott Frederick
* @author Stephane Nicoll
*/ */
class TestCompilerTests { class TestCompilerTests {
@ -87,6 +88,20 @@ class TestCompilerTests {
} }
"""; """;
private static final String HELLO_DEPRECATED = """
package com.example;
import java.util.function.Supplier;
public class Hello implements Supplier<String> {
@Deprecated
public String get() {
return "Hello Deprecated";
}
}
""";
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -119,6 +134,70 @@ class TestCompilerTests {
})); }));
} }
@Test
@SuppressWarnings("unchecked")
void compileWhenSourceUseDeprecateCodeAndNoOptionSet() {
SourceFile main = SourceFile.of("""
package com.example;
public class Main {
public static void main(String[] args) {
new Hello().get();
}
}
""");
TestCompiler.forSystem().withSources(
SourceFile.of(HELLO_DEPRECATED), main).compile(compiled -> {
Supplier<String> supplier = compiled.getInstance(Supplier.class,
"com.example.Hello");
assertThat(supplier.get()).isEqualTo("Hello Deprecated");
});
}
@Test
void compileWhenSourceUseDeprecateCodeAndFailOnWarningIsSet() {
SourceFile main = SourceFile.of("""
package com.example;
public class Main {
public static void main(String[] args) {
new Hello().get();
}
}
""");
assertThatExceptionOfType(CompilationException.class).isThrownBy(
() -> TestCompiler.forSystem().failOnWarning().withSources(
SourceFile.of(HELLO_DEPRECATED), main).compile(compiled -> {
})).withMessageContaining("warnings found and -Werror specified");
}
@Test
@SuppressWarnings("unchecked")
void compileWhenSourceUseDeprecateCodeAndFailOnWarningWithSuppressWarnings() {
SourceFile main = SourceFile.of("""
package com.example;
public class Main {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
new Hello().get();
}
}
""");
TestCompiler.forSystem().failOnWarning().withSources(
SourceFile.of(HELLO_DEPRECATED), main).compile(compiled -> {
Supplier<String> supplier = compiled.getInstance(Supplier.class,
"com.example.Hello");
assertThat(supplier.get()).isEqualTo("Hello Deprecated");
});
}
@Test @Test
void withSourcesArrayAddsSource() { void withSourcesArrayAddsSource() {
SourceFile sourceFile = SourceFile.of(HELLO_WORLD); SourceFile sourceFile = SourceFile.of(HELLO_WORLD);

Loading…
Cancel
Save