/* * Copyright 2012 Google Inc. All rights reserved. * * 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 org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; 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<JCImport> baseImportList = ImmutableList.of( stubImport("com.google.common.base.Preconditions.checkNotNull", true, 82, 145), stubImport("com.google.ads.pebl.AdGroupCriterionPredicate.PAUSED", true, 147, 213), stubImport("com.google.common.collect.ImmutableMap", false, 215, 260), stubImport("com.google.common.collect.ImmutableList", false, 262, 308), stubImport("org.joda.time.Interval", false, 310, 339), stubImport("org.joda.time.DateTime", false, 341, 370), stubImport("org.joda.time.DateTimeZone", false, 372, 405), stubImport("com.sun.tools.javac.tree.JCTree", false, 407, 445), stubImport("com.sun.source.tree.ImportTree", false, 447, 484), stubImport("com.sun.tools.javac.tree.JCTree.JCExpression", false, 486, 537), stubImport("com.sun.source.tree.CompilationUnitTree", false, 539, 585), stubImport("java.io.File", false, 587, 606), stubImport("java.util.Iterator", false, 608, 633), stubImport("java.io.IOException", false, 635, 661), stubImport("javax.tools.StandardJavaFileManager", false, 663, 705), stubImport("javax.tools.JavaFileObject", false, 707, 740), stubImport("javax.tools.JavaCompiler", false, 742, 773), stubImport("javax.tools.ToolProvider", false, 775, 806)); /** * 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) { JCExpression result = mock(JCExpression.class); when(result.getEndPosition(any(EndPosTable.class))).thenReturn(endPos); return result; } /** * 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 * @param startPos the start position of the import statement * @param endPos the end position of the import statement * @return a new JCImport stub */ private static JCImport stubImport(String typeName, boolean isStatic, int startPos, int endPos) { JCImport result = mock(JCImport.class); when(result.isStatic()).thenReturn(isStatic); when(result.getStartPosition()).thenReturn(startPos); when(result.getEndPosition(any(EndPosTable.class))).thenReturn(endPos); // craft import string StringBuilder returnSB = new StringBuilder("import "); if (isStatic) { returnSB.append("static "); } returnSB.append(typeName); returnSB.append(";\n"); when(result.toString()).thenReturn(returnSB.toString()); return result; } private static ImportStatements createImportStatements( JCExpression basePackage, List<JCImport> 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); assertEquals( "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;", imports.toString()); } /** * 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"); assertTrue(added); assertEquals( "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;", imports.toString()); } /** * 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")); assertTrue(added); assertEquals( "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;", imports.toString()); } /** * 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"); assertTrue(!added); assertEquals( "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;", imports.toString()); } /** * 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"); assertTrue(removed); assertEquals( "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;", imports.toString()); } /** * 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")); assertTrue(removed); assertEquals( "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;", imports.toString()); } /** 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")); assertTrue(removed); assertEquals( "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;", imports.toString()); } /** * 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"); assertTrue(!removed); assertEquals( "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;", imports.toString()); } /** * Test empty initial import list. Positions should match package end * positions. */ @Test public void emptyImportListShouldGivePositionOfPackageStmt() { ImportStatements imports = createImportStatements(basePackage, new ArrayList<JCImport>()); assertEquals(81, imports.getStartPos()); assertEquals(81, imports.getEndPos()); } /** * 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<JCImport>()); imports.add("import org.joda.time.Interval"); assertEquals("\n" + "import org.joda.time.Interval;\n", imports.toString()); } /** * 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); assertEquals(82, imports.getStartPos()); assertEquals(806, imports.getEndPos()); } @Test public void addingToEmptyImportListInDefaultPackage() { ImportStatements imports = createImportStatements(null, new ArrayList<>()); imports.add("import java.util.List"); assertEquals(0, imports.getStartPos()); assertEquals("\nimport java.util.List;\n", imports.toString()); } }