| /******************************************************************************* |
| * Copyright (c) 2005, 2010 IBM Corporation 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: |
| * IBM Corporation - initial API and implementation |
| * |
| *******************************************************************************/ |
| package org.eclipse.wst.sse.ui.tests; |
| |
| import java.io.ByteArrayInputStream; |
| import java.lang.reflect.Method; |
| import java.util.Arrays; |
| |
| import junit.framework.TestCase; |
| |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBuffer; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| import org.eclipse.core.filebuffers.LocationKind; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IProjectDescription; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.reconciler.IReconciler; |
| import org.eclipse.jface.text.source.IAnnotationModel; |
| import org.eclipse.jface.text.source.SourceViewerConfiguration; |
| import org.eclipse.jface.viewers.ISelectionProvider; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IPageLayout; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchWindow; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.ide.IDE; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.part.IShowInTargetList; |
| import org.eclipse.ui.texteditor.AbstractTextEditor; |
| import org.eclipse.wst.sse.ui.StructuredTextEditor; |
| import org.eclipse.wst.sse.ui.internal.SSEUIPlugin; |
| import org.eclipse.wst.sse.ui.internal.StructuredTextViewer; |
| import org.eclipse.wst.sse.ui.internal.reconcile.DocumentRegionProcessor; |
| import org.eclipse.wst.sse.ui.reconcile.ISourceReconcilingListener; |
| |
| public class TestStructuredTextEditor extends TestCase { |
| private final String PROJECT_NAME = "TestStructuredTextEditor"; |
| private final String FILE_NAME = "testStructuredTextEditor.xml"; |
| private static final int MAX_WAIT = 10000; |
| private static final int TIME_DELTA = 50; |
| |
| private static StructuredTextEditor fEditor; |
| private static IFile fFile; |
| private static boolean fIsSetup = false; |
| |
| public TestStructuredTextEditor() { |
| super("TestStructuredTextEditor"); |
| } |
| |
| protected void setUp() throws Exception { |
| if (!fIsSetup) { |
| // create project |
| createProject(PROJECT_NAME); |
| fFile = getOrCreateFile(PROJECT_NAME + "/" + FILE_NAME); |
| fIsSetup = true; |
| } |
| |
| if (fIsSetup && fEditor == null) { |
| IWorkbenchWindow workbenchWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); |
| IWorkbenchPage page = workbenchWindow.getActivePage(); |
| IEditorInput input = new FileEditorInput(fFile); |
| /* |
| * This should take care of testing init, createPartControl, |
| * beginBackgroundOperation, endBackgroundOperation methods |
| */ |
| IEditorPart part = page.openEditor(input, "org.eclipse.wst.sse.ui.StructuredTextEditor.test", true); |
| if (part instanceof StructuredTextEditor) |
| fEditor = (StructuredTextEditor) part; |
| else |
| assertTrue("Unable to open structured text editor", false); |
| } |
| } |
| |
| protected void tearDown() throws Exception { |
| if (fEditor != null) { |
| /* |
| * This should take care of testing close and dispose methods |
| */ |
| fEditor.close(false); |
| assertTrue("Unable to close editor", true); |
| fEditor = null; |
| } |
| } |
| |
| private void createProject(String projName) { |
| IProjectDescription description = ResourcesPlugin.getWorkspace().newProjectDescription(projName); |
| |
| IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projName); |
| try { |
| project.create(description, new NullProgressMonitor()); |
| project.open(new NullProgressMonitor()); |
| } |
| catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| protected IFile getOrCreateFile(String filePath) { |
| IFile blankJspFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(filePath)); |
| if (blankJspFile != null && !blankJspFile.exists()) { |
| try { |
| blankJspFile.create(new ByteArrayInputStream(new byte[0]), true, new NullProgressMonitor()); |
| } |
| catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| } |
| return blankJspFile; |
| } |
| |
| public void testDoSaving() { |
| fEditor.doRevertToSaved(); |
| assertTrue("Unable to revert to saved", true); |
| fEditor.doSave(new NullProgressMonitor()); |
| assertTrue("Unable to save", true); |
| } |
| |
| public void testEditorContextMenuAboutToShow() { |
| IMenuManager menu = new MenuManager(); |
| fEditor.editorContextMenuAboutToShow(menu); |
| assertTrue("Unable to prepare for context menu about to show", true); |
| menu.dispose(); |
| menu = null; |
| } |
| |
| public void testGetAdapter() { |
| Object adapter = fEditor.getAdapter(IShowInTargetList.class); |
| assertTrue("Get adapter for show in target failed", adapter instanceof IShowInTargetList); |
| } |
| |
| public void testGetSetEditorPart() { |
| fEditor.setEditorPart(null); |
| assertTrue("Unable to set editor part", true); |
| IEditorPart part = fEditor.getEditorPart(); |
| assertTrue("Did not get expected editor part", part instanceof StructuredTextEditor); |
| } |
| |
| public void testInitializeDocumentProvider() { |
| fEditor.initializeDocumentProvider(null); |
| assertTrue("Unable to initialize document provider", true); |
| } |
| |
| public void testGetOrientation() { |
| int or = fEditor.getOrientation(); |
| assertEquals(SWT.LEFT_TO_RIGHT, or); |
| } |
| |
| public void testGetSelectionProvider() { |
| ISelectionProvider provider = fEditor.getSelectionProvider(); |
| assertNotNull("Editor's selection provider was null", provider); |
| } |
| |
| public void testGetTextViewer() { |
| StructuredTextViewer viewer = fEditor.getTextViewer(); |
| assertNotNull("Editor's text viewer was null", viewer); |
| } |
| |
| public void testRememberRestoreSelection() { |
| fEditor.rememberSelection(); |
| assertTrue("Unable to remember editor selection", true); |
| fEditor.restoreSelection(); |
| assertTrue("Unable to restore editor selection", true); |
| } |
| |
| public void testSafelySanityCheck() { |
| fEditor.safelySanityCheckState(fEditor.getEditorInput()); |
| assertTrue("Unable to safely sanity check editor state", true); |
| } |
| |
| public void testShowBusy() { |
| fEditor.showBusy(false); |
| assertTrue("Unable to show editor is busy", true); |
| } |
| |
| public void testUpdate() { |
| fEditor.update(); |
| assertTrue("Unable to update editor", true); |
| } |
| |
| public void testSingleNonUIThreadUpdatesToEditorDocument() throws Exception { |
| IFile file = getOrCreateFile(PROJECT_NAME + "/" + "testBackgroundChanges.xml"); |
| ITextFileBufferManager textFileBufferManager = FileBuffers.getTextFileBufferManager(); |
| textFileBufferManager.connect(file.getFullPath(), LocationKind.IFILE, new NullProgressMonitor()); |
| |
| ITextFileBuffer textFileBuffer = textFileBufferManager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE); |
| final IDocument document = textFileBuffer.getDocument(); |
| document.replace(0, 0, "<?xml encoding=\"UTF-8\" version=\"1.0\"?>\n"); |
| textFileBuffer.commit(new NullProgressMonitor(), true); |
| |
| String testText = document.get() + "<c/><b/><a/>"; |
| final int end = document.getLength(); |
| IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| IEditorPart openedEditor = IDE.openEditor(activePage, file); |
| |
| final boolean state[] = new boolean[]{false}; |
| Job changer = new Job("text changer") { |
| protected IStatus run(IProgressMonitor monitor) { |
| try { |
| document.replace(end, 0, "<a/>"); |
| document.replace(end, 0, "<b/>"); |
| document.replace(end, 0, "<c/>"); |
| } |
| catch (Exception e) { |
| return new Status(IStatus.ERROR, SSEUIPlugin.ID, e.getMessage()); |
| } |
| finally { |
| state[0] = true; |
| } |
| return Status.OK_STATUS; |
| } |
| }; |
| changer.setUser(true); |
| changer.setSystem(false); |
| changer.schedule(); |
| |
| while (!state[0]) { |
| openedEditor.getSite().getShell().getDisplay().readAndDispatch(); |
| } |
| |
| String finalText = document.get(); |
| textFileBuffer.commit(new NullProgressMonitor(), true); |
| textFileBufferManager.disconnect(file.getFullPath(), LocationKind.IFILE, new NullProgressMonitor()); |
| activePage.closeEditor(openedEditor, false); |
| assertEquals("Non-UI changes did not apply", testText, finalText); |
| } |
| |
| |
| /** |
| * Note: This test takes a while to run, but it's testing scalability after all. |
| * @throws Exception |
| */ |
| public void testManyNonUIThreadsUpdatingEditorDocument() throws Exception { |
| final int numberOfJobs = 50; |
| /** |
| * 15 minute timeout before we stop waiting for the change jobs to |
| * complete |
| */ |
| long timeout = 15*60*1000; |
| |
| long startTime = System.currentTimeMillis(); |
| IFile file = getOrCreateFile(PROJECT_NAME + "/" + "testManyBackgroundChanges.xml"); |
| ITextFileBufferManager textFileBufferManager = FileBuffers.getTextFileBufferManager(); |
| textFileBufferManager.connect(file.getFullPath(), LocationKind.IFILE, new NullProgressMonitor()); |
| |
| ITextFileBuffer textFileBuffer = textFileBufferManager.getTextFileBuffer(file.getFullPath(), LocationKind.IFILE); |
| final IDocument document = textFileBuffer.getDocument(); |
| document.replace(0, 0, "<?xml encoding=\"UTF-8\" version=\"1.0\"?>\n"); |
| textFileBuffer.commit(new NullProgressMonitor(), true); |
| |
| final int insertionPoint = document.getLength(); |
| // numberOfJobs Jobs, inserting 26 tags, each 4 characters long |
| int testLength = insertionPoint + numberOfJobs * 4 * 26; |
| IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| activePage.showView(IPageLayout.ID_PROGRESS_VIEW); |
| IEditorPart openedEditor = IDE.openEditor(activePage, file); |
| |
| final int finished[] = new int[]{numberOfJobs}; |
| Job changers[] = new Job[numberOfJobs]; |
| for (int i = 0; i < changers.length; i++) { |
| changers[i] = new Job("Text Changer " + Integer.toString(i)) { |
| protected IStatus run(IProgressMonitor monitor) { |
| try { |
| char names[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); |
| for (int j = 0; j < names.length; j++) { |
| document.replace(insertionPoint + 4 * j, 0, "<" + names[j] + "/>"); |
| } |
| } |
| catch (Exception e) { |
| return new Status(IStatus.ERROR, SSEUIPlugin.ID, e.getMessage()); |
| } |
| finally { |
| finished[0]--; |
| } |
| return Status.OK_STATUS; |
| } |
| }; |
| changers[i].setUser(true); |
| changers[i].setSystem(false); |
| } |
| for (int i = 0; i < changers.length; i++) { |
| changers[i].schedule(); |
| } |
| |
| long runtime = 0; |
| while (finished[0] > 0 && (runtime = System.currentTimeMillis()) - startTime < timeout) { |
| openedEditor.getSite().getShell().getDisplay().readAndDispatch(); |
| } |
| assertTrue("Test timed out", runtime - startTime < timeout); |
| |
| int finalLength = document.getLength(); |
| textFileBuffer.commit(new NullProgressMonitor(), true); |
| textFileBufferManager.disconnect(file.getFullPath(), LocationKind.IFILE, new NullProgressMonitor()); |
| activePage.closeEditor(openedEditor, false); |
| assertEquals("Some non-UI changes did not apply", testLength, finalLength); |
| } |
| |
| /** |
| * Test receiving the initial reconcile notification when the editor opens |
| */ |
| public void testInitialReconciling() throws Exception { |
| IFile file = getOrCreateFile(PROJECT_NAME + "/" + "reconcilingtest.xml"); |
| IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| final int[] state = new int[2]; |
| Arrays.fill(state, -1); |
| ISourceReconcilingListener listener = new ISourceReconcilingListener() { |
| int mod = 0; |
| public void reconciled(IDocument document, IAnnotationModel model, boolean forced, IProgressMonitor progressMonitor) { |
| state[1] = mod++; |
| } |
| |
| public void aboutToBeReconciled() { |
| state[0] = mod++; |
| } |
| }; |
| IEditorPart editor = IDE.openEditor(activePage, file, "org.eclipse.wst.sse.ui.StructuredTextEditor.test"); |
| try { |
| assertTrue("Not a StructuredTextEditor", editor instanceof StructuredTextEditor); |
| addReconcilingListener((StructuredTextEditor) editor, listener); |
| waitForReconcile(state); |
| assertTrue("Initial: Reconciling did not complete in a timely fashion", state[0] != -1 && state[1] != -1); |
| assertTrue("Initial: aboutToBeReconciled not invoked first (" + state[0] +")", state[0] == 0); |
| assertTrue("Initial: reconciled not invoked after aboutToBeReconciled (" + state[1] +")", state[1] == 1); |
| } finally { |
| if (editor != null && activePage != null) { |
| activePage.closeEditor(editor, false); |
| } |
| } |
| } |
| |
| /** |
| * Test that modifications to the editor's content will notify reconciling listeners |
| */ |
| public void testModificationReconciling() throws Exception { |
| IFile file = getOrCreateFile(PROJECT_NAME + "/" + "reconcilingmodificationtest.xml"); |
| IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| final int[] state = new int[2]; |
| Arrays.fill(state, -1); |
| ISourceReconcilingListener listener = new ISourceReconcilingListener() { |
| int mod = 0; |
| public void reconciled(IDocument document, IAnnotationModel model, boolean forced, IProgressMonitor progressMonitor) { |
| state[1] = mod++; |
| } |
| |
| public void aboutToBeReconciled() { |
| state[0] = mod++; |
| } |
| }; |
| IEditorPart editor = IDE.openEditor(activePage, file, "org.eclipse.wst.sse.ui.StructuredTextEditor.test"); |
| try { |
| assertTrue("Not a StructuredTextEditor", editor instanceof StructuredTextEditor); |
| addReconcilingListener((StructuredTextEditor) editor, listener); |
| waitForReconcile(state); |
| assertTrue("Initial: Reconciling did not complete in a timely fashion", state[0] != -1 && state[1] != -1); |
| assertTrue("Initial: aboutToBeReconciled not invoked first (" + state[0] +")", state[0] == 0); |
| assertTrue("Initial: reconciled not invoked after aboutToBeReconciled (" + state[1] +")", state[1] == 1); |
| IDocument document = ((StructuredTextEditor) editor).getDocumentProvider().getDocument(editor.getEditorInput()); |
| assertTrue("Editor doesn't have a document", document != null); |
| document.set("<?xml version=\"1.0\" ?>"); |
| Arrays.fill(state, -1); |
| waitForReconcile(state); |
| assertTrue("Modified: Reconciling did not complete in a timely fashion", state[0] != -1 && state[1] != -1); |
| assertTrue("Modified: aboutToBeReconciled not invoked first (" + state[0] +")", state[0] == 2); |
| assertTrue("Modified: reconciled not invoked after aboutToBeReconciled (" + state[1] +")", state[1] == 3); |
| } finally { |
| if (editor != null && activePage != null) { |
| activePage.closeEditor(editor, false); |
| } |
| } |
| } |
| |
| /** |
| * Test that an editor notifies reconciling listeners when the editor gets focus. |
| */ |
| public void testFocusedReconciling() throws Exception { |
| IFile file = getOrCreateFile(PROJECT_NAME + "/" + "focustest.xml"); |
| IFile fileAlt = getOrCreateFile(PROJECT_NAME + "/" + "focustestAlt.xml"); |
| IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
| final int[] state = new int[2]; |
| Arrays.fill(state, -1); |
| ISourceReconcilingListener listener = new ISourceReconcilingListener() { |
| int mod = 0; |
| public void reconciled(IDocument document, IAnnotationModel model, boolean forced, IProgressMonitor progressMonitor) { |
| state[1] = mod++; |
| } |
| |
| public void aboutToBeReconciled() { |
| state[0] = mod++; |
| } |
| }; |
| IEditorPart editor = IDE.openEditor(activePage, file, "org.eclipse.wst.sse.ui.StructuredTextEditor.test"); |
| try { |
| assertTrue("Not a StructuredTextEditor", editor instanceof StructuredTextEditor); |
| addReconcilingListener((StructuredTextEditor) editor, listener); |
| waitForReconcile(state); |
| assertTrue("Initial: Reconciling did not complete in a timely fashion", state[0] != -1 && state[1] != -1); |
| assertTrue("Initial: aboutToBeReconciled not invoked first (" + state[0] +")", state[0] == 0); |
| assertTrue("Initial: reconciled not invoked after aboutToBeReconciled (" + state[1] +")", state[1] == 1); |
| IDE.openEditor(activePage, fileAlt, "org.eclipse.wst.sse.ui.StructuredTextEditor.test"); |
| Arrays.fill(state, -1); |
| IEditorPart editorPart = IDE.openEditor(activePage, file, "org.eclipse.wst.sse.ui.StructuredTextEditor.test"); |
| assertEquals("Didn't get the original editor back.", editor, editorPart); |
| waitForReconcile(state); |
| assertTrue("Modified: Reconciling did not complete in a timely fashion", state[0] != -1 && state[1] != -1); |
| assertTrue("Modified: aboutToBeReconciled not invoked first (" + state[0] +")", state[0] == 2); |
| assertTrue("Modified: reconciled not invoked after aboutToBeReconciled (" + state[1] +")", state[1] == 3); |
| } finally { |
| if (editor != null && activePage != null) { |
| activePage.closeEditor(editor, false); |
| } |
| } |
| } |
| |
| private void addReconcilingListener(StructuredTextEditor editor, ISourceReconcilingListener listener) throws Exception { |
| Method mConfig = AbstractTextEditor.class.getDeclaredMethod("getSourceViewerConfiguration", null); |
| mConfig.setAccessible(true); |
| Object config = mConfig.invoke(editor, null); |
| assertTrue("Did not get a source viewer configuration", config instanceof SourceViewerConfiguration); |
| IReconciler reconciler = ((SourceViewerConfiguration) config).getReconciler(fEditor.getTextViewer()); |
| assertTrue("Reconciler is not a DirtyRegionProcessor", reconciler instanceof DocumentRegionProcessor); |
| ((DocumentRegionProcessor) reconciler).addReconcilingListener(listener); |
| } |
| |
| public void waitForReconcile(int[] state) throws Exception { |
| int time = 0; |
| while ((state[0] == -1 || state[1] == -1) && time < MAX_WAIT) { |
| Thread.sleep(TIME_DELTA); |
| time += TIME_DELTA; |
| } |
| } |
| |
| } |