[296761] Create Unit Tests for Reconciling Listeners
diff --git a/tests/org.eclipse.wst.sse.ui.tests/src/org/eclipse/wst/sse/ui/tests/TestStructuredTextEditor.java b/tests/org.eclipse.wst.sse.ui.tests/src/org/eclipse/wst/sse/ui/tests/TestStructuredTextEditor.java
index db298d8..54b4710 100644
--- a/tests/org.eclipse.wst.sse.ui.tests/src/org/eclipse/wst/sse/ui/tests/TestStructuredTextEditor.java
+++ b/tests/org.eclipse.wst.sse.ui.tests/src/org/eclipse/wst/sse/ui/tests/TestStructuredTextEditor.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2006 IBM Corporation and others.
+ * 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
@@ -12,6 +12,8 @@
 package org.eclipse.wst.sse.ui.tests;
 
 import java.io.ByteArrayInputStream;
+import java.lang.reflect.Method;
+import java.util.Arrays;
 
 import junit.framework.TestCase;
 
@@ -33,6 +35,9 @@
 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;
@@ -44,13 +49,18 @@
 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;
@@ -306,4 +316,139 @@
 		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");
+		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");
+		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");
+		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");
+			Arrays.fill(state, -1);
+			IEditorPart editorPart = IDE.openEditor(activePage, file, "org.eclipse.wst.sse.ui.StructuredTextEditor");
+			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;
+		}
+	}
+	
 }