diff --git a/check_api/src/test/java/com/google/errorprone/apply/ImportStatementsTest.java b/check_api/src/test/java/com/google/errorprone/apply/ImportStatementsTest.java deleted file mode 100644 index b1a28b9a528..00000000000 --- a/check_api/src/test/java/com/google/errorprone/apply/ImportStatementsTest.java +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright 2012 The Error Prone Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.errorprone.apply; - -import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; - -import com.google.common.collect.ImmutableList; -import com.google.errorprone.annotations.CanIgnoreReturnValue; -import com.sun.source.tree.TreeVisitor; -import com.sun.tools.javac.tree.EndPosTable; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.JCTree.JCExpression; -import com.sun.tools.javac.tree.JCTree.JCImport; -import com.sun.tools.javac.util.Position; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Unit tests for {@link ImportStatements} - * - * @author eaftan@google.com (Eddie Aftandilian) - */ -@RunWith(JUnit4.class) -public class ImportStatementsTest { - private static final EndPosTable FAKE_END_POS_MAP = - new EndPosTable() { - @Override - public int getEndPos(JCTree tree) { - return Position.NOPOS; - } - - @Override - public void storeEnd(JCTree tree, int endpos) {} - - @Override - public int replaceTree(JCTree oldtree, JCTree newtree) { - return Position.NOPOS; - } - }; - - /** A stubbed package JCExpression to use for testing. */ - private final JCExpression basePackage = stubPackage(79); - - /** An unsorted list of JCImport stubs to use for testing. */ - private final List baseImportList = - new StubImportBuilder(82) - .addStaticImport("com.google.common.base.Preconditions.checkNotNull") - .addStaticImport("com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED") - .addImport("com.google.common.collect.ImmutableMap") - .addImport("com.google.common.collect.ImmutableList") - .addImport("org.joda.time.Interval") - .addImport("org.joda.time.DateTime") - .addImport("org.joda.time.DateTimeZone") - .addImport("com.sun.tools.javac.tree.JCTree") - .addImport("com.sun.source.tree.ImportTree") - .addImport("com.sun.tools.javac.tree.JCTree.JCExpression") - .addImport("com.sun.source.tree.CompilationUnitTree") - .addImport("java.io.File") - .addImport("java.util.Iterator") - .addImport("java.io.IOException") - .addImport("javax.tools.StandardJavaFileManager") - .addImport("javax.tools.JavaFileObject") - .addImport("javax.tools.JavaCompiler") - .addImport("javax.tools.ToolProvider") - .build(); - - /** Makes it easy to build a consecutive list of JCImport mocks. */ - private static class StubImportBuilder { - private int startPos; - private final ImmutableList.Builder imports = ImmutableList.builder(); - - StubImportBuilder(int startPos) { - this.startPos = startPos; - } - - /** - * A helper method to create a JCImport stub. - * - * @param typeName the fully-qualified name of the type being imported - * @return a new JCImport stub - */ - @CanIgnoreReturnValue - StubImportBuilder addImport(String typeName) { - return addImport(typeName, /* isStatic= */ false); - } - - /** - * A helper method to create a JCImport stub. - * - * @param typeName the fully-qualified name of the type being imported - * @return a new JCImport stub - */ - @CanIgnoreReturnValue - StubImportBuilder addStaticImport(String typeName) { - return addImport(typeName, /* isStatic= */ true); - } - - /** - * A helper method to create a JCImport stub. - * - * @param typeName the fully-qualified name of the type being imported - * @param isStatic whether the import is static - * @return a new JCImport stub - */ - @CanIgnoreReturnValue - private StubImportBuilder addImport(String typeName, boolean isStatic) { - // craft import string - StringBuilder returnSB = new StringBuilder("import "); - if (isStatic) { - returnSB.append("static "); - } - returnSB.append(typeName); - returnSB.append(";\n"); - String stringRepresentation = returnSB.toString(); - - // Calculate the end position of the input line. - int endPos = startPos + stringRepresentation.length(); - int curStartPos = startPos; - - // TODO(b/67738557): consolidate helpers for creating fake trees - JCImport result = - new JCImport(/* qualid= */ null, /* importStatic= */ isStatic) { - @Override - public int getStartPosition() { - return curStartPos; - } - - @Override - public int getEndPosition(EndPosTable endPosTable) { - return endPos - 2; - } - - @Override - public String toString() { - return stringRepresentation; - } - }; - - imports.add(result); - - // Move the start position of the next import to the end of the previous one. - startPos = endPos; - - return this; - } - - List build() { - return imports.build(); - } - } - - /** - * A helper method to create a stubbed package JCExpression. - * - * @param endPos the end position of the package JCExpression - * @return a new package JCExpression stub - */ - private static JCExpression stubPackage(int endPos) { - // TODO(b/67738557): consolidate helpers for creating fake trees - return new JCExpression() { - @Override - public Tag getTag() { - throw new UnsupportedOperationException(); - } - - @Override - public void accept(Visitor visitor) { - throw new UnsupportedOperationException(); - } - - @Override - public Kind getKind() { - throw new UnsupportedOperationException(); - } - - @Override - public R accept(TreeVisitor treeVisitor, D d) { - throw new UnsupportedOperationException(); - } - - @Override - public int getStartPosition() { - return endPos; - } - - @Override - public int getEndPosition(EndPosTable endPosTable) { - return endPos; - } - }; - } - - private static ImportStatements createImportStatements( - JCExpression basePackage, List importTrees) { - return new ImportStatements( - basePackage, importTrees, FAKE_END_POS_MAP, ImportOrganizer.STATIC_FIRST_ORGANIZER); - } - - /** Test that the import statements are sorted according to the Google Style Guide. */ - @Test - public void shouldSortImports() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - - assertThat(imports.toString()) - .isEqualTo( - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED;\n" - + "import static com.google.common.base.Preconditions.checkNotNull;\n" - + "\n" - + "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;\n" - + "import org.joda.time.Interval;"); - } - - /** Test that adding a new import inserts it in the correct position. */ - @Test - public void shouldAddImportInCorrectPosition() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - boolean added = imports.add("import static org.junit.Assert.assertEquals"); - - assertThat(added).isTrue(); - assertThat(imports.toString()) - .isEqualTo( - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED;\n" - + "import static com.google.common.base.Preconditions.checkNotNull;\n" - + "import static org.junit.Assert.assertEquals;\n" - + "\n" - + "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;\n" - + "import org.joda.time.Interval;"); - } - - /** Test that adding multiple new imports using addAll() inserts them in the correct positions. */ - @Test - public void shouldAddMultipleImportsInCorrectPositions() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - boolean added = - imports.addAll( - Arrays.asList( - "import static org.junit.Assert.assertEquals", - "import javax.servlet.http.HttpServletRequest", - "import com.google.common.flags.FlagSpec")); - - assertThat(added).isTrue(); - assertThat(imports.toString()) - .isEqualTo( - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED;\n" - + "import static com.google.common.base.Preconditions.checkNotNull;\n" - + "import static org.junit.Assert.assertEquals;\n" - + "\n" - + "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.google.common.flags.FlagSpec;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.servlet.http.HttpServletRequest;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;\n" - + "import org.joda.time.Interval;"); - } - - /** Test that adding an already-existing import doesn't change anything. */ - @Test - public void shouldNotAddExistingImport() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - boolean added = imports.add("import com.google.common.collect.ImmutableMap"); - - assertThat(added).isFalse(); - assertThat(imports.toString()) - .isEqualTo( - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED;\n" - + "import static com.google.common.base.Preconditions.checkNotNull;\n" - + "\n" - + "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;\n" - + "import org.joda.time.Interval;"); - } - - /** Test that removing an import works and the resulting output is correctly sorted. */ - @Test - public void shouldRemoveImportAndSort() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - boolean removed = imports.remove("import com.sun.tools.javac.tree.JCTree"); - - assertThat(removed).isTrue(); - assertThat(imports.toString()) - .isEqualTo( - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED;\n" - + "import static com.google.common.base.Preconditions.checkNotNull;\n" - + "\n" - + "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;\n" - + "import org.joda.time.Interval;"); - } - - /** - * Test that removing multiple imports using removeAll() works and the resulting output is - * correctly sorted. - */ - @Test - public void shouldRemoveMultipleImportsAndSort() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - boolean removed = - imports.removeAll( - Arrays.asList( - "import com.sun.tools.javac.tree.JCTree", - "import static com.google.common.base.Preconditions.checkNotNull", - "import org.joda.time.Interval")); - - assertThat(removed).isTrue(); - assertThat(imports.toString()) - .isEqualTo( - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED;\n" - + "\n" - + "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;"); - } - - /** Tests that a list of imports with no static imports is handled correctly. */ - @Test - public void noRemainingStaticImports() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - boolean removed = - imports.removeAll( - Arrays.asList( - "import static com.google.common.base.Preconditions.checkNotNull", - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED")); - - assertThat(removed).isTrue(); - assertThat(imports.toString()) - .isEqualTo( - "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;\n" - + "import org.joda.time.Interval;"); - } - - /** Test that removing a non-existent import doesn't change anything. */ - @Test - public void removingNonExistingImportShouldntChangeImports() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - boolean removed = imports.remove("import org.joda.time.format.ISODateTimeFormat;\n"); - - assertThat(removed).isFalse(); - assertThat(imports.toString()) - .isEqualTo( - "import static com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED;\n" - + "import static com.google.common.base.Preconditions.checkNotNull;\n" - + "\n" - + "import com.google.common.collect.ImmutableList;\n" - + "import com.google.common.collect.ImmutableMap;\n" - + "import com.sun.source.tree.CompilationUnitTree;\n" - + "import com.sun.source.tree.ImportTree;\n" - + "import com.sun.tools.javac.tree.JCTree;\n" - + "import com.sun.tools.javac.tree.JCTree.JCExpression;\n" - + "import java.io.File;\n" - + "import java.io.IOException;\n" - + "import java.util.Iterator;\n" - + "import javax.tools.JavaCompiler;\n" - + "import javax.tools.JavaFileObject;\n" - + "import javax.tools.StandardJavaFileManager;\n" - + "import javax.tools.ToolProvider;\n" - + "import org.joda.time.DateTime;\n" - + "import org.joda.time.DateTimeZone;\n" - + "import org.joda.time.Interval;"); - } - - /** Test empty initial import list. Positions should match package end positions. */ - @Test - public void emptyImportListShouldGivePositionOfPackageStmt() { - ImportStatements imports = createImportStatements(basePackage, new ArrayList()); - assertThat(imports.getStartPos()).isEqualTo(81); - assertThat(imports.getEndPos()).isEqualTo(81); - } - - /** - * Test empty initial import list. The output string should start and end with newlines because it - * is intended to be inserted after the package statement. - */ - @Test - public void addingToEmptyImportListOutputShouldStartAndEndWithNewlines() { - ImportStatements imports = createImportStatements(basePackage, new ArrayList()); - imports.add("import org.joda.time.Interval"); - assertThat(imports.toString()).isEqualTo("\n" + "import org.joda.time.Interval;\n"); - } - - /** - * Test start and end position calculations. The start position should be the start offset of the - * first import statement, and the end position should be the end position of the last import - * statement. - */ - @Test - public void startAndEndPositionsShouldComeFromImportStatements() { - ImportStatements imports = createImportStatements(basePackage, baseImportList); - assertThat(imports.getStartPos()).isEqualTo(82); - assertThat(imports.getEndPos()).isEqualTo(806); - } - - @Test - public void addingToEmptyImportListInDefaultPackage() { - ImportStatements imports = createImportStatements(null, new ArrayList<>()); - imports.add("import java.util.List"); - assertThat(imports.getStartPos()).isEqualTo(0); - assertThat(imports.toString()).isEqualTo("\nimport java.util.List;\n"); - } - - @Test - public void addsAllImports() { - ImportStatements imports = - new ImportStatements( - null, - new ArrayList<>(), - FAKE_END_POS_MAP, - new ImportOrganizer() { - @Override - public OrganizedImports organizeImports(List imports) { - return new OrganizedImports(); - } - }); - - imports.add("import java.util.List"); - IllegalStateException exception = assertThrows(IllegalStateException.class, imports::toString); - assertThat(exception) - .hasMessageThat() - .isEqualTo("Expected 1 import(s) in the organized imports but it contained 0"); - } -} diff --git a/core/src/test/java/com/google/errorprone/apply/ImportStatementsTest.java b/core/src/test/java/com/google/errorprone/apply/ImportStatementsTest.java new file mode 100644 index 00000000000..698fc60d425 --- /dev/null +++ b/core/src/test/java/com/google/errorprone/apply/ImportStatementsTest.java @@ -0,0 +1,387 @@ +/* + * Copyright 2012 The Error Prone Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.errorprone.apply; + +import static com.google.errorprone.BugCheckerRefactoringTestHelper.TestMode.TEXT_MATCH; +import static com.google.errorprone.BugPattern.SeverityLevel.ERROR; +import static com.google.errorprone.matchers.Description.NO_MATCH; +import static com.google.errorprone.util.ASTHelpers.getSymbol; +import static com.google.errorprone.util.MoreAnnotations.getAnnotationValue; +import static java.util.stream.Collectors.joining; + +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableList; +import com.google.errorprone.BugCheckerRefactoringTestHelper; +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.util.MoreAnnotations; +import com.sun.source.tree.ClassTree; +import com.sun.tools.javac.code.Attribute; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.Optional; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Unit tests for {@link ImportStatements} + * + * @author eaftan@google.com (Eddie Aftandilian) + */ +@RunWith(JUnit4.class) +public class ImportStatementsTest { + + @Target(ElementType.TYPE) + public @interface ImportsToAdd { + String[] toAdd() default {}; + + String[] toRemove() default {}; + } + + @BugPattern(summary = "Refactoring to add imports", severity = ERROR) + public static class ImportsToAddChecker extends BugChecker implements ClassTreeMatcher { + @Override + public Description matchClass(ClassTree tree, VisitorState state) { + Optional annotation = + getSymbol(tree).getAnnotationMirrors().stream() + .filter( + a -> + a.type + .tsym + .getQualifiedName() + .contentEquals(ImportsToAdd.class.getCanonicalName())) + .findAny(); + if (annotation.isEmpty()) { + return NO_MATCH; + } + SuggestedFix.Builder fix = SuggestedFix.builder(); + getAnnotationValue(annotation.get(), "toAdd") + .map(MoreAnnotations::asStrings) + .ifPresent( + toAdd -> + toAdd.forEach( + i -> { + if (i.startsWith("static ")) { + fix.addStaticImport(i.substring("static ".length())); + } else { + fix.addImport(i); + } + })); + getAnnotationValue(annotation.get(), "toRemove") + .map(MoreAnnotations::asStrings) + .ifPresent( + toRemove -> + toRemove.forEach( + i -> { + if (i.startsWith("static ")) { + fix.removeStaticImport(i.substring("static ".length())); + } else { + fix.removeImport(i); + } + })); + return describeMatch(tree, fix.build()); + } + } + + private static final ImmutableList BASE_IMPORTS = + ImmutableList.of( + "static com.google.common.base.Preconditions.checkNotNull", + "static java.lang.annotation.ElementType.TYPE_USE", + "com.google.common.collect.ImmutableMap", + "com.google.common.collect.ImmutableList", + "org.joda.time.Interval", + "org.joda.time.DateTime", + "org.joda.time.DateTimeZone", + "com.sun.tools.javac.tree.JCTree", + "com.sun.source.tree.ImportTree", + "com.sun.tools.javac.tree.JCTree.JCExpression", + "com.sun.source.tree.CompilationUnitTree", + "java.io.File", + "java.util.Iterator", + "java.io.IOException", + "javax.tools.StandardJavaFileManager", + "javax.tools.JavaFileObject", + "javax.tools.JavaCompiler", + "javax.tools.ToolProvider"); + + private final BugCheckerRefactoringTestHelper testHelper = + BugCheckerRefactoringTestHelper.newInstance(ImportsToAddChecker.class, getClass()); + + private void testImports( + Optional pkg, + ImmutableList baseImports, + ImmutableList toAdd, + ImmutableList toRemove, + String... expected) { + String prefix = pkg.map(p -> String.format("package %s;\n", p)).orElse(""); + String suffix = + Joiner.on('\n') + .join( + "@com.google.errorprone.apply.ImportStatementsTest.ImportsToAdd(", + " toAdd = {" + + toAdd.stream().map(x -> "\"" + x + "\"").collect(joining(", ")) + + "},", + " toRemove = {" + + toRemove.stream().map(x -> "\"" + x + "\"").collect(joining(", ")) + + "})", + "class Test {}"); + testHelper + .addInputLines( + "Test.java", + prefix, + baseImports.stream().map(i -> String.format("import %s;", i)).collect(joining("\n")), + "", + suffix) + .addOutputLines("Test.java", prefix, Joiner.on('\n').join(expected), suffix) + .addModules("jdk.compiler/com.sun.tools.javac.tree") + .doTest(TEXT_MATCH); + } + + /** Test that adding a new import inserts it in the correct position. */ + @Test + public void shouldAddImportInCorrectPosition() { + testImports( + /* pkg= */ Optional.empty(), + /* baseImports= */ BASE_IMPORTS, + /* toAdd= */ ImmutableList.of("static org.junit.Assert.assertEquals"), + /* toRemove= */ ImmutableList.of(), + "import static com.google.common.base.Preconditions.checkNotNull;", + "import static java.lang.annotation.ElementType.TYPE_USE;", + "import static org.junit.Assert.assertEquals;", + "", + "import com.google.common.collect.ImmutableList;", + "import com.google.common.collect.ImmutableMap;", + "import com.sun.source.tree.CompilationUnitTree;", + "import com.sun.source.tree.ImportTree;", + "import com.sun.tools.javac.tree.JCTree;", + "import com.sun.tools.javac.tree.JCTree.JCExpression;", + "import java.io.File;", + "import java.io.IOException;", + "import java.util.Iterator;", + "import javax.tools.JavaCompiler;", + "import javax.tools.JavaFileObject;", + "import javax.tools.StandardJavaFileManager;", + "import javax.tools.ToolProvider;", + "import org.joda.time.DateTime;", + "import org.joda.time.DateTimeZone;", + "import org.joda.time.Interval;"); + } + + /** Test that adding multiple new imports using addAll() inserts them in the correct positions. */ + @Test + public void shouldAddMultipleImportsInCorrectPositions() { + testImports( + /* pkg= */ Optional.of("p"), + /* baseImports= */ BASE_IMPORTS, + /* toAdd= */ ImmutableList.of( + "static org.junit.Assert.assertEquals", + "javax.security.auth.spi.LoginModule", + "com.google.common.graph.Graph"), + /* toRemove= */ ImmutableList.of(), + "import static com.google.common.base.Preconditions.checkNotNull;", + "import static java.lang.annotation.ElementType.TYPE_USE;", + "import static org.junit.Assert.assertEquals;", + "", + "import com.google.common.collect.ImmutableList;", + "import com.google.common.collect.ImmutableMap;", + "import com.google.common.graph.Graph;", + "import com.sun.source.tree.CompilationUnitTree;", + "import com.sun.source.tree.ImportTree;", + "import com.sun.tools.javac.tree.JCTree;", + "import com.sun.tools.javac.tree.JCTree.JCExpression;", + "import java.io.File;", + "import java.io.IOException;", + "import java.util.Iterator;", + "import javax.security.auth.spi.LoginModule;", + "import javax.tools.JavaCompiler;", + "import javax.tools.JavaFileObject;", + "import javax.tools.StandardJavaFileManager;", + "import javax.tools.ToolProvider;", + "import org.joda.time.DateTime;", + "import org.joda.time.DateTimeZone;", + "import org.joda.time.Interval;"); + } + + /** Test that adding an already-existing import doesn't change anything. */ + @Test + public void shouldNotAddExistingImport() { + testImports( + /* pkg= */ Optional.empty(), + /* baseImports= */ BASE_IMPORTS, + /* toAdd= */ ImmutableList.of("com.google.common.collect.ImmutableMap"), + /* toRemove= */ ImmutableList.of(), + "import static com.google.common.base.Preconditions.checkNotNull;", + "import static java.lang.annotation.ElementType.TYPE_USE;", + "import com.google.common.collect.ImmutableMap;", + "import com.google.common.collect.ImmutableList;", + "import org.joda.time.Interval;", + "import org.joda.time.DateTime;", + "import org.joda.time.DateTimeZone;", + "import com.sun.tools.javac.tree.JCTree;", + "import com.sun.source.tree.ImportTree;", + "import com.sun.tools.javac.tree.JCTree.JCExpression;", + "import com.sun.source.tree.CompilationUnitTree;", + "import java.io.File;", + "import java.util.Iterator;", + "import java.io.IOException;", + "import javax.tools.StandardJavaFileManager;", + "import javax.tools.JavaFileObject;", + "import javax.tools.JavaCompiler;", + "import javax.tools.ToolProvider;"); + } + + /** Test that removing an import works and the resulting output is correctly sorted. */ + @Test + public void shouldRemoveImportAndSort() { + testImports( + /* pkg= */ Optional.empty(), + /* baseImports= */ BASE_IMPORTS, + /* toAdd= */ ImmutableList.of(), + /* toRemove= */ ImmutableList.of("com.sun.tools.javac.tree.JCTree"), + "import static com.google.common.base.Preconditions.checkNotNull;", + "import static java.lang.annotation.ElementType.TYPE_USE;", + "", + "import com.google.common.collect.ImmutableList;", + "import com.google.common.collect.ImmutableMap;", + "import com.sun.source.tree.CompilationUnitTree;", + "import com.sun.source.tree.ImportTree;", + "import com.sun.tools.javac.tree.JCTree.JCExpression;", + "import java.io.File;", + "import java.io.IOException;", + "import java.util.Iterator;", + "import javax.tools.JavaCompiler;", + "import javax.tools.JavaFileObject;", + "import javax.tools.StandardJavaFileManager;", + "import javax.tools.ToolProvider;", + "import org.joda.time.DateTime;", + "import org.joda.time.DateTimeZone;", + "import org.joda.time.Interval;"); + } + + /** + * Test that removing multiple imports using removeAll() works and the resulting output is + * correctly sorted. + */ + @Test + public void shouldRemoveMultipleImportsAndSort() { + testImports( + /* pkg= */ Optional.empty(), + /* baseImports= */ BASE_IMPORTS, + /* toAdd= */ ImmutableList.of(), + /* toRemove= */ ImmutableList.of( + "com.sun.tools.javac.tree.JCTree", + "static com.google.common.base.Preconditions.checkNotNull", + "org.joda.time.Interval"), + "import static java.lang.annotation.ElementType.TYPE_USE;", + "", + "import com.google.common.collect.ImmutableList;", + "import com.google.common.collect.ImmutableMap;", + "import com.sun.source.tree.CompilationUnitTree;", + "import com.sun.source.tree.ImportTree;", + "import com.sun.tools.javac.tree.JCTree.JCExpression;", + "import java.io.File;", + "import java.io.IOException;", + "import java.util.Iterator;", + "import javax.tools.JavaCompiler;", + "import javax.tools.JavaFileObject;", + "import javax.tools.StandardJavaFileManager;", + "import javax.tools.ToolProvider;", + "import org.joda.time.DateTime;", + "import org.joda.time.DateTimeZone;"); + } + + /** Tests that a list of imports with no static imports is handled correctly. */ + @Test + public void noRemainingStaticImports() { + testImports( + /* pkg= */ Optional.empty(), + /* baseImports= */ BASE_IMPORTS, + /* toAdd= */ ImmutableList.of(), + /* toRemove= */ ImmutableList.of( + "static com.google.common.base.Preconditions.checkNotNull", + "static java.lang.annotation.ElementType.TYPE_USE"), + "import com.google.common.collect.ImmutableList;", + "import com.google.common.collect.ImmutableMap;", + "import com.sun.source.tree.CompilationUnitTree;", + "import com.sun.source.tree.ImportTree;", + "import com.sun.tools.javac.tree.JCTree;", + "import com.sun.tools.javac.tree.JCTree.JCExpression;", + "import java.io.File;", + "import java.io.IOException;", + "import java.util.Iterator;", + "import javax.tools.JavaCompiler;", + "import javax.tools.JavaFileObject;", + "import javax.tools.StandardJavaFileManager;", + "import javax.tools.ToolProvider;", + "import org.joda.time.DateTime;", + "import org.joda.time.DateTimeZone;", + "import org.joda.time.Interval;"); + } + + /** Test that removing a non-existent import doesn't change anything. */ + @Test + public void removingNonExistingImportShouldntChangeImports() { + testImports( + /* pkg= */ Optional.empty(), + /* baseImports= */ BASE_IMPORTS, + /* toAdd= */ ImmutableList.of(), + /* toRemove= */ ImmutableList.of("org.joda.time.format.ISODateTimeFormat"), + "import static com.google.common.base.Preconditions.checkNotNull;", + "import static java.lang.annotation.ElementType.TYPE_USE;", + "import com.google.common.collect.ImmutableMap;", + "import com.google.common.collect.ImmutableList;", + "import org.joda.time.Interval;", + "import org.joda.time.DateTime;", + "import org.joda.time.DateTimeZone;", + "import com.sun.tools.javac.tree.JCTree;", + "import com.sun.source.tree.ImportTree;", + "import com.sun.tools.javac.tree.JCTree.JCExpression;", + "import com.sun.source.tree.CompilationUnitTree;", + "import java.io.File;", + "import java.util.Iterator;", + "import java.io.IOException;", + "import javax.tools.StandardJavaFileManager;", + "import javax.tools.JavaFileObject;", + "import javax.tools.JavaCompiler;", + "import javax.tools.ToolProvider;"); + } + + @Test + public void addingToEmptyImportList() { + testImports( + /* pkg= */ Optional.of("p"), + /* baseImports= */ ImmutableList.of(), + /* toAdd= */ ImmutableList.of("org.joda.time.Interval"), + /* toRemove= */ ImmutableList.of(), + "import org.joda.time.Interval;"); + } + + @Test + public void addingToEmptyImportListInDefaultPackage() { + testImports( + /* pkg= */ Optional.empty(), + /* baseImports= */ ImmutableList.of(), + /* toAdd= */ ImmutableList.of("org.joda.time.Interval"), + /* toRemove= */ ImmutableList.of(), + "import org.joda.time.Interval;"); + } +}