/******************************************************************************* * Copyright (c) 2012-2016 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.springsource.ide.eclipse.commons.frameworks.test.util; import org.eclipse.core.runtime.jobs.IJobManager; import org.eclipse.core.runtime.jobs.Job; import org.springsource.ide.eclipse.commons.tests.util.StsTestUtil; import junit.framework.AssertionFailedError; /** * Abstract class to create a "waitable" condition. This is to be used in implementing * tests where we want some condition to be true eventually, but cannot be sure it will be * true immediately. * <p> * Typical use would like so: * <code> * new ACondition() { * public boolean test() throws Exception { * ... some asserts ... * return ...something to test...; * } * }.waitFor(4000); // wait for 4 seconds or until test passes. * </code> * An exception will be thrown if the test method does not return true before the * timeout limit. * <p> * If the test method throws an exception this is treated the same as returning false. * If condition fails, we will try to rethrow a pertinent exception (typically the * exception thrown by the test method, the last time we tried to run it. * * @author Kris De Volder */ public abstract class ACondition { private String description = null; /** * This method is deprecated because it is a common problem that client calls it * and then forgets to call the waitFor method on the created instance. This results * in test code that looks like it is testing something but doesn't because the * asserts inside the ACondition are never actually executed. * <p> * Clients should instead call one of the constructor that takes a timeout * as a parameter (which does the to waitFor automatically). */ @Deprecated public ACondition() { } /** * This method is deprecated because it is a common problem that client calls it * and then forgets to call the waitFor method on the created instance. This results * in test code that looks like it is testing something but doesn't because the * asserts inside the ACondition are never actually executed. * <p> * Clients should instead call one of the constructor that takes a timeout * as a parameter (which does the to waitFor automatically). */ @Deprecated public ACondition(String description) { this.description = description; } public ACondition(long timeout) throws Exception { this(); waitFor(timeout); } public ACondition(String description, long timeout) throws Exception { this(description); waitFor(timeout); } Throwable e = null; /** * Deprecated. Use one of the constructors that accepts a timeout value and automatically * calls waitFor. */ @Deprecated public void waitFor(long timeout) throws Exception { long SLEEP_FOR = Math.min(100, timeout/5); long startTime = System.currentTimeMillis(); long endTime = startTime + timeout; boolean result = false; StsTestUtil.waitForDisplay(); //Make sure this gets called at least once to avoid 'UI thread starvation'. while (!(result = doTest()) && System.currentTimeMillis() < endTime) { StsTestUtil.waitForDisplay(); // Avoids UI deadlock by allowing UI to process events. try { Thread.sleep(SLEEP_FOR); } catch (InterruptedException e) { } } if (!result) { //Try our best to create a 'nice' exception reflecting the reason for the test failure System.err.println("ACondition "+describe()+" timed out. Dumping current Thread stacks...\n" + StsTestUtil.getStackDumps() ); if (e!=null) { if (e instanceof Exception) { throw (Exception)e; } else { throw new Error(e); } } else { throw new Error(getMessage()); } } if (description!=null) { System.out.println(description + " succeeded after: " + (System.currentTimeMillis() - startTime)); } } private String describe() { if (description!=null) { return "["+description+"]"; } return ""; } private boolean doTest() { boolean result = false; try { e = null; result = test(); } catch (Throwable e) { this.e = e; } return result; } /** * Test something. If the method returns true, the test passes. * If it returns false or throws an exception the test fails (and will be * retried until it passes or timeout is reached). */ public abstract boolean test() throws Exception; /** * Message used when time out reached without an exception */ public String getMessage() { return "timed out"; } public static void assertJobManagerIdle() { final IJobManager jm = Job.getJobManager(); if (jm.isIdle()) { return; //OK! } //Make a nice message listing all the jobs and their present state. Job[] allJobs = jm.find(null); StringBuffer msg = new StringBuffer("JobManager not idle: \n"); for (Job job : allJobs) { msg.append(" Job: "+job.getName() + " State: " + stateString(job) +"\n"); } throw new AssertionFailedError(msg.toString()); } public static String stateString(Job job) { int state = job.getState(); switch (state) { case Job.RUNNING: return "RUNNING"; case Job.SLEEPING: return "SLEEPING"; case Job.WAITING: return "WAITING"; case Job.NONE: return "NONE"; default: return ""+state; } } /** * A Java 8 friendlier way of using this class with a lambda. * <p> * Waits for some asserts to pass by periodically checking them until either: * a) the asserts all pass (i.e. asserter returns without throwing); or b) * the timeout is exceeded. */ public static void waitFor(String name, long timeout, final Asserter asserter) throws Exception { new ACondition("Wait for "+name, timeout) { @Override public boolean test() throws Exception { asserter.execute(); return true; } }; } }