blob: f7c1cc35dd737be12d707d7501cf0520b7acae3a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2014 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sergey Prigogin (Google) - initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.ui.tests.refactoring;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URI;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringContext;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptorProxy;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
import org.eclipse.ltk.core.refactoring.history.RefactoringHistory;
import org.eclipse.ltk.internal.core.refactoring.history.RefactoringHistoryService;
import org.osgi.framework.Bundle;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
import org.eclipse.cdt.ui.testplugin.CTestPlugin;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoring;
import org.eclipse.cdt.internal.ui.refactoring.CRefactoringContext;
/**
* Common base for refactoring tests.
*/
public abstract class RefactoringTestBase extends BaseTestCase {
private static final Pattern FILENAME_PATTERN = Pattern.compile("((\\w|_|-)+/)*(\\w|_|-)+\\.\\w+");
/** Allows empty files to be created during test setup. */
protected boolean createEmptyFiles = true;
/** See {@link PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER} */
protected boolean ascendingVisibilityOrder;
/** Expected counts of errors, warnings and info messages */
protected int expectedInitialErrors;
protected int expectedInitialWarnings;
protected int expectedFinalWarnings;
protected int expectedFinalInfos;
private boolean cpp = true;
private ICProject cproject;
private final Set<TestSourceFile> testFiles = new LinkedHashSet<>();
private TestSourceFile selectedFile;
private TextSelection selection;
private TestSourceFile historyScript;
protected RefactoringTestBase() {
super();
}
protected RefactoringTestBase(String name) {
super(name);
}
@Override
public void setUp() throws Exception {
super.setUp();
resetPreferences();
cproject = cpp ?
CProjectHelper.createCCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER) :
CProjectHelper.createCProject(getName() + System.currentTimeMillis(), "bin", IPDOMManager.ID_NO_INDEXER);
TestScannerProvider.sLocalIncludes = new String[] { cproject.getProject().getLocation().toOSString() };
Bundle bundle = CTestPlugin.getDefault().getBundle();
CharSequence[] testData = TestSourceReader.getContentsForTest(bundle, "ui", getClass(), getName(), 0);
for (CharSequence contents : testData) {
TestSourceFile testFile = null;
boolean firstAfterDelimiter = false;
boolean expectedResult = false;
BufferedReader reader = new BufferedReader(new StringReader(contents.toString()));
String line;
while ((line = reader.readLine()) != null) {
String trimmedLine = line.trim();
if (testFile == null) {
if (isResultDelimiter(trimmedLine)) {
expectedResult = true;
firstAfterDelimiter = true;
testFile = new TestSourceFile(null);
} else {
assertTrue("Invalid file name \"" + trimmedLine + "\"",
FILENAME_PATTERN.matcher(trimmedLine).matches());
testFile = new TestSourceFile(trimmedLine);
}
} else if (isResultDelimiter(trimmedLine)) {
expectedResult = true;
firstAfterDelimiter = true;
} else if (expectedResult) {
if (firstAfterDelimiter) {
firstAfterDelimiter = false;
if (FILENAME_PATTERN.matcher(trimmedLine).matches()) {
testFile.setExpectedName(trimmedLine);
continue;
}
}
assertTrue(testFile.getExpectedName() != null);
testFile.addLineToExpectedSource(line);
} else {
testFile.addLineToSource(line);
}
}
reader.close();
if (testFile.getName() != null && (createEmptyFiles || !testFile.getSource().isEmpty())) {
TestSourceReader.createFile(cproject.getProject(), new Path(testFile.getName()),
testFile.getSource());
}
testFiles.add(testFile);
if (testFile.getName().endsWith(".xml")) {
historyScript = testFile;
} else if (selection == null) {
selection = testFile.getSelection();
if (selection != null)
selectedFile = testFile;
}
}
if (selectedFile == null && !testFiles.isEmpty()) {
selectedFile = testFiles.iterator().next();
}
CCorePlugin.getIndexManager().setIndexerId(cproject, IPDOMManager.ID_FAST_INDEXER);
waitForIndexer(cproject);
}
@Override
public void tearDown() throws Exception {
if (cproject != null) {
cproject.getProject().delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT,
npm());
}
resetPreferences();
super.tearDown();
}
protected void assertRefactoringSuccess() throws Exception {
executeRefactoring(true);
compareFiles();
}
protected void assertRefactoringFailure() throws Exception {
executeRefactoring(false);
}
protected void executeRefactoring(boolean expectedSuccess) throws Exception {
if (ascendingVisibilityOrder) {
getPreferenceStore().setValue(PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER,
ascendingVisibilityOrder);
}
if (historyScript != null) {
executeHistoryRefactoring(expectedSuccess);
return;
}
Refactoring refactoring = createRefactoring();
RefactoringContext context;
if (refactoring instanceof CRefactoring) {
context = new CRefactoringContext((CRefactoring) refactoring);
} else {
context = new RefactoringContext(refactoring);
}
executeRefactoring(refactoring, context, true, expectedSuccess);
}
protected void executeRefactoring(Refactoring refactoring, boolean expectedSuccess)
throws CoreException, Exception {
executeRefactoring(refactoring, null, false, expectedSuccess);
}
protected void executeRefactoring(Refactoring refactoring, RefactoringContext context,
boolean withUserInput, boolean expectedSuccess) throws CoreException, Exception {
try {
RefactoringStatus initialStatus = refactoring.checkInitialConditions(npm());
if (!expectedSuccess) {
assertStatusFatalError(initialStatus);
return;
}
if (expectedInitialErrors != 0) {
assertStatusError(initialStatus, expectedInitialErrors);
} else if (expectedInitialWarnings != 0) {
assertStatusWarning(initialStatus, expectedInitialWarnings);
} else {
assertStatusOk(initialStatus);
}
if (withUserInput)
simulateUserInput();
RefactoringStatus finalStatus = refactoring.checkFinalConditions(npm());
if (expectedFinalWarnings != 0) {
assertStatusWarning(finalStatus, expectedFinalWarnings);
} else if (expectedFinalInfos != 0) {
assertStatusInfo(finalStatus, expectedFinalInfos);
} else {
assertStatusOk(finalStatus);
}
Change change = refactoring.createChange(npm());
change.perform(npm());
} finally {
if (context != null)
context.dispose();
}
}
private void executeHistoryRefactoring(boolean expectedSuccess) throws Exception {
URI uri= URIUtil.toURI(cproject.getProject().getLocation());
String scriptSource = historyScript.getSource().replaceAll("\\$\\{projectPath\\}", uri.getPath());
RefactoringHistory history = RefactoringHistoryService.getInstance().readRefactoringHistory(
new ByteArrayInputStream(scriptSource.getBytes()), 0);
for (RefactoringDescriptorProxy proxy : history.getDescriptors()) {
RefactoringDescriptor descriptor = proxy.requestDescriptor(npm());
RefactoringStatus status = new RefactoringStatus();
RefactoringContext context = descriptor.createRefactoringContext(status);
assertTrue(status.isOK());
executeRefactoring(context.getRefactoring(), context, false, expectedSuccess);
}
}
/**
* Creates a refactoring object.
*/
protected abstract Refactoring createRefactoring();
/**
* Subclasses can override to simulate user input.
*/
protected void simulateUserInput() {
}
protected ICProject getCProject() {
return cproject;
}
protected IProject getProject() {
return cproject.getProject();
}
protected TestSourceFile getSelectedTestFile() {
return selectedFile;
}
protected IFile getSelectedFile() {
if (selectedFile == null)
return null;
return cproject.getProject().getFile(new Path(selectedFile.getName()));
}
protected ITranslationUnit getSelectedTranslationUnit() {
IFile file = getSelectedFile();
if (file == null)
return null;
return (ITranslationUnit) CoreModel.getDefault().create(file);
}
protected TestSourceFile getHistoryScriptFile() {
return historyScript;
}
protected TextSelection getSelection() {
return selection;
}
protected boolean isCpp() {
return cpp;
}
protected void setCpp(boolean cpp) {
this.cpp = cpp;
}
private boolean isResultDelimiter(String str) {
if (str.isEmpty())
return false;
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) != '=')
return false;
}
return true;
}
protected void assertStatusOk(RefactoringStatus status) {
if (!status.isOK())
fail("Error or warning status: " + status.getEntries()[0].getMessage());
}
protected void assertStatusWarning(RefactoringStatus status, int number) {
if (number > 0) {
assertTrue("Warning status expected", status.hasWarning());
}
RefactoringStatusEntry[] entries = status.getEntries();
int count = 0;
for (RefactoringStatusEntry entry : entries) {
if (entry.isWarning()) {
++count;
}
}
assertEquals("Found " + count + " warnings instead of expected " + number, number, count);
}
protected void assertStatusInfo(RefactoringStatus status, int number) {
if (number > 0) {
assertTrue("Info status expected", status.hasInfo());
}
RefactoringStatusEntry[] entries = status.getEntries();
int count = 0;
for (RefactoringStatusEntry entry : entries) {
if (entry.isInfo()) {
++count;
}
}
assertEquals("Found " + count + " informational messages instead of expected " + number, number, count);
}
protected void assertStatusError(RefactoringStatus status, int number) {
if (number > 0) {
assertTrue("Error status expected", status.hasError());
}
RefactoringStatusEntry[] entries = status.getEntries();
int count = 0;
for (RefactoringStatusEntry entry : entries) {
if (entry.isError()) {
++count;
}
}
assertEquals("Found " + count + " errors instead of expected " + number, number, count);
}
protected void assertStatusFatalError(RefactoringStatus status, int number) {
if (number > 0) {
assertTrue("Fatal error status expected", status.hasFatalError());
}
RefactoringStatusEntry[] entries = status.getEntries();
int count = 0;
for (RefactoringStatusEntry entry : entries) {
if (entry.isFatalError()) {
++count;
}
}
assertEquals("Found " + count + " fatal errors instead of expected " + number, number, count);
}
protected void assertStatusFatalError(RefactoringStatus status) {
assertTrue("Fatal error status expected", status.hasFatalError());
}
protected void assertEquals(TestSourceFile testFile, IFile file) throws Exception {
String actualSource = getFileContents(file);
assertEquals(testFile.getExpectedSource(), actualSource);
}
protected void compareFiles() throws Exception {
for (TestSourceFile testFile : testFiles) {
String expectedSource = testFile.getExpectedSource();
IFile file = cproject.getProject().getFile(new Path(testFile.getExpectedName()));
String actualSource = getFileContents(file);
expectedSource= expectedSource.replace("\r\n", "\n");
actualSource= actualSource.replace("\r\n", "\n");
assertEquals(expectedSource, actualSource);
}
}
protected String getFileContents(IFile file) throws Exception {
BufferedReader reader = new BufferedReader(new InputStreamReader(file.getContents(), "UTF-8"));
StringBuilder buffer = new StringBuilder();
char[] part= new char[2048];
int read= 0;
while ((read= reader.read(part)) != -1)
buffer.append(part, 0, read);
reader.close();
return buffer.toString().replace("\r", "");
}
protected void resetPreferences() {
getPreferenceStore().setToDefault(PreferenceConstants.CLASS_MEMBER_ASCENDING_VISIBILITY_ORDER);
}
protected IPreferenceStore getPreferenceStore() {
return CUIPlugin.getDefault().getPreferenceStore();
}
}