blob: 7d190bef5dfd689659a9a8d19fe2bf31d1470806 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2015 Wind River Systems, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Anton Leherbauer (Wind River Systems) - initial API and implementation
* Andrew Eidsness - fix and test for bug 278632
* Serge Beauchamp (Freescale Semiconductor) - Bug 417909
*******************************************************************************/
package org.eclipse.cdt.ui.tests.text;
import java.io.File;
import java.lang.ref.WeakReference;
import java.net.URI;
import java.net.URISyntaxException;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
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.ResourceHelper;
import org.eclipse.cdt.internal.core.model.ExternalTranslationUnit;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
import org.eclipse.cdt.internal.ui.util.EditorUtility;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.testplugin.Accessor;
import org.eclipse.cdt.ui.testplugin.DisplayHelper;
import org.eclipse.cdt.ui.testplugin.EditorTestHelper;
import org.eclipse.cdt.ui.testplugin.FileTool;
import org.eclipse.cdt.ui.testplugin.ResourceTestHelper;
import org.eclipse.cdt.ui.tests.BaseUITestCase;
import org.eclipse.cdt.ui.text.ICColorConstants;
import org.eclipse.cdt.ui.text.IColorManager;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.filesystem.provider.FileSystem;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Basic CEditor tests.
*
* @since 4.0
*/
public class BasicCEditorTest extends BaseUITestCase {
final static class TestDocListener implements IDocumentListener {
public boolean fDocChanged;
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
}
@Override
public void documentChanged(DocumentEvent event) {
fDocChanged = true;
}
}
public static class Bug278632FileSystem extends FileSystem {
@Override
public IFileStore getStore(URI uri) {
try {
// For the test case, this just returns the FS implementation used for the null
// filesystem. In a real application this would be a real implementation,
// however for the purposes of exposing the bug, any non-"file://" scheme will do.
return EFS.getStore(new URI(EFS.getNullFileSystem().getScheme(), uri.getSchemeSpecificPart(), null));
} catch (URISyntaxException | CoreException e) {
throw new RuntimeException(e);
}
}
}
private CEditor fEditor;
private SourceViewer fSourceViewer;
private ICProject fCProject;
private IProject fNonCProject;
private StyledText fTextWidget;
private Accessor fAccessor;
private IDocument fDocument;
private TestDocListener fDocListener = new TestDocListener();
public static Test suite() {
return new TestSuite(BasicCEditorTest.class);
}
@Override
protected void setUp() throws Exception {
closeWelcome();
super.setUp();
}
@Override
protected void tearDown() throws Exception {
EditorTestHelper.closeEditor(fEditor);
if (fCProject != null)
CProjectHelper.delete(fCProject);
if (fNonCProject != null) {
ResourceHelper.delete(fNonCProject);
}
super.tearDown();
}
private void setUpEditor(IFile file) throws PartInitException {
IEditorPart editor = EditorTestHelper.openInEditor(file, true);
assertNotNull(editor);
assertTrue(editor instanceof CEditor);
fEditor = (CEditor) editor;
fTextWidget = fEditor.getViewer().getTextWidget();
assertNotNull(fTextWidget);
fAccessor = new Accessor(fTextWidget, StyledText.class);
fDocument = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
assertNotNull(fDocument);
}
private void setUpEditor(String file) throws PartInitException {
setUpEditor(ResourceTestHelper.findFile(file));
}
private void setUpEditor(File file) throws PartInitException, CModelException {
IEditorPart editor = EditorUtility
.openInEditor(new ExternalTranslationUnit(fCProject, file.toURI(), CCorePlugin.CONTENT_TYPE_CXXSOURCE));
assertNotNull(editor);
assertTrue(editor instanceof CEditor);
fEditor = (CEditor) editor;
fTextWidget = fEditor.getViewer().getTextWidget();
assertNotNull(fTextWidget);
fAccessor = new Accessor(fTextWidget, StyledText.class);
fDocument = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
assertNotNull(fDocument);
}
private void setUpEditorUsingFileStore(File file) throws CoreException {
final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
IEditorPart editor = IDE.openEditorOnFileStore(activePage, EFS.getStore(URIUtil.toURI(file.getAbsolutePath())));
assertNotNull(editor);
assertTrue(editor instanceof CEditor);
fEditor = (CEditor) editor;
fTextWidget = fEditor.getViewer().getTextWidget();
assertNotNull(fTextWidget);
fAccessor = new Accessor(fTextWidget, StyledText.class);
fDocument = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
assertNotNull(fDocument);
}
public void testEditTranslationUnit() throws Exception {
final String file = "/ceditor/src/main.cpp";
fCProject = EditorTestHelper.createCProject("ceditor", "resources/ceditor", false, false);
IFile mainFile = ResourceTestHelper.findFile(file);
assertNotNull(mainFile);
setUpEditor(mainFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
String content = fDocument.get();
setCaret(0);
String newtext = "/* " + getName() + " */";
type(newtext);
type('\n');
String newContent = fDocument.get();
assertEquals("Edit failed", newtext, newContent.substring(0, newtext.length()));
// save
fEditor.doSave(new NullProgressMonitor());
assertFalse("Editor is still dirty", fEditor.isDirty());
// close and reopen
EditorTestHelper.closeEditor(fEditor);
setUpEditor(mainFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
content = fDocument.get();
assertEquals("Save failed", newContent, content);
// check reconciler
ITranslationUnit tUnit = fEditor.getInputCElement();
ICElement[] children = tUnit.getChildren();
assertEquals(2, children.length);
setCaret(content.length());
type('\n');
type("void func() {}\n");
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
children = tUnit.getChildren();
assertEquals(3, children.length);
}
//{Point.cpp}
//#include <math.h>
//class Point {
//public:
// Point(double xc, double yc) :
// x(xc), y(yc) {}
// double distance(const Point& other) const;
// int compareX(const Point& other) const;
// double x;
// double y;
//};
//double Point::distance(const Point& other) const {
// double dx = x - other.x;
// double dy = y - other.y;
// return sqrt(dx * dx + dy * dy);
//}
//int Point::compareX(const Point& other) const {
// if (x < other.x) {
// return -1;
// }
// else if (x > other.x) {
// return 1;
// }
// else {
// return 0;
// }
//}
//
public void testEditNewTranslationUnit() throws Exception {
fCProject = EditorTestHelper.createCProject("ceditor", "resources/ceditor", false, false);
IFile newFile = createFile(fCProject.getProject(), "Point.cpp", "");
assertNotNull(newFile);
setUpEditor(newFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
String content = fDocument.get();
setCaret(0);
String newText = "/* " + getName() + " */\n";
newText += readTaggedComment("Point.cpp");
String[] lines = newText.split("\\r\\n|\\r|\\n");
for (int i = 0; i < lines.length; i++) {
String line = lines[i].trim();
if (line.startsWith("}")) {
setCaret(fDocument.get().indexOf(line, getCaret()) + line.length());
Thread.sleep(100);
} else {
if (i > 0)
type('\n');
type(line);
Thread.sleep(50);
}
}
String newContent = fDocument.get();
String[] newLines = newContent.split("\\r\\n|\\r|\\n");
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
assertEquals(line, newLines[i]);
}
// save
fEditor.doSave(new NullProgressMonitor());
assertFalse("Editor is still dirty", fEditor.isDirty());
// close and reopen
EditorTestHelper.closeEditor(fEditor);
setUpEditor(newFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
content = fDocument.get().trim();
assertEquals("Save failed", newContent, content);
// check reconciler
ITranslationUnit tUnit = fEditor.getInputCElement();
ICElement[] children = tUnit.getChildren();
assertEquals(4, children.length);
setCaret(content.length());
type('\n');
type("void func() {}\n");
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
children = tUnit.getChildren();
assertEquals(5, children.length);
}
public void testEditInNonCProject() throws Exception {
final String file = "/ceditor/src/main.cpp";
fNonCProject = EditorTestHelper.createNonCProject("ceditor", "resources/ceditor", false);
setUpEditor(file);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
String content = fDocument.get();
setCaret(0);
String newtext = "/* " + getName() + " */";
type(newtext);
type('\n');
String newContent = fDocument.get();
assertEquals("Edit failed", newtext, newContent.substring(0, newtext.length()));
// save
fEditor.doSave(new NullProgressMonitor());
assertFalse("Editor is still dirty", fEditor.isDirty());
// close and reopen
EditorTestHelper.closeEditor(fEditor);
setUpEditor(file);
content = fDocument.get();
assertEquals("Save failed", newContent, content);
}
public void testEditExternalTranslationUnit() throws Exception {
final String file = "/ceditor/src/main.cpp";
fCProject = EditorTestHelper.createCProject("ceditor", "resources/ceditor", false, false);
IFile mainFile = ResourceTestHelper.findFile(file);
assertNotNull(mainFile);
File tmpFile = File.createTempFile("tmp", ".cpp");
tmpFile.deleteOnExit();
FileTool.copy(mainFile.getLocation().toFile(), tmpFile);
setUpEditor(tmpFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
String content = fDocument.get();
setCaret(0);
String newtext = "/* " + getName() + " */";
type(newtext);
type('\n');
String newContent = fDocument.get();
assertEquals("Edit failed", newtext, newContent.substring(0, newtext.length()));
// save
fEditor.doSave(new NullProgressMonitor());
assertFalse("Editor is still dirty", fEditor.isDirty());
// close and reopen
EditorTestHelper.closeEditor(fEditor);
setUpEditor(tmpFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
content = fDocument.get();
assertEquals("Save failed", newContent, content);
// check reconciler
ITranslationUnit tUnit = fEditor.getInputCElement();
ICElement[] children = tUnit.getChildren();
assertEquals(2, children.length);
setCaret(content.length());
type('\n');
type("void func() {}\n");
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
children = tUnit.getChildren();
assertEquals(3, children.length);
tmpFile.delete();
}
public void testEditExternalTranslationUnitUsingFileStore() throws Exception {
final String file = "/ceditor/src/main.cpp";
fCProject = EditorTestHelper.createCProject("ceditor", "resources/ceditor", false, false);
IFile mainFile = ResourceTestHelper.findFile(file);
assertNotNull(mainFile);
File tmpFile = File.createTempFile("tmp", ".cpp");
tmpFile.deleteOnExit();
FileTool.copy(mainFile.getLocation().toFile(), tmpFile);
setUpEditorUsingFileStore(tmpFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
String content = fDocument.get();
setCaret(0);
String newtext = "/* " + getName() + " */";
type(newtext);
type('\n');
String newContent = fDocument.get();
assertEquals("Edit failed", newtext, newContent.substring(0, newtext.length()));
// save
fEditor.doSave(new NullProgressMonitor());
assertFalse("Editor is still dirty", fEditor.isDirty());
// close and reopen
EditorTestHelper.closeEditor(fEditor);
setUpEditor(tmpFile);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
content = fDocument.get();
assertEquals("Save failed", newContent, content);
// check reconciler
ITranslationUnit tUnit = fEditor.getInputCElement();
ICElement[] children = tUnit.getChildren();
assertEquals(2, children.length);
setCaret(content.length());
type('\n');
type("void func() {}\n");
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
children = tUnit.getChildren();
assertEquals(3, children.length);
tmpFile.delete();
}
public void testSyntaxHighlighting_Bug180433() throws Exception {
IColorManager colorMgr = CUIPlugin.getDefault().getTextTools().getColorManager();
colorMgr.unbindColor(ICColorConstants.PP_DIRECTIVE);
colorMgr.bindColor(ICColorConstants.PP_DIRECTIVE, new RGB(7, 7, 7));
final Color ppDirectiveColor = colorMgr.getColor(ICColorConstants.PP_DIRECTIVE);
final String file = "/ceditor/src/main.cpp";
fCProject = EditorTestHelper.createCProject("ceditor", "resources/ceditor", false, false);
setUpEditor(file);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
String content = fDocument.get();
String include = "#include";
int includeIdx = content.indexOf(include);
StyleRange style = fTextWidget.getStyleRangeAtOffset(includeIdx);
assertSame(style.foreground, ppDirectiveColor);
}
public void testNonFileEFSResource_Bug278632() {
IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
assertNotNull(page);
IEditorRegistry reg = page.getWorkbenchWindow().getWorkbench().getEditorRegistry();
String editorID = reg.getDefaultEditor(".c").getId();
URI uri = null;
try {
uri = new URI("bug278632", "/folder/file", null);
} catch (URISyntaxException e) {
e.printStackTrace();
}
assertNotNull(uri);
IEditorPart part = null;
try {
part = IDE.openEditor(page, uri, editorID, true);
} catch (PartInitException e) {
e.printStackTrace();
}
assertNotNull(part);
assertTrue(part instanceof CEditor);
}
public static void closeWelcome() {
try {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
IWorkbenchPage activePage = window.getActivePage();
IWorkbenchPart activePart = activePage.getActivePart();
if (activePart.getTitle().equals("Welcome")) {
activePart.dispose();
}
} catch (Exception e) {
// ignore
}
}
public void testLeakingInstanceAfterClose() throws Exception {
final String file = "/ceditor/src/main.cpp";
fCProject = EditorTestHelper.createCProject("ceditor", "resources/ceditor", false, false);
setUpEditor(file);
fSourceViewer = EditorTestHelper.getSourceViewer(fEditor);
assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100));
WeakReference<CEditor> ref = new WeakReference<>(fEditor);
EditorTestHelper.closeEditor(fEditor);
fEditor = null;
fSourceViewer = null;
int ngc = 0;
while (ref.get() != null && ngc++ < 100) {
System.gc();
EditorTestHelper.runEventQueue(200);
}
System.out
.println("BasicCEditorTest.testLeakingInstanceAfterClose took " + ngc + " iterations of loop to exit");
assertNull("CEditor instance seems to be leaking after close", ref.get());
}
/**
* Type characters into the styled text.
*
* @param characters the characters to type
*/
private void type(CharSequence characters) {
for (int i = 0; i < characters.length(); i++)
type(characters.charAt(i), 0, 0);
}
/**
* Type a character into the styled text.
*
* @param character the character to type
*/
private void type(char character) {
type(character, 0, 0);
}
/**
* Type a character into the styled text.
*
* @param character the character to type
* @param keyCode the key code
* @param stateMask the state mask
*/
private void type(char character, int keyCode, int stateMask) {
fDocument.addDocumentListener(fDocListener);
fDocListener.fDocChanged = false;
Event event = new Event();
event.character = character;
event.keyCode = keyCode;
event.stateMask = stateMask;
fAccessor.invoke("handleKeyDown", new Object[] { event });
new DisplayHelper() {
@Override
protected boolean condition() {
return fDocListener.fDocChanged;
}
}.waitForCondition(EditorTestHelper.getActiveDisplay(), 50);
fDocument.removeDocumentListener(fDocListener);
}
private int getCaret() {
return ((ITextSelection) fEditor.getSelectionProvider().getSelection()).getOffset();
}
private void setCaret(int offset) {
fEditor.getSelectionProvider().setSelection(new TextSelection(offset, 0));
int newOffset = ((ITextSelection) fEditor.getSelectionProvider().getSelection()).getOffset();
assertEquals(offset, newOffset);
}
// See Bug 417909 - Opening a large file from a progress dialog causes the scalability dialog to
// disappear.
public void testScalabilityDialogNotDismissedInadvertently_417909() throws Exception {
// 1. Create a project with a very large source file.
fCProject = EditorTestHelper.createCProject("ceditor", "resources/ceditor", false, false);
// 1a. Dynamically create the large source file.
String originalFile = "/ceditor/src/main.cpp";
String file = "/ceditor/src/large_main.cpp";
ResourceTestHelper.copy(originalFile, file);
setUpEditor(file);
StringBuilder buffer = new StringBuilder(fDocument.get());
buffer.append("unsigned long c[250000] = {\n");
for (int i = 0; i < 12499; i++)
buffer.append(" 0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,\n");
buffer.append(" 0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1,0x0,0x1\n};\n");
fDocument.set(buffer.toString());
fEditor.doSave(new NullProgressMonitor());
EditorTestHelper.closeEditor(fEditor);
// 2. Create and open a progress dialog window.
IWorkbenchWindow window = EditorTestHelper.getActiveWorkbenchWindow();
ProgressMonitorDialog dialog = new ProgressMonitorDialog(window.getShell());
dialog.open();
// 3. Open the large source file in the editor, which should cause the
// scalability dialog to be displayed.
setUpEditor(file);
// 4. Close the progress dialog window
dialog.close();
// 5. Verify that the scalability dialog has not been closed
// unexpectedly.
Shell scalabilityDialog = null;
Shell[] shells = Display.getCurrent().getShells();
for (Shell shell : shells) {
if (shell.getText().equals(CEditorMessages.Scalability_info)) {
scalabilityDialog = shell;
break;
}
}
assertNotNull(scalabilityDialog);
scalabilityDialog.close();
EditorTestHelper.closeEditor(fEditor);
}
}