mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Introduce BugPatternTestExtractor with tests
This commit is contained in:
committed by
Stephan Schroevers
parent
554a3e634c
commit
4bcba7f2a0
@@ -9,6 +9,7 @@ import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.BugPattern.SeverityLevel;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
@@ -44,7 +45,7 @@ final class BugPatternExtractor implements Extractor<BugPatternDocumentation> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExtract(ClassTree tree) {
|
||||
public boolean canExtract(ClassTree tree, VisitorState state) {
|
||||
return ASTHelpers.hasDirectAnnotationWithSimpleName(tree, BugPattern.class.getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.instanceMethod;
|
||||
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.google.errorprone.annotations.Var;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import tech.picnic.errorprone.documentation.BugPatternTestExtractor.BugPatternTestDocumentation;
|
||||
|
||||
/**
|
||||
* An {@link Extractor} that describes how to extract data from a test that tests a {@code
|
||||
* BugChecker}.
|
||||
*/
|
||||
@Immutable
|
||||
final class BugPatternTestExtractor implements Extractor<BugPatternTestDocumentation> {
|
||||
private static final Matcher<MethodTree> JUNIT_TEST_METHOD =
|
||||
hasAnnotation("org.junit.jupiter.api.Test");
|
||||
|
||||
// XXX: Improve support for correctly extracting multiple sources from a single
|
||||
// `{BugCheckerRefactoring,Compilation}TestHelper` test.
|
||||
@Override
|
||||
public BugPatternTestDocumentation extract(ClassTree tree, Context context) {
|
||||
VisitorState state = VisitorState.createForUtilityPurposes(context);
|
||||
CollectBugPatternTests scanner = new CollectBugPatternTests(state);
|
||||
|
||||
tree.getMembers().stream()
|
||||
.filter(MethodTree.class::isInstance)
|
||||
.map(MethodTree.class::cast)
|
||||
.filter(m -> JUNIT_TEST_METHOD.matches(m, state))
|
||||
.forEach(m -> scanner.scan(m, null));
|
||||
|
||||
String className = tree.getSimpleName().toString();
|
||||
return new AutoValue_BugPatternTestExtractor_BugPatternTestDocumentation(
|
||||
className.substring(0, className.lastIndexOf("Test")),
|
||||
scanner.getIdentificationTests(),
|
||||
scanner.getReplacementTests());
|
||||
}
|
||||
|
||||
// XXX: Further improve the heuristics for determining what a BugPattern test is.
|
||||
@Override
|
||||
public boolean canExtract(ClassTree tree, VisitorState state) {
|
||||
String className = tree.getSimpleName().toString();
|
||||
if (!className.endsWith("Test")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ScanBugPatternTest scanBugPatternTest = new ScanBugPatternTest();
|
||||
scanBugPatternTest.scan(tree, state);
|
||||
|
||||
String bugPatternName = className.substring(0, className.lastIndexOf("Test"));
|
||||
return scanBugPatternTest.hasTestUsingClassInstance(bugPatternName);
|
||||
}
|
||||
|
||||
private static final class ScanBugPatternTest extends TreeScanner<@Nullable Void, VisitorState> {
|
||||
private static final Matcher<ExpressionTree> BUG_PATTERN_TEST_METHOD =
|
||||
staticMethod()
|
||||
.onDescendantOfAny(
|
||||
"com.google.errorprone.CompilationTestHelper",
|
||||
"com.google.errorprone.BugCheckerRefactoringTestHelper")
|
||||
.named("newInstance");
|
||||
|
||||
private final List<String> encounteredClasses = new ArrayList<>();
|
||||
|
||||
boolean hasTestUsingClassInstance(String clazz) {
|
||||
return encounteredClasses.contains(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitMethodInvocation(MethodInvocationTree node, VisitorState state) {
|
||||
if (BUG_PATTERN_TEST_METHOD.matches(node, state)) {
|
||||
MemberSelectTree firstArgumentTree = (MemberSelectTree) node.getArguments().get(0);
|
||||
encounteredClasses.add(firstArgumentTree.getExpression().toString());
|
||||
}
|
||||
return super.visitMethodInvocation(node, state);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class CollectBugPatternTests
|
||||
extends TreeScanner<@Nullable Void, @Nullable Void> {
|
||||
private static final Matcher<ExpressionTree> IDENTIFICATION_SOURCE_LINES =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.CompilationTestHelper")
|
||||
.named("addSourceLines");
|
||||
private static final Matcher<ExpressionTree> REPLACEMENT_INPUT =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper")
|
||||
.named("addInputLines");
|
||||
private static final Matcher<ExpressionTree> REPLACEMENT_OUTPUT =
|
||||
instanceMethod()
|
||||
.onDescendantOf("com.google.errorprone.BugCheckerRefactoringTestHelper.ExpectOutput")
|
||||
.named("addOutputLines");
|
||||
|
||||
private final VisitorState state;
|
||||
private final List<String> identificationTests = new ArrayList<>();
|
||||
private final List<BugPatternReplacementTestDocumentation> replacementTests = new ArrayList<>();
|
||||
|
||||
@Var private String replacementOutputLines = "";
|
||||
|
||||
CollectBugPatternTests(VisitorState state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public ImmutableList<String> getIdentificationTests() {
|
||||
return ImmutableList.copyOf(identificationTests);
|
||||
}
|
||||
|
||||
public ImmutableList<BugPatternReplacementTestDocumentation> getReplacementTests() {
|
||||
return ImmutableList.copyOf(replacementTests);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Void visitMethodInvocation(MethodInvocationTree node, @Nullable Void unused) {
|
||||
if (IDENTIFICATION_SOURCE_LINES.matches(node, state)) {
|
||||
identificationTests.add(getSourceLines(node));
|
||||
} else if (REPLACEMENT_INPUT.matches(node, state)) {
|
||||
/* The visitor starts with `addOutputLines` and in the next visit it will go over the `addInputLines`. */
|
||||
replacementTests.add(
|
||||
BugPatternReplacementTestDocumentation.create(
|
||||
getSourceLines(node), replacementOutputLines));
|
||||
} else if (REPLACEMENT_OUTPUT.matches(node, state)) {
|
||||
replacementOutputLines = getSourceLines(node);
|
||||
}
|
||||
return super.visitMethodInvocation(node, unused);
|
||||
}
|
||||
|
||||
// XXX: Duplicate from `ErrorProneTestSourceFormat`, should we move this to `SourceCode` util?
|
||||
private static String getSourceLines(MethodInvocationTree tree) {
|
||||
List<? extends ExpressionTree> sourceLines =
|
||||
tree.getArguments().subList(1, tree.getArguments().size());
|
||||
StringBuilder source = new StringBuilder();
|
||||
|
||||
for (ExpressionTree sourceLine : sourceLines) {
|
||||
Object value = ASTHelpers.constValue(sourceLine);
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
source.append(value).append('\n');
|
||||
}
|
||||
|
||||
return source.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class BugPatternTestDocumentation {
|
||||
abstract String name();
|
||||
|
||||
abstract ImmutableList<String> identificationTests();
|
||||
|
||||
abstract ImmutableList<BugPatternReplacementTestDocumentation> replacementTests();
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class BugPatternReplacementTestDocumentation {
|
||||
static BugPatternReplacementTestDocumentation create(String sourceLines, String outputLines) {
|
||||
return new AutoValue_BugPatternTestExtractor_BugPatternReplacementTestDocumentation(
|
||||
sourceLines, outputLines);
|
||||
}
|
||||
|
||||
abstract String inputLines();
|
||||
|
||||
abstract String outputLines();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskEvent.Kind;
|
||||
@@ -57,7 +58,7 @@ final class DocumentationGeneratorTaskListener implements TaskListener {
|
||||
return;
|
||||
}
|
||||
|
||||
ExtractorType.findMatchingType(classTree)
|
||||
ExtractorType.findMatchingType(classTree, VisitorState.createForUtilityPurposes(context))
|
||||
.ifPresent(
|
||||
extractorType ->
|
||||
writeToFile(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
@@ -27,7 +28,9 @@ interface Extractor<T> {
|
||||
* ClassTree}.
|
||||
*
|
||||
* @param tree The {@link ClassTree} of interest.
|
||||
* @param state A {@link VisitorState} describes the context in which the given {@link ClassTree}
|
||||
* is found.
|
||||
* @return {@code true} iff data extraction is supported.
|
||||
*/
|
||||
boolean canExtract(ClassTree tree);
|
||||
boolean canExtract(ClassTree tree, VisitorState state);
|
||||
}
|
||||
|
||||
@@ -2,13 +2,15 @@ package tech.picnic.errorprone.documentation;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
|
||||
/** An enumeration of {@link Extractor} types. */
|
||||
enum ExtractorType {
|
||||
BUG_PATTERN("bugpattern", new BugPatternExtractor());
|
||||
BUG_PATTERN("bugpattern", new BugPatternExtractor()),
|
||||
BUG_PATTERN_TEST("bugpattern-test", new BugPatternTestExtractor());
|
||||
|
||||
private static final ImmutableSet<ExtractorType> TYPES =
|
||||
Sets.immutableEnumSet(EnumSet.allOf(ExtractorType.class));
|
||||
@@ -29,7 +31,7 @@ enum ExtractorType {
|
||||
return extractor;
|
||||
}
|
||||
|
||||
static Optional<ExtractorType> findMatchingType(ClassTree tree) {
|
||||
return TYPES.stream().filter(type -> type.getExtractor().canExtract(tree)).findFirst();
|
||||
static Optional<ExtractorType> findMatchingType(ClassTree tree, VisitorState state) {
|
||||
return TYPES.stream().filter(type -> type.getExtractor().canExtract(tree, state)).findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ final class BugPatternExtractorTest {
|
||||
.hasMessage("BugPattern annotation must be present");
|
||||
|
||||
return buildDescription(tree)
|
||||
.setMessage(String.format("Can extract: %s", extractor.canExtract(tree)))
|
||||
.setMessage(String.format("Can extract: %s", extractor.canExtract(tree, state)))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,465 @@
|
||||
package tech.picnic.errorprone.documentation;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
final class BugPatternTestExtractorTest {
|
||||
@Test
|
||||
void noBugPatternTest(@TempDir Path outputDirectory) {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"TestCheckerWithoutAnnotation.java",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"",
|
||||
"public final class TestCheckerWithoutAnnotation extends BugChecker {}");
|
||||
|
||||
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
void minimalBugPatternTest(@TempDir Path outputDirectory) throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" CompilationTestHelper compilationTestHelper = CompilationTestHelper.newInstance(IdentityConversion.class, getClass());",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-minimal.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentBugPatternTest(@TempDir Path outputDirectory) {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class DifferentBugPattern extends BugChecker {}",
|
||||
"",
|
||||
" CompilationTestHelper compilationTestHelper = CompilationTestHelper.newInstance(DifferentBugPattern.class, getClass());",
|
||||
"}");
|
||||
|
||||
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
void differentBugPatternWithTest(@TempDir Path outputDirectory) {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class TestChecker extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void identification() {",
|
||||
" CompilationTestHelper.newInstance(TestChecker.class, getClass())",
|
||||
" .addSourceLines(\"A.java\", \"class A {}\")",
|
||||
" .doTest();",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
assertThat(outputDirectory.toAbsolutePath()).isEmptyDirectory();
|
||||
}
|
||||
|
||||
@Test
|
||||
void bugPatternTestSingleIdentification(@TempDir Path outputDirectory) throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void identification() {",
|
||||
" CompilationTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .addSourceLines(",
|
||||
" \"A.java\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" // BUG: Diagnostic contains:\",",
|
||||
" \" Boolean b = Boolean.valueOf(Boolean.FALSE);\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest();",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-identification.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bugPatternTestIdentificationMultipleSourceLines(@TempDir Path outputDirectory)
|
||||
throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void identification() {",
|
||||
" CompilationTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .addSourceLines(",
|
||||
" \"A.java\",",
|
||||
" \"public final class A {}\")",
|
||||
" .addSourceLines(",
|
||||
" \"B.java\",",
|
||||
" \"public final class B {}\")",
|
||||
" .doTest();",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-identification-two-sources.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bugPatternTestSingleReplacement(@TempDir Path outputDirectory) throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void replacementFirstSuggestedFix() {",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .setFixChooser(FixChoosers.FIRST)",
|
||||
" .addInputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .addOutputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.of();\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-replacement.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bugPatternTestMultipleReplacements(@TempDir Path outputDirectory) throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void replacementFirstSuggestedFix() {",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .setFixChooser(FixChoosers.FIRST)",
|
||||
" .addInputLines(",
|
||||
" \"A.java\",",
|
||||
" \"public final class A {}\")",
|
||||
" .addOutputLines(",
|
||||
" \"A.java\",",
|
||||
" \"public final class A {}\")",
|
||||
" .addInputLines(",
|
||||
" \"B.java\",",
|
||||
" \"public final class B {}\")",
|
||||
" .addOutputLines(",
|
||||
" \"B.java\",",
|
||||
" \"public final class B {}\")",
|
||||
" .doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-replacement-two-sources.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bugPatternReplacementExpectUnchanged(@TempDir Path outputDirectory) throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void replacementFirstSuggestedFix() {",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .addInputLines(",
|
||||
" \"A.java\",",
|
||||
" \"public final class A {}\")",
|
||||
" .expectUnchanged()",
|
||||
" .doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-replacement-expect-unchanged.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bugPatternTestIdentificationAndReplacement(@TempDir Path outputDirectory)
|
||||
throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void identification() {",
|
||||
" CompilationTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .addSourceLines(",
|
||||
" \"A.java\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" // BUG: Diagnostic contains:\",",
|
||||
" \" Boolean b = Boolean.valueOf(Boolean.FALSE);\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest();",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void replacementFirstSuggestedFix() {",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .setFixChooser(FixChoosers.FIRST)",
|
||||
" .addInputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .addOutputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.of();\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-identification-and-replacement.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void bugPatternTestMultipleIdentificationAndReplacement(@TempDir Path outputDirectory)
|
||||
throws IOException {
|
||||
JavacTaskCompilation.compile(
|
||||
outputDirectory,
|
||||
"IdentityConversionTest.java",
|
||||
"package pkg;",
|
||||
"",
|
||||
"import static com.google.errorprone.BugCheckerRefactoringTestHelper.FixChoosers.SECOND;",
|
||||
"",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper;",
|
||||
"import com.google.errorprone.bugpatterns.BugChecker;",
|
||||
"import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;",
|
||||
"import com.google.errorprone.CompilationTestHelper;",
|
||||
"import org.junit.jupiter.api.Test;",
|
||||
"",
|
||||
"final class IdentityConversionTest {",
|
||||
" private static class IdentityConversion extends BugChecker {}",
|
||||
"",
|
||||
" @Test",
|
||||
" void identification() {",
|
||||
" CompilationTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .addSourceLines(",
|
||||
" \"A.java\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" // BUG: Diagnostic contains:\",",
|
||||
" \" Boolean b = Boolean.valueOf(Boolean.FALSE);\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest();",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void identification2() {",
|
||||
" CompilationTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .addSourceLines(",
|
||||
" \"B.java\",",
|
||||
" \"public final class B {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" // BUG: Diagnostic contains:\",",
|
||||
" \" Boolean b = Boolean.valueOf(Boolean.FALSE);\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest();",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void replacementFirstSuggestedFix() {",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .addInputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .addOutputLines(",
|
||||
" \"A.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class A {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.of();\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);",
|
||||
" }",
|
||||
"",
|
||||
" @Test",
|
||||
" void replacementSecondSuggestedFix() {",
|
||||
" BugCheckerRefactoringTestHelper.newInstance(IdentityConversion.class, getClass())",
|
||||
" .setFixChooser(SECOND)",
|
||||
" .addInputLines(",
|
||||
" \"B.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class B {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .addOutputLines(",
|
||||
" \"B.java\",",
|
||||
" \"import com.google.common.collect.ImmutableSet;\",",
|
||||
" \"\",",
|
||||
" \"public final class B {\",",
|
||||
" \" public void m() {\",",
|
||||
" \" ImmutableSet<Object> set = ImmutableSet.of();\",",
|
||||
" \" }\",",
|
||||
" \"}\")",
|
||||
" .doTest(BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH);",
|
||||
" }",
|
||||
"}");
|
||||
|
||||
verifyFileMatchesResource(
|
||||
outputDirectory,
|
||||
"bugpattern-test-IdentityConversionTest.json",
|
||||
"bugpattern-test-documentation-multiple-identification-and-replacement.json");
|
||||
}
|
||||
|
||||
private static void verifyFileMatchesResource(
|
||||
Path outputDirectory, String fileName, String resourceName) throws IOException {
|
||||
assertThat(Files.readString(outputDirectory.resolve(fileName)))
|
||||
.isEqualToIgnoringWhitespace(getResource(resourceName));
|
||||
}
|
||||
|
||||
// XXX: Once we support only JDK 15+, drop this method in favour of including the resources as
|
||||
// text blocks in this class. (This also requires renaming the `verifyFileMatchesResource`
|
||||
// method.)
|
||||
private static String getResource(String resourceName) throws IOException {
|
||||
return Resources.toString(
|
||||
Resources.getResource(BugPatternTestExtractorTest.class, resourceName), UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [
|
||||
"public final class A {\n public void m() {\n // BUG: Diagnostic contains:\n Boolean b = Boolean.valueOf(Boolean.FALSE);\n}\n}\n"
|
||||
],
|
||||
"replacementTests": [
|
||||
{
|
||||
"inputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class A {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\n }\n}\n",
|
||||
"outputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class A {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.of();\n }\n}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [
|
||||
"public final class B {}\n",
|
||||
"public final class A {}\n"
|
||||
],
|
||||
"replacementTests": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [
|
||||
"public final class A {\n public void m() {\n // BUG: Diagnostic contains:\n Boolean b = Boolean.valueOf(Boolean.FALSE);\n}\n}\n"
|
||||
],
|
||||
"replacementTests": []
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [],
|
||||
"replacementTests": []
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [
|
||||
"public final class A {\n public void m() {\n // BUG: Diagnostic contains:\n Boolean b = Boolean.valueOf(Boolean.FALSE);\n}\n}\n",
|
||||
"public final class B {\n public void m() {\n // BUG: Diagnostic contains:\n Boolean b = Boolean.valueOf(Boolean.FALSE);\n}\n}\n"
|
||||
],
|
||||
"replacementTests": [
|
||||
{
|
||||
"inputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class A {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\n }\n}\n",
|
||||
"outputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class A {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.of();\n }\n}\n"
|
||||
},
|
||||
{
|
||||
"inputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class B {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\n }\n}\n",
|
||||
"outputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class B {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.of();\n }\n}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [],
|
||||
"replacementTests": [
|
||||
{
|
||||
"inputLines": "public final class A {}\n",
|
||||
"outputLines": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [],
|
||||
"replacementTests": [
|
||||
{
|
||||
"inputLines": "public final class B {}\n",
|
||||
"outputLines": "public final class B {}\n"
|
||||
},
|
||||
{
|
||||
"inputLines": "public final class A {}\n",
|
||||
"outputLines": "public final class A {}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "IdentityConversion",
|
||||
"identificationTests": [],
|
||||
"replacementTests": [
|
||||
{
|
||||
"inputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class A {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.copyOf(ImmutableSet.of());\n }\n}\n",
|
||||
"outputLines": "import com.google.common.collect.ImmutableSet;\n\npublic final class A {\n public void m() {\n ImmutableSet<Object> set = ImmutableSet.of();\n }\n}\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user