mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
7 Commits
v0.19.0
...
gdejong/ps
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3132810c47 | ||
|
|
5de2496035 | ||
|
|
b8ab20b754 | ||
|
|
46aab0f5ba | ||
|
|
84086d9fee | ||
|
|
4451319fb8 | ||
|
|
af46c602e7 |
@@ -141,12 +141,12 @@
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.fixes.Replacement;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.AssignmentTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.tools.javac.tree.JCTree;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import tech.picnic.errorprone.bugpatterns.util.AnnotationAttributeMatcher;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that replaces a predefined list of annotation attributes with its own
|
||||
* separate annotation.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Replace annotation attributes with an annotation",
|
||||
linkType = NONE,
|
||||
tags = REFACTORING,
|
||||
severity = ERROR)
|
||||
public final class AnnotationAttributeReplacement extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final ImmutableMap<AnnotationAttributeMatcher, AnnotationAttributeReplacer>
|
||||
ANNOTATION_ATTRIBUTE_REPLACEMENT =
|
||||
ImmutableMap.<AnnotationAttributeMatcher, AnnotationAttributeReplacer>builder()
|
||||
.put(
|
||||
singleArgumentMatcher("org.testng.annotations.Test#singleThreaded"),
|
||||
(annotation, argument, state) ->
|
||||
Optional.of(
|
||||
SuggestedFix.builder()
|
||||
.merge(removeAnnotationArgument(annotation, argument, state))
|
||||
.merge(
|
||||
SuggestedFix.prefixWith(
|
||||
annotation,
|
||||
"// XXX: Removed argument `singleThreaded = true`, as this cannot be migrated to JUnit!\n"))
|
||||
.build()))
|
||||
.put(
|
||||
singleArgumentMatcher("org.testng.annotations.Test#priority"),
|
||||
(annotation, argument, state) -> {
|
||||
ClassTree classTree = state.findEnclosing(ClassTree.class);
|
||||
if (classTree == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (argument.getKind() != Tree.Kind.ASSIGNMENT) {
|
||||
return Optional.empty();
|
||||
}
|
||||
AssignmentTree assignmentTree = (AssignmentTree) argument;
|
||||
|
||||
return Optional.of(
|
||||
SuggestedFix.builder()
|
||||
.merge(removeAnnotationArgument(annotation, argument, state))
|
||||
.merge(
|
||||
SuggestedFix.postfixWith(
|
||||
annotation,
|
||||
String.format(
|
||||
"\n@org.junit.jupiter.api.Order(%s)",
|
||||
SourceCode.treeToString(
|
||||
assignmentTree.getExpression(), state))))
|
||||
.merge(
|
||||
SuggestedFix.prefixWith(
|
||||
classTree,
|
||||
"@TestMethodOrder(MethodOrderer.OrderAnnotation.class)\n"))
|
||||
.addImport("org.junit.jupiter.api.TestMethodOrder")
|
||||
.addImport("org.junit.jupiter.api.MethodOrderer")
|
||||
.build());
|
||||
})
|
||||
.put(
|
||||
singleArgumentMatcher("org.testng.annotations.Test#description"),
|
||||
(annotation, argument, state) ->
|
||||
Optional.of(argument)
|
||||
.filter(AssignmentTree.class::isInstance)
|
||||
.map(AssignmentTree.class::cast)
|
||||
.map(
|
||||
assignmentTree ->
|
||||
SuggestedFix.builder()
|
||||
.merge(removeAnnotationArgument(annotation, argument, state))
|
||||
.merge(
|
||||
SuggestedFix.postfixWith(
|
||||
annotation,
|
||||
String.format(
|
||||
"\n@org.junit.jupiter.api.DisplayName(%s)",
|
||||
SourceCode.treeToString(
|
||||
assignmentTree.getExpression(), state))))
|
||||
.build()))
|
||||
.put(
|
||||
singleArgumentMatcher("org.testng.annotations.Test#groups"),
|
||||
(annotation, argument, state) ->
|
||||
Optional.of(argument)
|
||||
.filter(AssignmentTree.class::isInstance)
|
||||
.map(AssignmentTree.class::cast)
|
||||
.map(
|
||||
assignmentTree ->
|
||||
SuggestedFix.builder()
|
||||
.merge(removeAnnotationArgument(annotation, argument, state))
|
||||
.merge(
|
||||
SuggestedFix.postfixWith(
|
||||
annotation,
|
||||
String.format(
|
||||
"\n@org.junit.jupiter.api.Tag(%s)",
|
||||
SourceCode.treeToString(
|
||||
assignmentTree.getExpression(), state))))
|
||||
.build()))
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
ImmutableList<AnnotationTree> annotations =
|
||||
ASTHelpers.getAnnotations(tree).stream().collect(toImmutableList());
|
||||
|
||||
SuggestedFix.Builder builder = SuggestedFix.builder();
|
||||
annotations.forEach(
|
||||
annotation -> {
|
||||
ANNOTATION_ATTRIBUTE_REPLACEMENT.forEach(
|
||||
(matcher, fixBuilder) ->
|
||||
matcher
|
||||
.extractMatchingArguments(annotation)
|
||||
.forEach(
|
||||
argument ->
|
||||
fixBuilder
|
||||
.buildFix(annotation, argument, state)
|
||||
.ifPresent(builder::merge)));
|
||||
|
||||
tryRemoveTrailingParenthesis(annotation, builder.build(), state)
|
||||
.ifPresent(builder::merge);
|
||||
});
|
||||
|
||||
return builder.isEmpty() ? Description.NO_MATCH : describeMatch(tree, builder.build());
|
||||
}
|
||||
|
||||
private static Optional<SuggestedFix> tryRemoveTrailingParenthesis(
|
||||
AnnotationTree annotation, SuggestedFix fix, VisitorState state) {
|
||||
JCTree.JCCompilationUnit compileUnit =
|
||||
((JCTree.JCCompilationUnit) state.findEnclosing(CompilationUnitTree.class));
|
||||
if (compileUnit == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Set<Replacement> replacements = fix.getReplacements(compileUnit.endPositions);
|
||||
String annotationSource = SourceCode.treeToString(annotation, state).replace(", ", ",");
|
||||
String annotationArguments =
|
||||
annotationSource.substring(
|
||||
annotationSource.indexOf("(") + 1, annotationSource.length() - 1);
|
||||
int argumentReplacementLength = replacements.stream().mapToInt(Replacement::length).sum();
|
||||
if (argumentReplacementLength != annotationArguments.length()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return replacements.stream()
|
||||
.filter(replacement -> replacement.length() != 0)
|
||||
.map(Replacement::startPosition)
|
||||
.reduce(Integer::min)
|
||||
.flatMap(
|
||||
min ->
|
||||
replacements.stream()
|
||||
.filter(replacement -> replacement.length() != 0)
|
||||
.map(Replacement::endPosition)
|
||||
.reduce(Integer::max)
|
||||
.map(
|
||||
max ->
|
||||
SuggestedFix.builder()
|
||||
.merge(SuggestedFix.replace(min - 1, min, ""))
|
||||
.merge(SuggestedFix.replace(max, max + 1, ""))
|
||||
.build()));
|
||||
}
|
||||
|
||||
private static SuggestedFix removeAnnotationArgument(
|
||||
AnnotationTree annotation, ExpressionTree argument, VisitorState state) {
|
||||
String annotationSource = SourceCode.treeToString(annotation, state);
|
||||
String argumentSource = SourceCode.treeToString(argument, state);
|
||||
int argumentSourceIndex = annotationSource.indexOf(argumentSource);
|
||||
boolean endsWithComma =
|
||||
annotationSource
|
||||
.substring(
|
||||
argumentSourceIndex + argumentSource.length(),
|
||||
argumentSourceIndex + argumentSource.length() + 1)
|
||||
.equals(",");
|
||||
return SuggestedFix.builder().replace(argument, "", 0, endsWithComma ? 1 : 0).build();
|
||||
}
|
||||
|
||||
private static AnnotationAttributeMatcher singleArgumentMatcher(String fullyQualifiedArgument) {
|
||||
return AnnotationAttributeMatcher.create(
|
||||
Optional.of(ImmutableList.of(fullyQualifiedArgument)), ImmutableList.of());
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface AnnotationAttributeReplacer {
|
||||
Optional<SuggestedFix> buildFix(
|
||||
AnnotationTree annotation, ExpressionTree argument, VisitorState state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that replaces TestNG annotations with their JUnit counterpart, if one exists
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Migrate TestNG test annotations to JUnit",
|
||||
linkType = NONE,
|
||||
severity = WARNING,
|
||||
tags = REFACTORING)
|
||||
public final class TestNGAnnotation extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final ImmutableMap<Matcher<AnnotationTree>, String>
|
||||
TESTNG_ANNOTATION_REPLACEMENTS =
|
||||
ImmutableMap.<Matcher<AnnotationTree>, String>builder()
|
||||
.put(isType("org.testng.annotations.AfterClass"), "@org.junit.jupiter.api.AfterAll")
|
||||
.put(isType("org.testng.annotations.AfterMethod"), "@org.junit.jupiter.api.AfterEach")
|
||||
.put(isType("org.testng.annotations.BeforeClass"), "@org.junit.jupiter.api.BeforeAll")
|
||||
.put(
|
||||
isType("org.testng.annotations.BeforeMethod"),
|
||||
"@org.junit.jupiter.api.BeforeEach")
|
||||
.put(isType("org.testng.annotations.Test"), "@org.junit.jupiter.api.Test")
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
ASTHelpers.getAnnotations(tree).stream()
|
||||
.filter(annotation -> annotation.getArguments().isEmpty())
|
||||
.forEach(
|
||||
annotation ->
|
||||
TESTNG_ANNOTATION_REPLACEMENTS.entrySet().stream()
|
||||
.filter(entry -> entry.getKey().matches(annotation, state))
|
||||
.forEach(entry -> fix.replace(annotation, entry.getValue())));
|
||||
|
||||
return fix.isEmpty() ? Description.NO_MATCH : describeMatch(tree, fix.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.anyOf;
|
||||
import static com.google.errorprone.matchers.Matchers.hasAnnotation;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.google.errorprone.matchers.Matchers.methodHasVisibility;
|
||||
import static com.google.errorprone.matchers.Matchers.not;
|
||||
import static com.google.errorprone.matchers.MethodVisibility.Visibility.PUBLIC;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.ClassTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/** A {@link BugChecker} which flags class level `@Test` annotations from TestNG. */
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "A bug pattern to migrate TestNG Test annotations to methods",
|
||||
linkType = NONE,
|
||||
tags = REFACTORING,
|
||||
severity = ERROR)
|
||||
public final class TestNGClassLevelTestAnnotation extends BugChecker implements ClassTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<ClassTree> CLASS_TREE = hasAnnotation("org.testng.annotations.Test");
|
||||
private static final Matcher<MethodTree> UNMIGRATED_TESTNG_TEST_METHOD =
|
||||
allOf(
|
||||
methodHasVisibility(PUBLIC),
|
||||
not(
|
||||
anyOf(
|
||||
hasAnnotation("org.testng.annotations.Test"),
|
||||
hasAnnotation("org.testng.annotations.AfterClass"),
|
||||
hasAnnotation("org.testng.annotations.AfterMethod"),
|
||||
hasAnnotation("org.testng.annotations.BeforeClass"),
|
||||
hasAnnotation("org.testng.annotations.BeforeMethod"))));
|
||||
private static final Matcher<AnnotationTree> TESTNG_ANNOTATION =
|
||||
isType("org.testng.annotations.Test");
|
||||
|
||||
@Override
|
||||
public Description matchClass(ClassTree tree, VisitorState state) {
|
||||
if (!CLASS_TREE.matches(tree, state)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
Optional<? extends AnnotationTree> testAnnotation =
|
||||
ASTHelpers.getAnnotations(tree).stream()
|
||||
.filter(annotation -> TESTNG_ANNOTATION.matches(annotation, state))
|
||||
.findFirst();
|
||||
if (testAnnotation.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
SuggestedFix.Builder fix = SuggestedFix.builder();
|
||||
tree.getMembers().stream()
|
||||
.filter(MethodTree.class::isInstance)
|
||||
.map(MethodTree.class::cast)
|
||||
.filter(method -> UNMIGRATED_TESTNG_TEST_METHOD.matches(method, state))
|
||||
.filter(Predicate.not(ASTHelpers::isGeneratedConstructor))
|
||||
.forEach(
|
||||
methodTree ->
|
||||
fix.merge(
|
||||
SuggestedFix.prefixWith(
|
||||
methodTree,
|
||||
String.format(
|
||||
"%s\n", SourceCode.treeToString(testAnnotation.get(), state)))));
|
||||
|
||||
fix.delete(testAnnotation.get());
|
||||
|
||||
return describeMatch(testAnnotation.get(), fix.build());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.sun.source.tree.Tree.Kind.NEW_ARRAY;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.google.errorprone.util.ErrorProneToken;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import com.sun.source.tree.ReturnTree;
|
||||
import com.sun.tools.javac.code.Symbol.ClassSymbol;
|
||||
import com.sun.tools.javac.parser.Tokens.Comment;
|
||||
import com.sun.tools.javac.util.Name;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags TestNG {@link org.testng.annotations.DataProvider} methods and
|
||||
* provides an equivalent JUnit {@link org.junit.jupiter.params.ParameterizedTest} replacement.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Migrate TestNG DataProvider to JUnit argument streams",
|
||||
linkType = NONE,
|
||||
tags = REFACTORING,
|
||||
severity = ERROR)
|
||||
public final class TestNGDataProvider extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<AnnotationTree> TESTNG_DATAPROVIDER_ANNOTATION =
|
||||
isType("org.testng.annotations.DataProvider");
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
Optional<? extends AnnotationTree> dataProviderAnnotation =
|
||||
ASTHelpers.getAnnotations(tree).stream()
|
||||
.filter(annotation -> TESTNG_DATAPROVIDER_ANNOTATION.matches(annotation, state))
|
||||
.findFirst();
|
||||
if (dataProviderAnnotation.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
String methodName = tree.getName().toString();
|
||||
Name migratedName = state.getName(methodName + "Junit");
|
||||
ClassTree classTree = state.findEnclosing(ClassTree.class);
|
||||
if (classTree == null
|
||||
|| isMethodAlreadyMigratedInEnclosingClass(ASTHelpers.getSymbol(classTree), migratedName)) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ReturnTree returnTree = getReturnTree(tree);
|
||||
Optional<NewArrayTree> returnArrayTree = getDataProviderReturnTree(returnTree);
|
||||
if (returnArrayTree.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(
|
||||
dataProviderAnnotation.get(),
|
||||
SuggestedFix.builder()
|
||||
.addStaticImport("org.junit.jupiter.params.provider.Arguments.arguments")
|
||||
.addImport("java.util.stream.Stream")
|
||||
.addImport("org.junit.jupiter.params.provider.Arguments")
|
||||
.merge(
|
||||
SuggestedFix.postfixWith(
|
||||
tree,
|
||||
buildMethodSource(
|
||||
classTree.getSimpleName().toString(),
|
||||
migratedName.toString(),
|
||||
tree,
|
||||
returnTree,
|
||||
returnArrayTree.orElseThrow(),
|
||||
state)))
|
||||
.build());
|
||||
}
|
||||
|
||||
private static boolean isMethodAlreadyMigratedInEnclosingClass(
|
||||
ClassSymbol enclosingClassSymbol, Name methodName) {
|
||||
return enclosingClassSymbol.members().getSymbolsByName(methodName).iterator().hasNext();
|
||||
}
|
||||
|
||||
private static ReturnTree getReturnTree(MethodTree methodTree) {
|
||||
return methodTree.getBody().getStatements().stream()
|
||||
.filter(ReturnTree.class::isInstance)
|
||||
.findFirst()
|
||||
.map(ReturnTree.class::cast)
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
private static Optional<NewArrayTree> getDataProviderReturnTree(ReturnTree returnTree) {
|
||||
if (returnTree.getExpression().getKind() != NEW_ARRAY
|
||||
|| ((NewArrayTree) returnTree.getExpression()).getInitializers().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of((NewArrayTree) returnTree.getExpression());
|
||||
}
|
||||
|
||||
private static String buildMethodSource(
|
||||
String className,
|
||||
String name,
|
||||
MethodTree methodTree,
|
||||
ReturnTree returnTree,
|
||||
NewArrayTree newArrayTree,
|
||||
VisitorState state) {
|
||||
StringBuilder sourceBuilder =
|
||||
new StringBuilder(
|
||||
"@SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)\n")
|
||||
.append(" private static Stream<Arguments> ")
|
||||
.append(name)
|
||||
.append(" () ");
|
||||
|
||||
if (!methodTree.getThrows().isEmpty()) {
|
||||
sourceBuilder
|
||||
.append(" throws ")
|
||||
.append(
|
||||
methodTree.getThrows().stream()
|
||||
.filter(IdentifierTree.class::isInstance)
|
||||
.map(IdentifierTree.class::cast)
|
||||
.map(identifierTree -> identifierTree.getName().toString())
|
||||
.collect(joining(", ")));
|
||||
}
|
||||
|
||||
return sourceBuilder
|
||||
.append(" {\n")
|
||||
.append(extractMethodBodyWithoutReturnStatement(methodTree, returnTree, state))
|
||||
.append(" return ")
|
||||
.append(buildArgumentStream(className, newArrayTree, state))
|
||||
.append(";\n}")
|
||||
.toString();
|
||||
}
|
||||
|
||||
private static String extractMethodBodyWithoutReturnStatement(
|
||||
MethodTree methodTree, ReturnTree returnTree, VisitorState state) {
|
||||
String body = SourceCode.treeToString(methodTree.getBody(), state);
|
||||
return body.substring(2, body.indexOf(SourceCode.treeToString(returnTree, state)) - 1);
|
||||
}
|
||||
|
||||
private static String buildArgumentStream(
|
||||
String className, NewArrayTree newArrayTree, VisitorState state) {
|
||||
StringBuilder argumentsBuilder = new StringBuilder();
|
||||
|
||||
int startPos = ASTHelpers.getStartPosition(newArrayTree);
|
||||
int endPos = state.getEndPosition(newArrayTree);
|
||||
Map<Integer, List<Comment>> comments =
|
||||
state.getOffsetTokens(startPos, endPos).stream()
|
||||
.collect(
|
||||
toMap(ErrorProneToken::pos, ErrorProneToken::comments, (a, b) -> b, HashMap::new));
|
||||
argumentsBuilder.append(
|
||||
newArrayTree.getInitializers().stream()
|
||||
.map(
|
||||
expression ->
|
||||
buildArguments(
|
||||
expression,
|
||||
comments.getOrDefault(
|
||||
ASTHelpers.getStartPosition(expression), ImmutableList.of()),
|
||||
state))
|
||||
.collect(joining(",\n")));
|
||||
|
||||
// This regex expression replaces all instances of "this.getClass()" or "getClass()"
|
||||
// with the fully qualified class name to retain functionality in static context.
|
||||
return String.format("Stream.of(\n%s\n )", argumentsBuilder)
|
||||
.replaceAll("((?<!\\b\\.)|(\\bthis\\.))(getClass\\(\\))", className + ".class");
|
||||
}
|
||||
|
||||
private static String buildArguments(
|
||||
ExpressionTree expressionTree, List<Comment> comments, VisitorState state) {
|
||||
if (expressionTree.getKind() == NEW_ARRAY) {
|
||||
return buildArgumentsFromArray(((NewArrayTree) expressionTree), comments, state);
|
||||
} else {
|
||||
return buildArgumentsFromExpression(expressionTree, comments, state);
|
||||
}
|
||||
}
|
||||
|
||||
private static String buildArgumentsFromExpression(
|
||||
ExpressionTree expressionTree, List<Comment> comments, VisitorState state) {
|
||||
return String.format(
|
||||
"\t\t%s\n\t\targuments(%s)",
|
||||
comments.stream().map(Comment::getText).collect(joining("\n")),
|
||||
SourceCode.treeToString(expressionTree, state));
|
||||
}
|
||||
|
||||
private static String buildArgumentsFromArray(
|
||||
NewArrayTree argumentArray, List<Comment> comments, VisitorState state) {
|
||||
String argSource = SourceCode.treeToString(argumentArray, state);
|
||||
return String.format(
|
||||
"\t\t%s\n\t\targuments(%s)",
|
||||
comments.stream().map(Comment::getText).collect(joining("\n")),
|
||||
argSource.substring(1, argSource.length() - 1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.auto.common.MoreStreams.toImmutableList;
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.sun.source.tree.Tree.Kind.MEMBER_SELECT;
|
||||
import static com.sun.source.tree.Tree.Kind.NEW_ARRAY;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.AssignmentTree;
|
||||
import com.sun.source.tree.BlockTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import com.sun.source.tree.NewArrayTree;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.function.Executable;
|
||||
import org.testng.annotations.Test;
|
||||
import tech.picnic.errorprone.bugpatterns.util.SourceCode;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} which flags {@link Test#expectedExceptions()} and suggests a JUnit
|
||||
* equivalent replacement.
|
||||
*
|
||||
* <p>The method body is wrapped in a {@link org.junit.jupiter.api.Assertions#assertThrows(Class,
|
||||
* Executable)} statement.
|
||||
*
|
||||
* <p>This {@link BugChecker} does not support migrating more than one exception and will therefore
|
||||
* omit extra {@code expectedExceptions}. As this is not behavior preserving, a note with
|
||||
* explanation is added in a comment.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Migrate TestNG expected exceptions to JUnit",
|
||||
linkType = NONE,
|
||||
tags = REFACTORING,
|
||||
severity = ERROR)
|
||||
public final class TestNGExpectedExceptions extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<AnnotationTree> TESTNG_ANNOTATION =
|
||||
isType("org.testng.annotations.Test");
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
Optional<? extends AnnotationTree> testAnnotation =
|
||||
ASTHelpers.getAnnotations(tree).stream()
|
||||
.filter(annotation -> TESTNG_ANNOTATION.matches(annotation, state))
|
||||
.findFirst();
|
||||
if (testAnnotation.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
Optional<AssignmentTree> assignmentTree =
|
||||
testAnnotation.get().getArguments().stream()
|
||||
.filter(AssignmentTree.class::isInstance)
|
||||
.map(AssignmentTree.class::cast)
|
||||
.filter(
|
||||
assignment ->
|
||||
SourceCode.treeToString(assignment.getVariable(), state)
|
||||
.equals("expectedExceptions"))
|
||||
.findFirst();
|
||||
if (assignmentTree.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ExpressionTree argumentExpression = assignmentTree.orElseThrow().getExpression();
|
||||
if (argumentExpression == null) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
Optional<String> expectedException = getExpectedException(argumentExpression, state);
|
||||
if (expectedException.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
SuggestedFix.Builder fix =
|
||||
SuggestedFix.builder()
|
||||
.replace(
|
||||
tree.getBody(),
|
||||
buildWrappedBody(tree.getBody(), expectedException.orElseThrow(), state))
|
||||
.replace(
|
||||
testAnnotation.get(),
|
||||
buildAnnotationReplacementSource(
|
||||
testAnnotation.get(), assignmentTree.orElseThrow(), state));
|
||||
|
||||
ImmutableList<String> removedExceptions = getRemovedExceptions(argumentExpression, state);
|
||||
if (!removedExceptions.isEmpty()) {
|
||||
fix.prefixWith(
|
||||
testAnnotation.get(),
|
||||
String.format(
|
||||
"// XXX: Removed handling of `%s` because this migration doesn't support it.\n",
|
||||
String.join(", ", removedExceptions)));
|
||||
}
|
||||
|
||||
return describeMatch(testAnnotation.get(), fix.build());
|
||||
}
|
||||
|
||||
private static String buildAnnotationReplacementSource(
|
||||
AnnotationTree annotationTree, AssignmentTree argumentToRemove, VisitorState state) {
|
||||
StringBuilder replacement = new StringBuilder();
|
||||
replacement.append(
|
||||
String.format("@%s", SourceCode.treeToString(annotationTree.getAnnotationType(), state)));
|
||||
String arguments =
|
||||
annotationTree.getArguments().stream()
|
||||
.filter(argument -> !argument.equals(argumentToRemove))
|
||||
.map(argument -> SourceCode.treeToString(argument, state))
|
||||
.collect(joining(", "));
|
||||
|
||||
if (!arguments.isEmpty()) {
|
||||
replacement.append(String.format("(%s)", arguments));
|
||||
}
|
||||
|
||||
return replacement.toString();
|
||||
}
|
||||
|
||||
private static Optional<String> getExpectedException(
|
||||
ExpressionTree expectedExceptions, VisitorState state) {
|
||||
if (expectedExceptions.getKind() == NEW_ARRAY) {
|
||||
NewArrayTree arrayTree = (NewArrayTree) expectedExceptions;
|
||||
if (arrayTree.getInitializers().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(SourceCode.treeToString(arrayTree.getInitializers().get(0), state));
|
||||
} else if (expectedExceptions.getKind() == MEMBER_SELECT) {
|
||||
return Optional.of(SourceCode.treeToString(expectedExceptions, state));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static ImmutableList<String> getRemovedExceptions(
|
||||
ExpressionTree expectedExceptions, VisitorState state) {
|
||||
if (expectedExceptions.getKind() != NEW_ARRAY) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
NewArrayTree arrayTree = (NewArrayTree) expectedExceptions;
|
||||
if (arrayTree.getInitializers().size() <= 1) {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
return arrayTree.getInitializers().subList(1, arrayTree.getInitializers().size()).stream()
|
||||
.map(initializer -> SourceCode.treeToString(initializer, state))
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
|
||||
private static String buildWrappedBody(BlockTree tree, String exception, VisitorState state) {
|
||||
return String.format(
|
||||
"{\norg.junit.jupiter.api.Assertions.assertThrows(%s, () -> %s);\n}",
|
||||
exception, SourceCode.treeToString(tree, state));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugPattern.LinkType.NONE;
|
||||
import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
|
||||
import static com.google.errorprone.BugPattern.StandardTags.REFACTORING;
|
||||
import static com.google.errorprone.matchers.Matchers.allOf;
|
||||
import static com.google.errorprone.matchers.Matchers.hasArgumentWithValue;
|
||||
import static com.google.errorprone.matchers.Matchers.isType;
|
||||
import static com.google.errorprone.matchers.Matchers.stringLiteral;
|
||||
import static com.sun.source.tree.Tree.Kind.STRING_LITERAL;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.errorprone.BugPattern;
|
||||
import com.google.errorprone.VisitorState;
|
||||
import com.google.errorprone.bugpatterns.BugChecker;
|
||||
import com.google.errorprone.bugpatterns.BugChecker.MethodTreeMatcher;
|
||||
import com.google.errorprone.fixes.SuggestedFix;
|
||||
import com.google.errorprone.fixes.SuggestedFixes;
|
||||
import com.google.errorprone.matchers.AnnotationMatcherUtils;
|
||||
import com.google.errorprone.matchers.Description;
|
||||
import com.google.errorprone.matchers.Matcher;
|
||||
import com.google.errorprone.util.ASTHelpers;
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.ExpressionTree;
|
||||
import com.sun.source.tree.LiteralTree;
|
||||
import com.sun.source.tree.MethodTree;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A {@link BugChecker} that will flag TestNG {@link org.testng.annotations.Test} annotations that
|
||||
* can be migrated to a JUnit {@link org.junit.jupiter.params.ParameterizedTest}. These methods will
|
||||
* only be flagged if a migrated version of the data provider is available, these are migrated using
|
||||
* {@link TestNGDataProvider}.
|
||||
*/
|
||||
@AutoService(BugChecker.class)
|
||||
@BugPattern(
|
||||
summary = "Migrate TestNG parameterized tests to JUnit",
|
||||
linkType = NONE,
|
||||
tags = REFACTORING,
|
||||
severity = ERROR)
|
||||
public final class TestNGParameterized extends BugChecker implements MethodTreeMatcher {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Matcher<AnnotationTree> SUPPRESS_WARNINGS_ANNOTATION =
|
||||
allOf(
|
||||
isType("java.lang.SuppressWarnings"),
|
||||
hasArgumentWithValue("value", stringLiteral("UnusedMethod")));
|
||||
private static final Matcher<AnnotationTree> TESTNG_ANNOTATION =
|
||||
isType("org.testng.annotations.Test");
|
||||
|
||||
@Override
|
||||
public Description matchMethod(MethodTree tree, VisitorState state) {
|
||||
Optional<? extends AnnotationTree> testAnnotation =
|
||||
ASTHelpers.getAnnotations(tree).stream()
|
||||
.filter(annotation -> TESTNG_ANNOTATION.matches(annotation, state))
|
||||
.findFirst();
|
||||
if (testAnnotation.isEmpty() || testAnnotation.get().getArguments().size() != 1) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ExpressionTree argumentExpression =
|
||||
AnnotationMatcherUtils.getArgument(testAnnotation.get(), "dataProvider");
|
||||
if (argumentExpression == null || argumentExpression.getKind() != STRING_LITERAL) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
ClassTree classTree = state.findEnclosing(ClassTree.class);
|
||||
if (classTree == null) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
String providerName = ((LiteralTree) argumentExpression).getValue().toString();
|
||||
Optional<MethodTree> providerMethod = findMethodInClassWithName(classTree, providerName);
|
||||
Optional<MethodTree> migratedMethod =
|
||||
findMethodInClassWithName(classTree, providerName + "Junit");
|
||||
|
||||
if (migratedMethod.isEmpty() || providerMethod.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
Optional<? extends AnnotationTree> suppressWarningsAnnotation =
|
||||
ASTHelpers.getAnnotations(migratedMethod.orElseThrow()).stream()
|
||||
.filter(annotation -> SUPPRESS_WARNINGS_ANNOTATION.matches(annotation, state))
|
||||
.findFirst();
|
||||
|
||||
if (suppressWarningsAnnotation.isEmpty()) {
|
||||
return Description.NO_MATCH;
|
||||
}
|
||||
|
||||
return describeMatch(
|
||||
testAnnotation.get(),
|
||||
SuggestedFix.builder()
|
||||
.addImport("org.junit.jupiter.params.ParameterizedTest")
|
||||
.addImport("org.junit.jupiter.params.provider.MethodSource")
|
||||
.merge(SuggestedFixes.renameMethod(migratedMethod.orElseThrow(), providerName, state))
|
||||
.delete(providerMethod.orElseThrow())
|
||||
.delete(suppressWarningsAnnotation.get())
|
||||
.replace(
|
||||
testAnnotation.get(), "@ParameterizedTest\n@MethodSource(\"" + providerName + "\")")
|
||||
.build());
|
||||
}
|
||||
|
||||
private static Optional<MethodTree> findMethodInClassWithName(ClassTree classTree, String name) {
|
||||
return classTree.getMembers().stream()
|
||||
.filter(MethodTree.class::isInstance)
|
||||
.map(MethodTree.class::cast)
|
||||
.filter(method -> method.getName().contentEquals(name))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class AnnotationAttributeReplacementTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(AnnotationAttributeReplacement.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(AnnotationAttributeReplacement.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test(priority = 10)",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void foo() {}",
|
||||
"",
|
||||
" @Test(description = \"unit\")",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void bar() {}",
|
||||
"",
|
||||
" @Test(dataProvider = \"unit\")",
|
||||
" public void baz() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test(priority = 1, groups = \"unit\", description = \"test\")",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.junit.jupiter.api.MethodOrderer;",
|
||||
"import org.junit.jupiter.api.TestMethodOrder;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@TestMethodOrder(MethodOrderer.OrderAnnotation.class)",
|
||||
"class A {",
|
||||
" @Test",
|
||||
" @org.junit.jupiter.api.Order(1)",
|
||||
" @org.junit.jupiter.api.DisplayName(\"test\")",
|
||||
" @org.junit.jupiter.api.Tag(\"unit\")",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementUpdateArgumentListAfterSingleArgument() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test(priority = 2, invocationTimeOut = 10L)",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.junit.jupiter.api.MethodOrderer;",
|
||||
"import org.junit.jupiter.api.TestMethodOrder;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@TestMethodOrder(MethodOrderer.OrderAnnotation.class)",
|
||||
"class A {",
|
||||
" @Test(invocationTimeOut = 10L)",
|
||||
" @org.junit.jupiter.api.Order(2)",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementSingleThreaded() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test(singleThreaded = true)",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" // XXX: Removed argument `singleThreaded = true`, as this cannot be migrated to JUnit!",
|
||||
" @Test",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TestNGAnnotationTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(TestNGAnnotation.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(TestNGAnnotation.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@Test",
|
||||
"class A {",
|
||||
" @BeforeMethod",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void init() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" public void foo() {",
|
||||
" int number = 10;",
|
||||
" }",
|
||||
"",
|
||||
" @Test(description = \"unit\")",
|
||||
" public void bar() {",
|
||||
" int number = 10;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @BeforeMethod",
|
||||
" public void init() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @org.junit.jupiter.api.BeforeEach",
|
||||
" public void init() {}",
|
||||
"",
|
||||
" @org.junit.jupiter.api.Test",
|
||||
" public void foo() {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TestNGClassLevelTestAnnotationTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(TestNGClassLevelTestAnnotation.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(TestNGClassLevelTestAnnotation.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"// BUG: Diagnostic contains:",
|
||||
"@Test",
|
||||
"class A {",
|
||||
" public void foo() {}",
|
||||
"",
|
||||
" @Test(description = \"unit\")",
|
||||
" public void bar() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"@Test",
|
||||
"class A {",
|
||||
" @BeforeMethod",
|
||||
" public void init() {}",
|
||||
"",
|
||||
" public void foo() {}",
|
||||
"",
|
||||
" @Test(priority = 12)",
|
||||
" public void bar() {}",
|
||||
"",
|
||||
" private void baz() {}",
|
||||
"",
|
||||
" protected void qux() {}",
|
||||
"",
|
||||
" void quux() {}",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.BeforeMethod;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @BeforeMethod",
|
||||
" public void init() {}",
|
||||
"",
|
||||
" @Test",
|
||||
" public void foo() {}",
|
||||
"",
|
||||
" @Test(priority = 12)",
|
||||
" public void bar() {}",
|
||||
"",
|
||||
" private void baz() {}",
|
||||
"",
|
||||
" protected void qux() {}",
|
||||
"",
|
||||
" void quux() {}",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TestNGDataProviderTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(TestNGDataProvider.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(TestNGDataProvider.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @DataProvider",
|
||||
" private Object[][] fooNumbers() {",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", 1},",
|
||||
" {\"2\", 2}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @DataProvider",
|
||||
" private Object[] barNumbers() {",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", 1},",
|
||||
" {\"2\", 2}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @DataProvider",
|
||||
" private Object[] bazNumbers() {",
|
||||
" return new Object[] {1, 2};",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void alreadyMigratedIdentification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[][] quxNumbers() {",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", 1},",
|
||||
" {\"2\", 2}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)",
|
||||
" private static final Stream<Arguments> quxNumbersJunit() {",
|
||||
" return Stream.of(arguments(\"1\", 1), arguments(\"2\", 2));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement1DArray() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[] numbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[] {",
|
||||
" // first",
|
||||
" values[0],",
|
||||
" // second",
|
||||
" values[1]",
|
||||
" };",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[] numbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[] {",
|
||||
" // first",
|
||||
" values[0],",
|
||||
" // second",
|
||||
" values[1]",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)",
|
||||
" private static Stream<Arguments> numbersJunit() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return Stream.of(",
|
||||
" // first",
|
||||
" arguments(values[0]),",
|
||||
" // second",
|
||||
" arguments(values[1]));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement1DArray2DReturnType() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[] numbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[][] {",
|
||||
" {String.valueOf(values[0]), values[0]},",
|
||||
" {String.valueOf(values[1]), values[1]}",
|
||||
" };",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[] numbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[][] {",
|
||||
" {String.valueOf(values[0]), values[0]},",
|
||||
" {String.valueOf(values[1]), values[1]}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)",
|
||||
" private static Stream<Arguments> numbersJunit() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return Stream.of(",
|
||||
" arguments(String.valueOf(values[0]), values[0]),",
|
||||
" arguments(String.valueOf(values[1]), values[1]));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement2DArray() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", /* comment */ values[0]},",
|
||||
" {\"2\", values[1]}",
|
||||
" };",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", /* comment */ values[0]},",
|
||||
" {\"2\", values[1]}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)",
|
||||
" private static Stream<Arguments> numbersJunit() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return Stream.of(arguments(\"1\", /* comment */ values[0]), arguments(\"2\", values[1]));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementBody() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() {",
|
||||
" // create value array",
|
||||
" int[] values = new int[2];",
|
||||
" values[0] = 1;",
|
||||
"",
|
||||
" // floating comment",
|
||||
"",
|
||||
" /* multi line comment */",
|
||||
" values[1] = 2;",
|
||||
" return new Object[][] {",
|
||||
" // first",
|
||||
" {\"1\", /* second */ values[0]},",
|
||||
" // third",
|
||||
" {",
|
||||
" /* fourth */",
|
||||
" \"2\", values[1]",
|
||||
" }",
|
||||
" };",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() {",
|
||||
" // create value array",
|
||||
" int[] values = new int[2];",
|
||||
" values[0] = 1;",
|
||||
"",
|
||||
" // floating comment",
|
||||
"",
|
||||
" /* multi line comment */",
|
||||
" values[1] = 2;",
|
||||
" return new Object[][] {",
|
||||
" // first",
|
||||
" {\"1\", /* second */ values[0]},",
|
||||
" // third",
|
||||
" {",
|
||||
" /* fourth */",
|
||||
" \"2\", values[1]",
|
||||
" }",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)",
|
||||
" private static Stream<Arguments> numbersJunit() {",
|
||||
" // create value array",
|
||||
" int[] values = new int[2];",
|
||||
" values[0] = 1;",
|
||||
"",
|
||||
" // floating comment",
|
||||
"",
|
||||
" /* multi line comment */",
|
||||
" values[1] = 2;",
|
||||
" return Stream.of(",
|
||||
" // first",
|
||||
" arguments(\"1\", /* second */ values[0]),",
|
||||
" // third",
|
||||
" arguments(",
|
||||
" /* fourth */",
|
||||
" \"2\", values[1]));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementThrows() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import java.io.IOException;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() throws InterruptedException, IOException {",
|
||||
" // create value array",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", values[0]},",
|
||||
" {\"2\", values[1]}",
|
||||
" };",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.io.IOException;",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() throws InterruptedException, IOException {",
|
||||
" // create value array",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", values[0]},",
|
||||
" {\"2\", values[1]}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)",
|
||||
" private static Stream<Arguments> numbersJunit() throws InterruptedException, IOException {",
|
||||
" // create value array",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return Stream.of(arguments(\"1\", values[0]), arguments(\"2\", values[1]));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacementGetClass() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() {",
|
||||
" return new Object[][] {",
|
||||
" {getClass().getSimpleName(), 1},",
|
||||
" {this.getClass().getSimpleName(), 2}",
|
||||
" };",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"",
|
||||
"class A {",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] numbers() {",
|
||||
" return new Object[][] {",
|
||||
" {getClass().getSimpleName(), 1},",
|
||||
" {this.getClass().getSimpleName(), 2}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\" /* This is an intermediate state for the JUnit migration. */)",
|
||||
" private static Stream<Arguments> numbersJunit() {",
|
||||
" return Stream.of(arguments(A.class.getSimpleName(), 1), arguments(A.class.getSimpleName(), 2));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TestMode.TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TestNGExpectedExceptionsTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(TestNGExpectedExceptions.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(TestNGExpectedExceptions.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Test(expectedExceptions = RuntimeException.class)",
|
||||
" public void foo() {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" }",
|
||||
"",
|
||||
" @Test(description = \"unit\")",
|
||||
" public void bar() {",
|
||||
" int number = 10;",
|
||||
" }",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test(expectedExceptions = RuntimeException.class)",
|
||||
" public void foo() {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" }",
|
||||
"",
|
||||
" @Test(priority = 10, expectedExceptions = RuntimeException.class)",
|
||||
" public void bar() {",
|
||||
" throw new RuntimeException(\"bar\");",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test",
|
||||
" public void foo() {",
|
||||
" org.junit.jupiter.api.Assertions.assertThrows(",
|
||||
" RuntimeException.class,",
|
||||
" () -> {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" });",
|
||||
" }",
|
||||
"",
|
||||
" @Test(priority = 10)",
|
||||
" public void bar() {",
|
||||
" org.junit.jupiter.api.Assertions.assertThrows(",
|
||||
" RuntimeException.class,",
|
||||
" () -> {",
|
||||
" throw new RuntimeException(\"bar\");",
|
||||
" });",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void arrayReplacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" @Test(expectedExceptions = {RuntimeException.class, ArithmeticException.class})",
|
||||
" public void foo() {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" // XXX: Removed handling of `ArithmeticException.class` because this migration doesn't support it.",
|
||||
" @Test",
|
||||
" public void foo() {",
|
||||
" org.junit.jupiter.api.Assertions.assertThrows(",
|
||||
" RuntimeException.class,",
|
||||
" () -> {",
|
||||
" throw new RuntimeException(\"foo\");",
|
||||
" });",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package tech.picnic.errorprone.bugpatterns;
|
||||
|
||||
import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH;
|
||||
|
||||
import com.google.errorprone.BugCheckerRefactoringTestHelper;
|
||||
import com.google.errorprone.CompilationTestHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class TestNGParameterizedTest {
|
||||
private final CompilationTestHelper compilationTestHelper =
|
||||
CompilationTestHelper.newInstance(TestNGParameterized.class, getClass());
|
||||
private final BugCheckerRefactoringTestHelper refactoringTestHelper =
|
||||
BugCheckerRefactoringTestHelper.newInstance(TestNGParameterized.class, getClass());
|
||||
|
||||
@Test
|
||||
void identification() {
|
||||
compilationTestHelper
|
||||
.addSourceLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
" // BUG: Diagnostic contains:",
|
||||
" @Test(dataProvider = \"fooNumbers\")",
|
||||
" public void foo(String first, int second) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] fooNumbers() {",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", 1},",
|
||||
" {\"2\", 2}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\")",
|
||||
" private static final Stream<Arguments> fooNumbersJunit() {",
|
||||
" return Stream.of(arguments(\"1\", 1), arguments(\"2\", 2));",
|
||||
" }",
|
||||
"",
|
||||
" @Test(dataProvider = \"barNumbers\")",
|
||||
" public void bar(String first, int second) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] barNumbers() {",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", 1},",
|
||||
" {\"2\", 2}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @Test(dataProvider = \"fooNumbers\", description = \"foo\")",
|
||||
" public void secondFoo() {}",
|
||||
"}")
|
||||
.doTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void replacement() {
|
||||
refactoringTestHelper
|
||||
.addInputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
"",
|
||||
" @Test(dataProvider = \"fooNumbers\")",
|
||||
" public void foo(String string, int number) {}",
|
||||
"",
|
||||
" @DataProvider",
|
||||
" private Object[][] fooNumbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return new Object[][] {",
|
||||
" {\"1\", values[0]},",
|
||||
" {\"2\", values[1]}",
|
||||
" };",
|
||||
" }",
|
||||
"",
|
||||
" @SuppressWarnings(\"UnusedMethod\")",
|
||||
" private static Stream<Arguments> fooNumbersJunit() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return Stream.of(arguments(\"1\", values[0]), arguments(\"2\", values[1]));",
|
||||
" }",
|
||||
"}")
|
||||
.addOutputLines(
|
||||
"A.java",
|
||||
"import static org.junit.jupiter.params.provider.Arguments.arguments;",
|
||||
"",
|
||||
"import java.util.stream.Stream;",
|
||||
"import org.junit.jupiter.params.ParameterizedTest;",
|
||||
"import org.junit.jupiter.params.provider.Arguments;",
|
||||
"import org.junit.jupiter.params.provider.MethodSource;",
|
||||
"import org.testng.annotations.DataProvider;",
|
||||
"import org.testng.annotations.Test;",
|
||||
"",
|
||||
"class A {",
|
||||
"",
|
||||
" @ParameterizedTest",
|
||||
" @MethodSource(\"fooNumbers\")",
|
||||
" public void foo(String string, int number) {}",
|
||||
"",
|
||||
" private static Stream<Arguments> fooNumbers() {",
|
||||
" int[] values = new int[] {1, 2};",
|
||||
" return Stream.of(arguments(\"1\", values[0]), arguments(\"2\", values[1]));",
|
||||
" }",
|
||||
"}")
|
||||
.doTest(TEXT_MATCH);
|
||||
}
|
||||
}
|
||||
2
pom.xml
2
pom.xml
@@ -878,6 +878,7 @@
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</arg>
|
||||
<arg>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</arg>
|
||||
<arg>-Xmaxerrs</arg>
|
||||
@@ -1023,6 +1024,7 @@
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED</additionalJOption>
|
||||
<additionalJOption>--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED</additionalJOption>
|
||||
</additionalJOptions>
|
||||
|
||||
Reference in New Issue
Block a user