blob: 4daee2640c275f286b162452cc7f94c7f3e47a7a [file] [log] [blame]
/*
* 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.psi.formatter;
import com.intellij.codeInsight.daemon.impl.CodeFoldingPassFactory;
import com.intellij.lang.Language;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ex.PathManagerEx;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.command.impl.UndoManagerImpl;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.impl.EditorImpl;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.testFramework.EditorTestUtil;
import com.intellij.testFramework.LightPlatformTestCase;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.LocalTimeCounter;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"HardCodedStringLiteral"})
public abstract class FormatterTestCase extends LightPlatformTestCase {
protected boolean doReformatRangeTest;
protected TextRange myTextRange;
protected EditorImpl myEditor;
protected PsiFile myFile;
enum CheckPolicy {
PSI(true, false),DOCUMENT(false, true),BOTH(true, true);
private final boolean myCheckPsi;
private final boolean myCheckDocument;
private CheckPolicy(boolean checkPsi, boolean checkDocument) {
myCheckDocument = checkDocument;
myCheckPsi = checkPsi;
}
public boolean isCheckPsi() {
return myCheckPsi;
}
public boolean isCheckDocument() {
return myCheckDocument;
}
}
@Override
protected void setUp() throws Exception {
super.setUp();
assertFalse(CodeStyleSettingsManager.getInstance(getProject()).USE_PER_PROJECT_SETTINGS);
assertNull(CodeStyleSettingsManager.getInstance(getProject()).PER_PROJECT_SETTINGS);
}
protected void doTest(String resultNumber) throws Exception {
doTestForResult(getTestName(true), resultNumber);
}
protected void doTest() throws Exception {
doTest(null);
}
private void doTestForResult(String testName, String resultNumber) throws Exception {
doTest(testName + "." + getFileExtension(), testName + "_after." + getFileExtension(), resultNumber);
}
protected void doTest(String fileNameBefore, String fileNameAfter, String resultNumber) throws Exception {
doTextTest(loadFile(fileNameBefore, null), loadFile(fileNameAfter, resultNumber));
}
protected final void doTest(@NonNls String fileNameBefore, @NonNls String fileNameAfter) throws Exception {
doTextTest(loadFile(fileNameBefore + "." + getFileExtension(), null), loadFile(fileNameAfter + "." + getFileExtension(), null));
}
protected void doTextTest(@NonNls final String text, @NonNls final String textAfter) throws IncorrectOperationException {
doTextTest(text, textAfter, CheckPolicy.BOTH);
}
protected void doTextTest(final String text, final String textAfter, @NotNull CheckPolicy checkPolicy)
throws IncorrectOperationException {
final String fileName = "before." + getFileExtension();
final PsiFile file = createFileFromText(text, fileName, PsiFileFactory.getInstance(getProject()));
if (checkPolicy.isCheckDocument()) {
checkDocument(file, text, textAfter);
}
if (checkPolicy.isCheckPsi()) {
/*
restoreFileContent(file, text);
checkPsi(file, textAfter);
*/
}
}
protected PsiFile createFileFromText(String text, String fileName, final PsiFileFactory fileFactory) {
return fileFactory.createFileFromText(fileName, getFileType(fileName), text, LocalTimeCounter.currentTime(), true, false);
}
protected FileType getFileType(String fileName) {
return FileTypeManager.getInstance().getFileTypeByFileName(fileName);
}
@Override
protected void tearDown() throws Exception {
if (myFile != null) {
((UndoManagerImpl)UndoManager.getInstance(getProject())).clearUndoRedoQueueInTests(myFile.getVirtualFile());
FileEditorManager.getInstance(getProject()).closeFile(myFile.getVirtualFile());
}
myEditor = null;
myFile = null;
super.tearDown();
}
@SuppressWarnings({"UNUSED_SYMBOL"})
private void restoreFileContent(final PsiFile file, final String text) {
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
document.replaceString(0, document.getTextLength(), text);
PsiDocumentManager.getInstance(getProject()).commitDocument(document);
}
});
}
}, "test", null);
}
protected boolean doCheckDocumentUpdate() {
return false;
}
private void checkDocument(final PsiFile file, final String text, String textAfter) {
final Document document = PsiDocumentManager.getInstance(getProject()).getDocument(file);
final EditorImpl editor;
if (doCheckDocumentUpdate()) {
editor =(EditorImpl)FileEditorManager.getInstance(getProject()).openTextEditor(new OpenFileDescriptor(getProject(), file.getVirtualFile(), 0), false);
editor.putUserData(EditorImpl.DO_DOCUMENT_UPDATE_TEST, Boolean.TRUE);
if (myFile != null) {
FileEditorManager.getInstance(getProject()).closeFile(myFile.getVirtualFile());
}
myEditor = editor;
myFile = file;
}
else {
editor = null;
}
WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() {
@Override
public void run() {
document.replaceString(0, document.getTextLength(), text);
PsiDocumentManager.getInstance(getProject()).commitDocument(document);
assertEquals(file.getText(), document.getText());
if (false && doCheckDocumentUpdate()) {
EditorTestUtil.runTextEditorHighlightingPass(editor, CodeFoldingPassFactory.class);
}
try {
if (doReformatRangeTest) {
CodeStyleManager.getInstance(getProject())
.reformatRange(file, file.getTextRange().getStartOffset(), file.getTextRange().getEndOffset());
}
else if (myTextRange != null) {
CodeStyleManager.getInstance(getProject()).reformatText(file, myTextRange.getStartOffset(), myTextRange.getEndOffset());
}
else {
CodeStyleManager.getInstance(getProject())
.reformatText(file, file.getTextRange().getStartOffset(), file.getTextRange().getEndOffset());
}
}
catch (IncorrectOperationException e) {
fail();
}
}
});
assertEquals(textAfter, document.getText());
PsiDocumentManager.getInstance(getProject()).commitDocument(document);
assertEquals(textAfter, file.getText());
}
@SuppressWarnings({"UNUSED_SYMBOL"})
private void checkPsi(final PsiFile file, String textAfter) {
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
performFormatting(file);
}
});
}
}, "", "");
String fileText = file.getText();
assertEquals(textAfter, fileText);
}
protected void performFormatting(final PsiFile file) {
try {
if (myTextRange == null) {
CodeStyleManager.getInstance(getProject()).reformat(file);
}
else {
CodeStyleManager.getInstance(getProject()).reformatRange(file, myTextRange.getStartOffset(), myTextRange.getEndOffset());
}
}
catch (IncorrectOperationException e) {
fail();
}
}
protected void performFormattingWithDocument(final PsiFile file) {
try {
if (myTextRange == null) {
CodeStyleManager.getInstance(getProject()).reformatText(file, 0, file.getTextLength());
}
else {
CodeStyleManager.getInstance(getProject()).reformatText(file, myTextRange.getStartOffset(), myTextRange.getEndOffset());
}
}
catch (IncorrectOperationException e) {
fail();
}
}
protected String loadFile(String name, String resultNumber) throws Exception {
String fullName = getTestDataPath() + File.separatorChar + getBasePath() + File.separatorChar + name;
String text = FileUtil.loadFile(new File(fullName));
text = StringUtil.convertLineSeparators(text);
if (resultNumber == null) {
return prepareText(text);
}
else {
String beginLine = "<<<" + resultNumber + ">>>";
String endLine = "<<</" + resultNumber + ">>>";
int beginPos = text.indexOf(beginLine);
assertTrue(beginPos >= 0);
int endPos = text.indexOf(endLine);
assertTrue(endPos >= 0);
return prepareText(text.substring(beginPos + beginLine.length(), endPos).trim());
}
}
protected String getTestDataPath() {
return PathManagerEx.getTestDataPath();
}
protected String prepareText(final String text) {
return text;
}
protected abstract String getBasePath();
protected abstract String getFileExtension();
protected void defaultSettings() {
CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
settings.ALIGN_MULTILINE_PARAMETERS = true;
settings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = false;
settings.ALIGN_MULTILINE_FOR = true;
settings.ALIGN_MULTILINE_BINARY_OPERATION = false;
settings.ALIGN_MULTILINE_TERNARY_OPERATION = false;
settings.ALIGN_MULTILINE_THROWS_LIST = false;
settings.ALIGN_MULTILINE_EXTENDS_LIST = false;
settings.ALIGN_MULTILINE_PARENTHESIZED_EXPRESSION = false;
settings.DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS = false;
getSettings().SPACE_BEFORE_ANOTATION_PARAMETER_LIST = false;
getSettings().SPACE_AROUND_ASSIGNMENT_OPERATORS = true;
getSettings().SPACE_WITHIN_ANNOTATION_PARENTHESES = false;
getSettings().SPACE_AROUND_ASSIGNMENT_OPERATORS = true;
}
/**
* Returns common (spacing, blank lines etc.) settings for the given language.
* @param language The language to search settings for.
* @return Language common settings or root settings if the language doesn't have any common
* settings of its own.
*/
protected static CommonCodeStyleSettings getSettings(Language language) {
return CodeStyleSettingsManager.getSettings(getProject()).getCommonSettings(language);
}
protected CodeStyleSettings getSettings() {
return CodeStyleSettingsManager.getSettings(getProject());
}
protected void doSanityTestForDirectory(File directory, final boolean formatWithPsi) throws IOException, IncorrectOperationException {
final List<File> failedFiles = new ArrayList<File>();
doSanityTestForDirectory(directory, failedFiles, formatWithPsi);
if (!failedFiles.isEmpty()) {
fail("Failed for files: " + composeMessage(failedFiles));
}
}
private void doSanityTestForDirectory(final File directory, final List<File> failedFiles, final boolean formatWithPsi)
throws IOException, IncorrectOperationException {
final File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
doSanityTestForFile(file, failedFiles, formatWithPsi);
doSanityTestForDirectory(file, failedFiles, formatWithPsi);
}
}
}
protected void doSanityTest(final boolean formatWithPsi) throws IOException, IncorrectOperationException {
final File sanityDirectory = new File(getTestDataPath() + File.separatorChar + getBasePath(), "sanity");
final File[] subFiles = sanityDirectory.listFiles();
final List<File> failedFiles = new ArrayList<File>();
if (subFiles != null) {
for (final File subFile : subFiles) {
doSanityTestForFile(subFile, failedFiles, formatWithPsi);
}
if (!failedFiles.isEmpty()) {
fail("Failed for files: " + composeMessage(failedFiles));
}
}
}
private void doSanityTestForFile(final File subFile, final List<File> failedFiles, final boolean formatWithPsi)
throws IOException, IncorrectOperationException {
if (subFile.isFile() && subFile.getName().endsWith(getFileExtension())) {
final byte[] bytes = FileUtil.loadFileBytes(subFile);
final String text = new String(bytes);
final String fileName = "before." + getFileExtension();
final PsiFile file = PsiFileFactory.getInstance(getProject()).createFileFromText(fileName, getFileType(fileName), StringUtil.convertLineSeparators(text), LocalTimeCounter.currentTime(), true);
try {
CommandProcessor.getInstance().executeCommand(getProject(), new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
try {
if (formatWithPsi) {
performFormatting(file);
}
else {
performFormattingWithDocument(file);
}
}
catch (Throwable e) {
//noinspection CallToPrintStackTrace
e.printStackTrace();
failedFiles.add(subFile);
}
//noinspection UseOfSystemOutOrSystemErr
System.out.println(subFile.getPath() + ": finished");
}
});
}
}, "", null);
}
finally {
final VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile != null) {
((UndoManagerImpl)UndoManager.getInstance(getProject())).clearUndoRedoQueueInTests(virtualFile);
((UndoManagerImpl)UndoManager.getGlobalInstance()).clearUndoRedoQueueInTests(virtualFile);
}
}
}
}
private String composeMessage(final List<File> failedFiles) {
final StringBuffer result = new StringBuffer();
for (File file : failedFiles) {
result.append(file.getPath());
result.append("\n");
}
return result.toString();
}
}