Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Keller2017-03-08 17:27:39 +0000
committerMarkus Keller2017-03-08 17:27:39 +0000
commitdad356880b62128e58665dd3e641e51ab61e03c0 (patch)
treee12b8e459073947b0a6b22293a142b966501b994 /org.eclipse.jface.text.tests
parent5985a1edc5adad4a81892f9229e3269c8b25e794 (diff)
downloadeclipse.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
Diffstat (limited to 'org.eclipse.jface.text.tests')
-rw-r--r--org.eclipse.jface.text.tests/META-INF/MANIFEST.MF3
-rw-r--r--org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/util/DisplayHelper.java581
2 files changed, 583 insertions, 1 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 &lt; 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 &lt; 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 &lt; 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();
+ }
+
+}

Back to the top