Skip to content

Commit

Permalink
Add support for specifying compiler options
Browse files Browse the repository at this point in the history
This commit is a prerequisite to help suppressing deprecating warnings
by allowing tests to validate that the compiler does not encounter them.

See spring-projectsgh-29597
  • Loading branch information
snicoll committed Oct 10, 2023
1 parent cd3daa8 commit b2c3ec8
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Locale;
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

import javax.annotation.processing.Processor;
import javax.tools.Diagnostic;
Expand All @@ -41,6 +42,7 @@
*
* @author Phillip Webb
* @author Scott Frederick
* @author Stephane Nicoll
* @since 6.0
* @see #forSystem()
*/
Expand All @@ -59,17 +61,20 @@ public final class TestCompiler {

private final List<Processor> processors;

private final List<String> compilerOptions;


private TestCompiler(@Nullable ClassLoader classLoader, JavaCompiler compiler,
SourceFiles sourceFiles, ResourceFiles resourceFiles, ClassFiles classFiles,
List<Processor> processors) {
List<Processor> processors, List<String> compilerOptions) {

this.classLoader = classLoader;
this.compiler = compiler;
this.sourceFiles = sourceFiles;
this.resourceFiles = resourceFiles;
this.classFiles = classFiles;
this.processors = processors;
this.compilerOptions = compilerOptions;
}


Expand All @@ -88,7 +93,7 @@ public static TestCompiler forSystem() {
*/
public static TestCompiler forCompiler(JavaCompiler javaCompiler) {
return new TestCompiler(null, javaCompiler, SourceFiles.none(),
ResourceFiles.none(), ClassFiles.none(), Collections.emptyList());
ResourceFiles.none(), ClassFiles.none(), Collections.emptyList(), Collections.emptyList());
}

/**
Expand All @@ -108,7 +113,7 @@ public TestCompiler with(UnaryOperator<TestCompiler> customizer) {
public TestCompiler withSources(SourceFile... sourceFiles) {
return new TestCompiler(this.classLoader, this.compiler,
this.sourceFiles.and(sourceFiles), this.resourceFiles,
this.classFiles, this.processors);
this.classFiles, this.processors, this.compilerOptions);
}

/**
Expand All @@ -119,7 +124,7 @@ public TestCompiler withSources(SourceFile... sourceFiles) {
public TestCompiler withSources(Iterable<SourceFile> sourceFiles) {
return new TestCompiler(this.classLoader, this.compiler,
this.sourceFiles.and(sourceFiles), this.resourceFiles,
this.classFiles, this.processors);
this.classFiles, this.processors, this.compilerOptions);
}

/**
Expand All @@ -130,7 +135,7 @@ public TestCompiler withSources(Iterable<SourceFile> sourceFiles) {
public TestCompiler withSources(SourceFiles sourceFiles) {
return new TestCompiler(this.classLoader, this.compiler,
this.sourceFiles.and(sourceFiles), this.resourceFiles,
this.classFiles, this.processors);
this.classFiles, this.processors, this.compilerOptions);
}

/**
Expand All @@ -140,7 +145,8 @@ public TestCompiler withSources(SourceFiles sourceFiles) {
*/
public TestCompiler withResources(ResourceFile... resourceFiles) {
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);
}

/**
Expand All @@ -150,7 +156,8 @@ public TestCompiler withResources(ResourceFile... resourceFiles) {
*/
public TestCompiler withResources(Iterable<ResourceFile> resourceFiles) {
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);
}

/**
Expand All @@ -160,7 +167,8 @@ public TestCompiler withResources(Iterable<ResourceFile> resourceFiles) {
*/
public TestCompiler withResources(ResourceFiles resourceFiles) {
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);
}

/**
Expand All @@ -170,7 +178,8 @@ public TestCompiler withResources(ResourceFiles resourceFiles) {
*/
public TestCompiler withClasses(Iterable<ClassFile> classFiles) {
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);
}

/**
Expand All @@ -182,7 +191,7 @@ public TestCompiler withProcessors(Processor... processors) {
List<Processor> mergedProcessors = new ArrayList<>(this.processors);
mergedProcessors.addAll(Arrays.asList(processors));
return new TestCompiler(this.classLoader, this.compiler, this.sourceFiles,
this.resourceFiles, this.classFiles, mergedProcessors);
this.resourceFiles, this.classFiles, mergedProcessors, this.compilerOptions);
}

/**
Expand All @@ -194,7 +203,32 @@ public TestCompiler withProcessors(Iterable<Processor> processors) {
List<Processor> mergedProcessors = new ArrayList<>(this.processors);
processors.forEach(mergedProcessors::add);
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");
}

/**
Expand Down Expand Up @@ -275,8 +309,8 @@ private DynamicClassLoader compile() {
standardFileManager, classLoaderToUse, this.classFiles, this.resourceFiles);
if (!this.sourceFiles.isEmpty()) {
Errors errors = new Errors();
CompilationTask task = this.compiler.getTask(null, fileManager, errors, null,
null, compilationUnits);
CompilationTask task = this.compiler.getTask(null, fileManager, errors,
this.compilerOptions, null, compilationUnits);
if (!this.processors.isEmpty()) {
task.setProcessors(this.processors);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,6 +43,7 @@
* @author Phillip Webb
* @author Andy Wilkinson
* @author Scott Frederick
* @author Stephane Nicoll
*/
class TestCompilerTests {

Expand Down Expand Up @@ -87,6 +88,20 @@ public String get() {
}
""";

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
@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -119,6 +134,70 @@ void compileWhenSourceHasCompileErrors() {
}));
}

@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
void withSourcesArrayAddsSource() {
SourceFile sourceFile = SourceFile.of(HELLO_WORLD);
Expand Down

0 comments on commit b2c3ec8

Please sign in to comment.