/* * Copyright 2000-2014 JetBrains s.r.o. * * 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.intellij.codeInsight.actions; import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.components.impl.ComponentManagerImpl; import com.intellij.openapi.fileTypes.PlainTextLanguage; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePathImpl; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangeListManager; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.SimpleContentRevision; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.testFramework.LightPlatformTestCase; import com.intellij.testFramework.PlatformTestCase; import com.intellij.testFramework.vcs.MockChangeListManager; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.picocontainer.MutablePicoContainer; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; import java.util.List; public class ReformatOnlyVcsChangedTextTest extends LightPlatformTestCase { private static final String TEMP_DIR_NAME = "dir"; private PsiDirectory myWorkingDirectory; private MockChangeListManager myMockChangeListManager; private MockCodeStyleManager myMockCodeStyleManager; private MockPlainTextFormattingModelBuilder myMockPlainTextFormattingModelBuilder; private ChangeListManager myRealChangeListManager; private CodeStyleManager myRealCodeStyleManger; private final static String COMMITTED = "class Test {\n" + " int a = 22;\n" + " public String getName() { return \"Test\"; }\n" + "}"; private final static String MODIFIED = "class Test {\n" + " int a = 22;\n" + " long l;\n" + " double d;\n" + " int i;\n" + " public String getName() { return \"Test\"; }\n" + " String test1;\n" + " String test2;\n" + "}"; private final static ChangedLines[] CHANGED_LINES = new ChangedLines[] { line(2, 4), line(6, 7) }; private final static ChangedLines[] NO_CHANGED_LINES = new ChangedLines[0]; @Override public void setUp() throws Exception { PlatformTestCase.initPlatformLangPrefix(); super.setUp(); myWorkingDirectory = TestFileStructure.createDirectory(getProject(), getSourceRoot(), TEMP_DIR_NAME); myRealChangeListManager = ChangeListManager.getInstance(getProject()); myMockChangeListManager = new MockChangeListManager(); registerChangeListManager(myMockChangeListManager); myRealCodeStyleManger = CodeStyleManager.getInstance(getProject()); myMockCodeStyleManager = new MockCodeStyleManager(); registerCodeStyleManager(myMockCodeStyleManager); myMockPlainTextFormattingModelBuilder = new MockPlainTextFormattingModelBuilder(); LanguageFormatting.INSTANCE.addExplicitExtension(PlainTextLanguage.INSTANCE, myMockPlainTextFormattingModelBuilder); } @Override public void tearDown() throws Exception { registerChangeListManager(myRealChangeListManager); registerCodeStyleManager(myRealCodeStyleManger); LanguageFormatting.INSTANCE.removeExplicitExtension(PlainTextLanguage.INSTANCE, myMockPlainTextFormattingModelBuilder); TestFileStructure.delete(myWorkingDirectory.getVirtualFile()); super.tearDown(); } public void testInsertion() throws IOException { doTest( "public class B {\n" + " int a = 3;\n" + " String text;\n" + " Object last = null;\n" + "}", "public class B {\n" + " int a = 3;\n" + " int toIndent1 = 1;\n" + " String text;\n" + " int toIndent2\n" + " Object last = null;\n" + "}", line(2, 2), line(4, 4) ); } private static ChangedLines line(int from, int to) { return new ChangedLines(from, to); } public void testDeletion() throws IOException { doTest( "public class B {\n" + " int a = 3;\n" + " String text;\n" + " Object last = null;\n" + " Object first = null;\n" + " Object second = null;\n" + "}", "public class B {\n" + " int newInt = 1;\n" + " Object last = null;\n" + "}", line(1, 1) ); } public void testModification() throws IOException { doTest( "public class B {\n" + " int a = 3;\n" + " String text;\n" + " Object last = null;\n" + " Object first = null;\n" + " Object second = null;\n" + "}", "public class B {\n" + " int a = 33;\n" + " String text;\n" + " Object last = new Object();\n" + " Object first = null;\n" + " Object second = new Object();\n" + "}", line(1, 1), line(3,3), line(5,5) ); } public void testReformatFiles() throws IOException { ChangedFilesStructure fs = new ChangedFilesStructure(myWorkingDirectory); PsiFile m1 = fs.createFile("Test1.java", COMMITTED, MODIFIED); PsiFile u1 = fs.createFile("Test2.java", COMMITTED, COMMITTED); fs.createDirectoryAndMakeItCurrent(); PsiFile m2 = fs.createFile("Test3.java", COMMITTED, MODIFIED); PsiFile u2 = fs.createFile("Test4.java", COMMITTED, COMMITTED); new ReformatCodeProcessor(getProject(), new PsiFile[] {m1, m2, u1, u2}, null, true).run(); assertFormattedLines(CHANGED_LINES, m1, m2); assertFormattedLines(NO_CHANGED_LINES, u1, u2); } public void testNoChangesNoFormatting() throws IOException { ChangedFilesStructure fs = new ChangedFilesStructure(myWorkingDirectory); PsiFile u1 = fs.createFile("Test1.java", COMMITTED, COMMITTED); PsiFile u2 = fs.createFile("Test2.java", COMMITTED, COMMITTED); reformatDirectory(fs.getCurrentDirectory()); assertFormattedLines(NO_CHANGED_LINES, u1, u2); } public void testReformatOnlyChanged() throws IOException { ChangedFilesStructure fs = new ChangedFilesStructure(myWorkingDirectory); PsiFile untouched1 = fs.createFile("Test1.java", COMMITTED, COMMITTED); PsiFile untouched2 = fs.createFile("Test2.java", COMMITTED, COMMITTED); PsiFile modified1 = fs.createFile("Test4.java", COMMITTED, MODIFIED); PsiFile modified2 = fs.createFile("Test5.java", COMMITTED, MODIFIED); PsiFile modified3 = fs.createFile("Test6.java", COMMITTED, MODIFIED); reformatDirectory(fs.getCurrentDirectory()); assertFormattedLines(CHANGED_LINES, modified1, modified2, modified3); assertFormattedLines(NO_CHANGED_LINES, untouched1, untouched2); } public void testReformatInAllSubtree() throws IOException { ChangedFilesStructure fs = new ChangedFilesStructure(myWorkingDirectory); PsiFile modified11 = fs.createFile("Test4.java", COMMITTED, MODIFIED); PsiFile modified12 = fs.createFile("Test5.java", COMMITTED, MODIFIED); fs.createDirectoryAndMakeItCurrent(); PsiDirectory dirToReformat = fs.getCurrentDirectory(); PsiFile modified21 = fs.createFile("Test6.java", COMMITTED, MODIFIED); PsiFile modified22 = fs.createFile("Test7.java", COMMITTED, MODIFIED); fs.createDirectoryAndMakeItCurrent(); PsiFile modified31 = fs.createFile("Test8.java", COMMITTED, MODIFIED); PsiFile modified32 = fs.createFile("Test9.java", COMMITTED, MODIFIED); reformatDirectory(dirToReformat); assertFormattedLines(CHANGED_LINES, modified21, modified22, modified31, modified32); assertFormattedLines(NO_CHANGED_LINES, modified11, modified12); } private static void registerChangeListManager(@NotNull ChangeListManager manager) { Project project = getProject(); assert (project instanceof ComponentManagerImpl); ComponentManagerImpl projectComponentManager = (ComponentManagerImpl)project; projectComponentManager.registerComponentInstance(ChangeListManager.class, manager); } private static void registerCodeStyleManager(@NotNull CodeStyleManager manager) { String componentKey = CodeStyleManager.class.getName(); MutablePicoContainer container = (MutablePicoContainer)getProject().getPicoContainer(); container.unregisterComponent(componentKey); container.registerComponentInstance(componentKey, manager); } private void doTest(@NotNull String committed, @NotNull String modified, @NotNull ChangedLines... lines) throws IOException { ChangedFilesStructure fs = new ChangedFilesStructure(myWorkingDirectory); PsiFile file = fs.createFile("Test.java", committed, modified); reformatDirectory(myWorkingDirectory); assertFormattedRangesEqualsTo(file, lines); } private void assertFormattedLines(@NotNull ChangedLines[] expectedLines, @NotNull PsiFile... files) { for (PsiFile file : files) assertFormattedRangesEqualsTo(file, expectedLines); } private void assertFormattedRangesEqualsTo(@NotNull PsiFile file, ChangedLines... expected) { ChangedLines[] formatted = myMockCodeStyleManager.getFormattedLinesFor(file); Comparator cmp = new Comparator() { @Override public int compare(ChangedLines o1, ChangedLines o2) { return o1.from < o2.from ? -1 : 1; } }; Arrays.sort(expected, cmp); Arrays.sort(formatted, cmp); assertTrue(getErrorMessage(expected, formatted), Arrays.equals(expected, formatted)); } @NotNull private static String getErrorMessage(ChangedLines[] expected, ChangedLines[] actual) { return "Expected: " + Arrays.toString(expected) + " Actual: " + Arrays.toString(actual); } private static void reformatDirectory(@NotNull PsiDirectory dir) { ReformatCodeProcessor processor = new ReformatCodeProcessor(getProject(), dir, true, true); processor.run(); } class ChangedFilesStructure { private TestFileStructure myFileStructure; public ChangedFilesStructure(@NotNull PsiDirectory directory) { myFileStructure = new TestFileStructure(getModule(), directory); } public void createDirectoryAndMakeItCurrent() throws IOException { myFileStructure.createDirectoryAndMakeItCurrent("inner"); } @NotNull public PsiFile createFile(@NotNull String fileName, @NotNull String committedContent, @NotNull String actualContent) throws IOException { PsiFile file = myFileStructure.addTestFile(fileName, actualContent); if (committedContent != actualContent) { registerCommittedRevision(committedContent, file); } return file; } private void registerCommittedRevision(@NotNull String committedContent, @NotNull PsiFile... files) { List changes = createChanges(committedContent, files); injectChanges(changes); } @NotNull private List createChanges(@NotNull String committed, @NotNull PsiFile... files) { List changes = ContainerUtil.newArrayList(); for (PsiFile file : files) { changes.add(createChange(committed, file)); } return changes; } private void injectChanges(@NotNull List changes) { Change[] arr = new Change[changes.size()]; ContainerUtil.toArray(changes, arr); myMockChangeListManager.addChanges(arr); } @NotNull private Change createChange(@NotNull String committed, @NotNull PsiFile file) { FilePathImpl filePath = new FilePathImpl(file.getVirtualFile()); ContentRevision before = new SimpleContentRevision(committed, filePath, ""); ContentRevision after = new SimpleContentRevision("", filePath, ""); return new Change(before, after); } @NotNull public PsiDirectory getCurrentDirectory() { return myFileStructure.getCurrentDirectory(); } } }