diff options
author | Markus Keller | 2017-03-08 17:27:39 +0000 |
---|---|---|
committer | Markus Keller | 2017-03-08 17:27:39 +0000 |
commit | dad356880b62128e58665dd3e641e51ab61e03c0 (patch) | |
tree | e12b8e459073947b0a6b22293a142b966501b994 | |
parent | 5985a1edc5adad4a81892f9229e3269c8b25e794 (diff) | |
download | eclipse.platform.text-dad356880b62128e58665dd3e641e51ab61e03c0.tar.gz eclipse.platform.text-dad356880b62128e58665dd3e641e51ab61e03c0.tar.xz eclipse.platform.text-dad356880b62128e58665dd3e641e51ab61e03c0.zip |
Bug 511101: [Generic Editor] misses quick fix on error markers
- improved HoverTest (no endless loop, better logging)
- removed dependency on platform.ui test bundle
- improved ScreenshotTest
5 files changed, 655 insertions, 44 deletions
diff --git a/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF b/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF index e716de1e27d..cbb20943ee5 100644 --- a/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.jface.text.tests/META-INF/MANIFEST.MF @@ -9,7 +9,8 @@ Export-Package: org.eclipse.jface.text.tests, org.eclipse.jface.text.tests.reconciler, org.eclipse.jface.text.tests.rules, - org.eclipse.jface.text.tests.source + org.eclipse.jface.text.tests.source, + org.eclipse.jface.text.tests.util Require-Bundle: org.eclipse.jface.text;bundle-version="[3.5.0,4.0.0)", org.eclipse.jface;bundle-version="[3.5.0,4.0.0)", diff --git a/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/util/DisplayHelper.java b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/util/DisplayHelper.java new file mode 100644 index 00000000000..221e77fe96e --- /dev/null +++ b/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/util/DisplayHelper.java @@ -0,0 +1,581 @@ +package org.eclipse.jface.text.tests.util; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.junit.Assert; + +import org.eclipse.swt.widgets.Display; + + +/** + * Runs the event loop of the given display until {@link #condition()} becomes + * <code>true</code> or no events have occurred for the supplied timeout. + * Between running the event loop, {@link Display#sleep()} is called. + * <p> + * There is a caveat: the given timeouts must be long enough that the calling + * thread can enter <code>Display.sleep()</code> before the timeout elapses, + * otherwise, the waiter may time out before <code>sleep</code> is called and + * the sleeping thread may never be waken up. + * </p> + * + * @since 3.11 + */ +public abstract class DisplayHelper { +// copy of org.eclipse.jdt.testplugin.util.DisplayHelper in org.eclipse.jdt.ui.tests + + /** + * Controls if the timeout is used. For debugging use true, false otherwise + */ + private static final boolean DISABLE_TIMEOUT= false; + + /** + * Creates a new instance. + */ + protected DisplayHelper() { + } + + /** + * Until {@link #condition()} becomes <code>true</code> or the timeout + * elapses, call {@link Display#sleep()} and run the event loop. + * <p> + * If <code>timeout < 0</code>, the event loop is never driven and + * only the condition is checked. If <code>timeout == 0</code>, the event + * loop is driven at most once, but <code>Display.sleep()</code> is never + * invoked. + * </p> + * + * @param display the display to run the event loop of + * @param timeout the timeout in milliseconds + * @return <code>true</code> if the condition became <code>true</code>, + * <code>false</code> if the timeout elapsed + */ + public final boolean waitForCondition(Display display, long timeout) { + // if the condition already holds, succeed + if (condition()) + return true; + + if (timeout < 0) + return false; + + // if driving the event loop once makes the condition hold, succeed + // without spawning a thread. + driveEventQueue(display); + if (condition()) + return true; + + // if the timeout is negative or zero, fail + if (timeout == 0) + return false; + + // repeatedly sleep until condition becomes true or timeout elapses + DisplayWaiter waiter= new DisplayWaiter(display); + DisplayWaiter.Timeout timeoutState= waiter.start(timeout); + boolean condition; + try { + do { + if (display.sleep()) + driveEventQueue(display); + condition= condition(); + } while (!condition && !timeoutState.hasTimedOut()); + } finally { + waiter.stop(); + } + return condition; + } + + /** + * Call {@link Display#sleep()} and run the event loop until the given + * timeout has elapsed. + * <p> + * If <code>timeout < 0</code>, nothing happens. If + * <code>timeout == 0</code>, the event loop is driven exactly once, but + * <code>Display.sleep()</code> is never invoked. + * </p> + * + * @param display the display to run the event loop of + * @param millis the timeout in milliseconds + */ + public static void sleep(Display display, long millis) { + new DisplayHelper() { + @Override + public boolean condition() { + return false; + } + }.waitForCondition(display, millis); + } + + /** + * The condition which has to be met in order for {@link #waitForCondition(Display, long)} to + * return before the timeout elapses. + * + * @return <code>true</code> if the condition is met, <code>false</code> if the event loop + * should be driven some more + */ + protected abstract boolean condition(); + + /** + * Runs the event loop on the given display. + * + * @param display the display + * @return if <code>display.readAndDispatch</code> returned + * <code>true</code> at least once + */ + private static boolean driveEventQueue(Display display) { + boolean events= false; + while (display.readAndDispatch()) { + events= true; + } + return events; + } + + /** + * Until {@link #condition()} becomes <code>true</code> or the timeout + * elapses, call {@link Display#sleep()} and run the event loop. + * <p> + * If <code>timeout < 0</code>, the event loop is never driven and + * only the condition is checked. If <code>timeout == 0</code>, the event + * loop is driven at most once, but <code>Display.sleep()</code> is never + * invoked. + * </p> + * <p> + * The condition gets rechecked every <code>interval</code> milliseconds, even + * if no events were read from the queue. + * </p> + * + * @param display the display to run the event loop of + * @param timeout the timeout in milliseconds + * @param interval the interval to re-check the condition in milliseconds + * @return <code>true</code> if the condition became <code>true</code>, + * <code>false</code> if the timeout elapsed + */ + public final boolean waitForCondition(Display display, long timeout, long interval) { + // if the condition already holds, succeed + if (condition()) + return true; + + if (timeout < 0) + return false; + + // if driving the event loop once makes the condition hold, succeed + // without spawning a thread. + driveEventQueue(display); + if (condition()) + return true; + + // if the timeout is negative or zero, fail + if (timeout == 0) + return false; + + // repeatedly sleep until condition becomes true or timeout elapses + DisplayWaiter waiter= new DisplayWaiter(display, true); + long currentTimeMillis= System.currentTimeMillis(); + long finalTimeout= timeout + currentTimeMillis; + if (finalTimeout < currentTimeMillis) + finalTimeout= Long.MAX_VALUE; + boolean condition; + try { + do { + waiter.restart(interval); + if (display.sleep()) + driveEventQueue(display); + condition= condition(); + } while (!condition && (DISABLE_TIMEOUT || finalTimeout > System.currentTimeMillis())); + } finally { + waiter.stop(); + } + return condition; + } + +} + +/** + * Implements the thread that will wait for the timeout and wake up the display + * so it does not wait forever. The thread may be restarted after it was stopped + * or timed out. + */ +final class DisplayWaiter { + /** + * Timeout state of a display waiter thread. + */ + public final class Timeout { + private boolean fTimeoutState= false; + /** + * Returns <code>true</code> if the timeout has been reached, + * <code>false</code> if not. + * + * @return <code>true</code> if the timeout has been reached, + * <code>false</code> if not + */ + public boolean hasTimedOut() { + synchronized (fMutex) { + return fTimeoutState; + } + } + void setTimedOut(boolean timedOut) { + fTimeoutState= timedOut; + } + Timeout(boolean initialState) { + fTimeoutState= initialState; + } + } + + // configuration + private final Display fDisplay; + private final Object fMutex= new Object(); + private final boolean fKeepRunningOnTimeout; + + /* State -- possible transitions: + * + * STOPPED -> RUNNING + * RUNNING -> STOPPED + * RUNNING -> IDLE + * IDLE -> RUNNING + * IDLE -> STOPPED + */ + private static final int RUNNING= 1 << 1; + private static final int STOPPED= 1 << 2; + private static final int IDLE= 1 << 3; + + /** The current state. */ + private int fState; + /** The time in milliseconds (see Date) that the timeout will occur. */ + private long fNextTimeout; + /** The thread. */ + private Thread fCurrentThread; + /** The timeout state of the current thread. */ + private Timeout fCurrentTimeoutState; + + /** + * Creates a new instance on the given display and timeout. + * + * @param display the display to run the event loop of + */ + public DisplayWaiter(Display display) { + this(display, false); + } + + /** + * Creates a new instance on the given display and timeout. + * + * @param display the display to run the event loop of + * @param keepRunning <code>true</code> if the thread should be kept + * running after timing out + */ + public DisplayWaiter(Display display, boolean keepRunning) { + Assert.assertNotNull(display); + fDisplay= display; + fState= STOPPED; + fKeepRunningOnTimeout= keepRunning; + } + + /** + * Starts the timeout thread if it is not currently running. Nothing happens + * if a thread is already running. + * + * @param delay the delay from now in milliseconds + * @return the timeout state which can be queried for its timed out status + */ + public Timeout start(long delay) { + Assert.assertTrue(delay > 0); + synchronized (fMutex) { + switch (fState) { + case STOPPED: + startThread(); + setNextTimeout(delay); + break; + case IDLE: + unhold(); + setNextTimeout(delay); + break; + } + + return fCurrentTimeoutState; + } + } + + /** + * Sets the next timeout to <em>current time</em> plus <code>delay</code>. + * + * @param delay the delay until the next timeout occurs in milliseconds from + * now + */ + private void setNextTimeout(long delay) { + long currentTimeMillis= System.currentTimeMillis(); + long next= currentTimeMillis + delay; + if (next > currentTimeMillis) + fNextTimeout= next; + else + fNextTimeout= Long.MAX_VALUE; + } + + /** + * Starts the thread if it is not currently running; resets the timeout if + * it is. + * + * @param delay the delay from now in milliseconds + * @return the timeout state which can be queried for its timed out status + */ + public Timeout restart(long delay) { + Assert.assertTrue(delay > 0); + synchronized (fMutex) { + switch (fState) { + case STOPPED: + startThread(); + break; + case IDLE: + unhold(); + break; + } + setNextTimeout(delay); + + return fCurrentTimeoutState; + } + } + + /** + * Stops the thread if it is running. If not, nothing happens. Another + * thread may be started by calling {@link #start(long)} or + * {@link #restart(long)}. + */ + public void stop() { + synchronized (fMutex) { + if (tryTransition(RUNNING | IDLE, STOPPED)) + fMutex.notifyAll(); + } + } + + /** + * Puts the reaper thread on hold but does not stop it. It may be restarted + * by calling {@link #start(long)} or {@link #restart(long)}. + */ + public void hold() { + synchronized (fMutex) { + // nothing to do if there is no thread + if (tryTransition(RUNNING, IDLE)) + fMutex.notifyAll(); + } + } + + /** + * Transition to <code>RUNNING</code> and clear the timed out flag. Assume + * current state is <code>IDLE</code>. + */ + private void unhold() { + checkedTransition(IDLE, RUNNING); + fCurrentTimeoutState= new Timeout(false); + fMutex.notifyAll(); + } + + /** + * Start the thread. Assume the current state is <code>STOPPED</code>. + */ + private void startThread() { + checkedTransition(STOPPED, RUNNING); + fCurrentTimeoutState= new Timeout(false); + fCurrentThread= new Thread() { + /** + * Exception thrown when a thread notices that it has been stopped + * and a new thread has been started. + */ + final class ThreadChangedException extends Exception { + private static final long serialVersionUID= 1L; + } + + /* + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try { + run2(); + } catch (InterruptedException e) { + // ignore and end the thread - we never interrupt ourselves, + // so it must be an external entity that interrupted us + Logger.getGlobal().log(Level.FINE, "", e); + } catch (ThreadChangedException e) { + // the thread was stopped and restarted before we got out + // of a wait - we're no longer used + // we might have been notified instead of the current thread, + // so wake it up + Logger.getGlobal().log(Level.FINE, "", e); + synchronized (fMutex) { + fMutex.notifyAll(); + } + } + } + + /** + * Runs the thread. + * + * @throws InterruptedException if the thread was interrupted + * @throws ThreadChangedException if the thread changed + */ + private void run2() throws InterruptedException, ThreadChangedException { + synchronized (fMutex) { + checkThread(); + tryHold(); // wait / potential state change + assertStates(STOPPED | RUNNING); + + while (isState(RUNNING)) { + waitForTimeout(); // wait / potential state change + + if (isState(RUNNING)) + timedOut(); // state change + assertStates(STOPPED | IDLE); + + tryHold(); // wait / potential state change + assertStates(STOPPED | RUNNING); + } + assertStates(STOPPED); + } + } + + /** + * Check whether the current thread is this thread, throw an + * exception otherwise. + * + * @throws ThreadChangedException if the current thread changed + */ + private void checkThread() throws ThreadChangedException { + if (fCurrentThread != this) + throw new ThreadChangedException(); + } + + /** + * Waits until the next timeout occurs. + * + * @throws InterruptedException if the thread was interrupted + * @throws ThreadChangedException if the thread changed + */ + private void waitForTimeout() throws InterruptedException, ThreadChangedException { + long delta; + while (isState(RUNNING) && (delta = fNextTimeout - System.currentTimeMillis()) > 0) { + delta= Math.max(delta, 50); // wait at least 50ms in order to avoid timing out before the display is going to sleep + Logger.getGlobal().finest("sleeping for " + delta + "ms"); + fMutex.wait(delta); + checkThread(); + } + } + + /** + * Sets the timed out flag and wakes up the display. Transitions to + * <code>IDLE</code> (if in keep-running mode) or + * <code>STOPPED</code>. + */ + private void timedOut() { + Logger.getGlobal().finer("timed out"); + fCurrentTimeoutState.setTimedOut(true); + fDisplay.wake(); // wake up call! + if (fKeepRunningOnTimeout) + checkedTransition(RUNNING, IDLE); + else + checkedTransition(RUNNING, STOPPED); + } + + /** + * Waits while the state is <code>IDLE</code>, then returns. The + * state must not be <code>RUNNING</code> when calling this + * method. The state is either <code>STOPPED</code> or + * <code>RUNNING</code> when the method returns. + * + * @throws InterruptedException if the thread was interrupted + * @throws ThreadChangedException if the thread has changed while on + * hold + */ + private void tryHold() throws InterruptedException, ThreadChangedException { + while (isState(IDLE)) { + fMutex.wait(0); + checkThread(); + } + assertStates(STOPPED | RUNNING); + } + }; + + fCurrentThread.start(); + } + + /** + * Transitions to <code>nextState</code> if the current state is one of + * <code>possibleStates</code>. Returns <code>true</code> if the + * transition happened, <code>false</code> otherwise. + * + * @param possibleStates the states which trigger a transition + * @param nextState the state to transition to + * @return <code>true</code> if the transition happened, + * <code>false</code> otherwise + */ + private boolean tryTransition(int possibleStates, int nextState) { + if (isState(possibleStates)) { + Logger.getGlobal().finer(name(fState) + " > " + name(nextState) + " (" + name(possibleStates) + ")"); + fState= nextState; + return true; + } + Logger.getGlobal().finest("noTransition" + name(fState) + " !> " + name(nextState) + " (" + name(possibleStates) + ")"); + return false; + } + + /** + * Checks the <code>possibleStates</code> and throws an assertion if it is + * not met, then transitions to <code>nextState</code>. + * + * @param possibleStates the allowed states + * @param nextState the state to transition to + */ + private void checkedTransition(int possibleStates, int nextState) { + assertStates(possibleStates); + Logger.getGlobal().finer(name(fState) + " > " + name(nextState)); + fState= nextState; + } + + /** + * Implements state consistency checking. + * + * @param states the allowed states + * @throws junit.framework.AssertionFailedError if the current state is not + * in <code>states</code> + */ + private void assertStates(int states) { + Assert.assertTrue("illegal state", isState(states)); + } + + /** + * Answers <code>true</code> if the current state is in the given + * <code>states</code>. + * + * @param states the possible states + * @return <code>true</code> if the current state is in the given states, + * <code>false</code> otherwise + */ + private boolean isState(int states) { + return (states & fState) == fState; + } + + /** + * Pretty print the given states. + * + * @param states the states + * @return a string representation of the states + */ + private String name(int states) { + StringBuffer buf= new StringBuffer(); + boolean comma= false; + if ((states & RUNNING) == RUNNING) { + buf.append("RUNNING"); + comma= true; + } + if ((states & STOPPED) == STOPPED) { + if (comma) + buf.append(","); + buf.append("STOPPED"); + comma= true; + } + if ((states & IDLE) == IDLE) { + if (comma) + buf.append(","); + buf.append("IDLE"); + } + return buf.toString(); + } + +} diff --git a/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF b/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF index cf5e719441c..123f4c261a7 100644 --- a/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF +++ b/org.eclipse.ui.genericeditor.tests/META-INF/MANIFEST.MF @@ -17,8 +17,9 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.6.0,4.0.0)", org.eclipse.ui;bundle-version="3.108.0", org.eclipse.ui.workbench.texteditor;bundle-version="3.10.0", org.eclipse.ui.ide;bundle-version="3.11.0", + org.eclipse.jface.text.tests;bundle-version="3.11.100", org.eclipse.text.tests;bundle-version="3.11.0", - org.eclipse.ui.tests.harness + org.eclipse.ui.workbench.texteditor.tests;bundle-version="3.11.100" Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Eclipse-BundleShape: dir Bundle-ActivationPolicy: lazy diff --git a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java index 4a6a394b4af..f130f4d236c 100644 --- a/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java +++ b/org.eclipse.ui.genericeditor.tests/src/org/eclipse/ui/genericeditor/tests/HoverTest.java @@ -14,6 +14,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.Collections; @@ -21,7 +22,9 @@ import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyledText; @@ -41,12 +44,14 @@ import org.eclipse.jface.text.AbstractInformationControl; import org.eclipse.jface.text.AbstractInformationControlManager; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.TextViewer; +import org.eclipse.jface.text.tests.util.DisplayHelper; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.genericeditor.tests.contributions.MagicHoverProvider; import org.eclipse.ui.genericeditor.tests.contributions.MarkerResolutionGenerator; import org.eclipse.ui.part.FileEditorInput; -import org.eclipse.ui.tests.harness.util.DisplayHelper; + +import org.eclipse.ui.workbench.texteditor.tests.ScreenshotTest; import org.eclipse.ui.texteditor.AbstractTextEditor; @@ -56,6 +61,9 @@ import org.eclipse.ui.texteditor.AbstractTextEditor; */ public class HoverTest { + @Rule + public TestName testName = new TestName(); + private AbstractTextEditor editor; @BeforeClass @@ -121,12 +129,19 @@ public class HoverTest { } private Shell getHoverShell(AbstractInformationControlManager manager) { - AbstractInformationControl control = null; - do { - DisplayHelper.runEventLoop(this.editor.getSite().getShell().getDisplay(), 100); - control = (AbstractInformationControl)new Accessor(manager, AbstractInformationControlManager.class).get("fInformationControl"); - } while (control == null); - Shell shell = (Shell)new Accessor(control, AbstractInformationControl.class).get("fShell"); + AbstractInformationControl[] control = { null }; + new DisplayHelper() { + @Override + protected boolean condition() { + control[0] = (AbstractInformationControl)new Accessor(manager, AbstractInformationControlManager.class).get("fInformationControl"); + return control[0] != null; + } + }.waitForCondition(this.editor.getSite().getShell().getDisplay(), 5000); + if (control[0] == null) { + ScreenshotTest.takeScreenshot(getClass(), testName.getMethodName(), System.out); + fail(); + } + Shell shell = (Shell)new Accessor(control[0], AbstractInformationControl.class).get("fShell"); assertTrue(shell.isVisible()); return shell; } @@ -190,7 +205,7 @@ public class HoverTest { editorTextWidget.getDisplay().setCursorLocation(editorTextWidget.toDisplay(hoverEvent.x, hoverEvent.y)); editorTextWidget.notifyListeners(SWT.MouseHover, hoverEvent); // Events need to be processed for hover listener to work correctly - DisplayHelper.runEventLoop(editorTextWidget.getDisplay(), 1000); + DisplayHelper.sleep(editorTextWidget.getDisplay(), 1000); // retrieving hover content ITextViewer viewer = (ITextViewer)new Accessor(editor, AbstractTextEditor.class).invoke("getSourceViewer", new Object[0]); AbstractInformationControlManager textHoverManager = (AbstractInformationControlManager)new Accessor(viewer, TextViewer.class).get("fTextHoverManager"); diff --git a/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/ScreenshotTest.java b/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/ScreenshotTest.java index 1e8a2bb3468..6978d329b03 100644 --- a/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/ScreenshotTest.java +++ b/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/workbench/texteditor/tests/ScreenshotTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2012, 2015 IBM Corporation and others. + * Copyright (c) 2012, 2017 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 @@ -86,6 +86,15 @@ public class ScreenshotTest { /** * Takes a screenshot and dumps other debugging information to the given stream. * + * <p> + * Workaround for missing {@link junit.framework.TestCase#getName()} in JUnit 4: + * </p> + * + * <pre> + * @Rule + * public TestName testName = new TestName(); + * </pre> + * * @param testClass test class that takes the screenshot * @param name screenshot identifier (e.g. test name) * @param out print stream to use for diagnostics. @@ -104,40 +113,8 @@ public class ScreenshotTest { Display display= PlatformUI.getWorkbench().getDisplay(); - // Wiggle the mouse: - Event mouseMove= new Event(); - mouseMove.x= 10; - mouseMove.y= 10; - display.post(mouseMove); - runEventQueue(); - mouseMove.x= 20; - mouseMove.y= 20; - display.post(mouseMove); - runEventQueue(); - // Dump focus control, parents, and shells: - Control focusControl = display.getFocusControl(); - out.println("FocusControl: "); - if (focusControl == null) { - System.out.println(" null!"); - } else { - StringBuffer indent = new StringBuffer(" "); - do { - out.println(indent.toString() + focusControl); - focusControl = focusControl.getParent(); - indent.append(" "); - } while (focusControl != null); - } - Shell[] shells = display.getShells(); - if (shells.length > 0) { - out.println("Shells: "); - for (int i = 0; i < shells.length; i++) { - Shell shell = shells[i]; - out.print(display.getActiveShell() == shell ? " active, " : " inactive, "); - out.print((shell.isVisible() ? "visible: " : "invisible: ") + shell); - out.println(" @ " + shell.getBounds().toString()); - } - } + dumpDisplayState(display, System.out); // Take a screenshot: GC gc = new GC(display); @@ -159,6 +136,42 @@ public class ScreenshotTest { return filename; } + private static void dumpDisplayState(Display display, PrintStream out) { + Control focusControl= display.getFocusControl(); + dumpControlParentHierarchy(out, "FocusControl", focusControl); + Shell[] shells= display.getShells(); + if (shells.length > 0) { + out.println("Shells: "); + for (int i= 0; i < shells.length; i++) { + Shell shell= shells[i]; + out.print(display.getActiveShell() == shell ? " active, " : " inactive, "); + out.print((shell.isVisible() ? "visible: " : "invisible: ") + shell); + out.println(" @ " + shell.getBounds().toString()); + } + } + out.println("Client area: " + display.getClientArea()); + out.println("Cursor location: " + display.getCursorLocation()); + Control cursorControl= display.getCursorControl(); + if (focusControl != cursorControl) { + dumpControlParentHierarchy(out, "CursorControl", cursorControl); + } + } + + private static void dumpControlParentHierarchy(PrintStream out, String label, Control control) { + out.print(label + ": "); + if (control == null) { + out.println("<none>"); + } else { + out.println(); + StringBuilder indent= new StringBuilder(" "); + do { + out.println(indent.toString() + control); + control= control.getParent(); + indent.append(" "); + } while (control != null); + } + } + private static File getJunitReportOutput() { String[] args= Platform.getCommandLineArgs(); for (int i= 0; i < args.length - 1; i++) { |