You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/01/30 04:38:46 UTC

[21/29] incubator-brooklyn git commit: fix failing (time sensitive) test

fix failing (time sensitive) test

and improve the "Asserts.eventually" routines which that used,
adding a new simpler eventuallyOnNotify(...) and having it used elsewhere.

also add convenience methods for CountdownTimer so its usage is more readable.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/dc0ca058
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/dc0ca058
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/dc0ca058

Branch: refs/heads/master
Commit: dc0ca058d848e1231cf4c05a603b8a74fd179903
Parents: 9d5ba14
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Fri Jan 29 23:30:07 2016 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Sat Jan 30 00:26:17 2016 +0000

----------------------------------------------------------------------
 .../core/task/DynamicSequentialTaskTest.java    |  32 +---
 .../software/base/SoftwareProcessImpl.java      |   2 +-
 .../AutoScalerPolicyNoMoreMachinesTest.java     |  17 +-
 .../SystemServiceEnricherTest.java              |   4 +-
 .../org/apache/brooklyn/test/WebAppMonitor.java |   2 +-
 .../java/org/apache/brooklyn/test/Asserts.java  | 183 +++++++++++++++----
 .../brooklyn/util/time/CountdownTimer.java      |  23 ++-
 .../brooklyn/util/time/CountdownTimerTest.java  |   7 +
 8 files changed, 190 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
index 763c067..c5664a6 100644
--- a/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
+++ b/brooklyn-server/core/src/test/java/org/apache/brooklyn/util/core/task/DynamicSequentialTaskTest.java
@@ -26,7 +26,6 @@ import java.util.Map;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
 import org.apache.brooklyn.api.mgmt.HasTaskChildren;
 import org.apache.brooklyn.api.mgmt.Task;
@@ -39,7 +38,6 @@ import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.core.task.TaskInternal.TaskCancellationMode;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.math.MathPredicates;
-import org.apache.brooklyn.util.time.CountdownTimer;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
@@ -64,7 +62,7 @@ public class DynamicSequentialTaskTest {
     private static final Logger log = LoggerFactory.getLogger(DynamicSequentialTaskTest.class);
     
     public static final Duration TIMEOUT = Duration.TEN_SECONDS;
-    public static final Duration TINY_TIME = Duration.millis(20);
+    public static final Duration TINY_TIME = Duration.millis(1);
     
     BasicExecutionManager em;
     BasicExecutionContext ec;
@@ -246,6 +244,8 @@ public class DynamicSequentialTaskTest {
     
     @Test
     public void testCancellationModeAndSubmitted() throws Exception {
+        // seems actually to be the logging which causes this to take ~50ms ?
+        
         doTestCancellationModeAndSubmitted(true, TaskCancellationMode.DO_NOT_INTERRUPT, false, false);
         
         doTestCancellationModeAndSubmitted(true, TaskCancellationMode.INTERRUPT_TASK_AND_ALL_SUBMITTED_TASKS, true, true);
@@ -317,27 +317,14 @@ public class DynamicSequentialTaskTest {
                 @Override public Number get() { return t1.getEndTimeUtc(); }}, 
                 MathPredicates.<Number>greaterThanOrEqual(0));
         } else {
-            Time.sleep(Duration.millis(5));
+            Time.sleep(TINY_TIME);
             Assert.assertFalse(t1.isCancelled());
             Assert.assertFalse(t1.isDone());
         }
     }
 
     protected void waitForMessages(Predicate<? super List<String>> predicate, Duration timeout) throws Exception {
-        long endtime = System.currentTimeMillis() + timeout.toMilliseconds();
-        synchronized (messages) {
-            while (true) {
-                if (predicate.apply(messages)) {
-                    return;
-                }
-                long waittime = endtime - System.currentTimeMillis();
-                if (waittime > 0) {
-                    messages.wait(waittime);
-                } else {
-                    throw new TimeoutException("Timeout after "+timeout+"; messages="+messages+"; predicate="+predicate);
-                }
-            }
-        }
+        Asserts.eventuallyOnNotify(messages, predicate, timeout);
     }
     
     protected Task<String> monitorableTask(final String id) {
@@ -373,14 +360,7 @@ public class DynamicSequentialTaskTest {
         monitorableJobSemaphoreMap.get(id).release();
     }
     protected void waitForMessage(final String id) {
-        CountdownTimer timer = CountdownTimer.newInstanceStarted(TIMEOUT);
-        synchronized (messages) {
-            while (!timer.isExpired()) {
-                if (messages.contains(id)) return;
-                timer.waitOnForExpiryUnchecked(messages);
-            }
-        }
-        Assert.fail("Did not see message "+id);
+        Asserts.eventuallyOnNotify(messages, CollectionFunctionals.contains(id), TIMEOUT);
     }
     protected void releaseAndWaitForMonitorableJob(final String id) {
         releaseMonitorableJob(id);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
index c62cc3d..ace4ebe 100644
--- a/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
+++ b/brooklyn-server/software/base/src/main/java/org/apache/brooklyn/entity/software/base/SoftwareProcessImpl.java
@@ -548,7 +548,7 @@ public abstract class SoftwareProcessImpl extends AbstractEntity implements Soft
         boolean isRunningResult = false;
         long delay = 100;
         Exception firstFailure = null;
-        while (!isRunningResult && !timer.isExpired()) {
+        while (!isRunningResult && timer.isNotExpired()) {
             Time.sleep(delay);
             try {
                 isRunningResult = driver.isRunning();

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
index 77175d2..e3dfb12 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/software/base/test/autoscaling/AutoScalerPolicyNoMoreMachinesTest.java
@@ -38,6 +38,7 @@ import org.apache.brooklyn.entity.group.DynamicCluster;
 import org.apache.brooklyn.entity.software.base.EmptySoftwareProcess;
 import org.apache.brooklyn.policy.autoscaling.AutoScalerPolicy;
 import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.CollectionFunctionals;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -75,12 +76,14 @@ public class AutoScalerPolicyNoMoreMachinesTest extends BrooklynAppUnitTestSuppo
         entitiesAdded = Sets.newLinkedHashSet();
         entitiesRemoved = Sets.newLinkedHashSet();
         mgmt.addEntitySetListener(new CollectionChangeListener<Entity>() {
-            @Override public void onItemAdded(Entity item) {
-                entitiesAdded.add(item);
-            }
-            @Override public void onItemRemoved(Entity item) {
-                entitiesRemoved.add(item);
-            }});
+            @Override public void onItemAdded(Entity item) { addToSetAndNotify(entitiesAdded, item); }
+            @Override public void onItemRemoved(Entity item) { addToSetAndNotify(entitiesRemoved, item); }});
+    }
+    private static <T> void addToSetAndNotify(Set<T> items, T item) {
+        synchronized (items) {
+            items.add(item);
+            items.notifyAll();
+        }
     }
 
     @Test
@@ -179,7 +182,7 @@ public class AutoScalerPolicyNoMoreMachinesTest extends BrooklynAppUnitTestSuppo
 
     protected void assertSize(int targetSize, int quarantineSize, final int deletedSize) {
         assertSize(targetSize, quarantineSize);
-        assertEquals(entitiesRemoved.size(), deletedSize, "removed="+entitiesRemoved);
+        Asserts.eventuallyOnNotify(entitiesRemoved, CollectionFunctionals.sizeEquals(deletedSize));
     }
     
     protected void assertSize(int targetSize, int quarantineSize) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java
index 10e2e15..70c0da5 100644
--- a/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java
+++ b/brooklyn-server/software/base/src/test/java/org/apache/brooklyn/entity/system_service/SystemServiceEnricherTest.java
@@ -86,10 +86,10 @@ public class SystemServiceEnricherTest extends BrooklynAppLiveTestSupport {
     }
 
     private void waitFailed(VanillaSoftwareProcess proc) {
-        Asserts.eventually(ImmutableMap.of("timeout", Duration.FIVE_MINUTES), Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE));
+        Asserts.eventually(Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE), Duration.FIVE_MINUTES);
     }
 
     private void waitHealthy(VanillaSoftwareProcess proc) {
-        Asserts.eventually(ImmutableMap.of("timeout", Duration.FIVE_MINUTES), Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING));
+        Asserts.eventually(Suppliers.ofInstance(proc), EntityPredicates.attributeEqualTo(Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING), Duration.FIVE_MINUTES);
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java b/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java
index d804312..b957a5b 100644
--- a/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java
+++ b/brooklyn-server/test-support/src/main/java/org/apache/brooklyn/test/WebAppMonitor.java
@@ -184,7 +184,7 @@ public class WebAppMonitor implements Runnable {
         return this;
     }
     public WebAppMonitor waitForAtLeastOneAttempt() {
-        return waitForAtLeastOneAttempt(Asserts.DEFAULT_TIMEOUT);
+        return waitForAtLeastOneAttempt(Asserts.DEFAULT_LONG_TIMEOUT);
     }
     public WebAppMonitor waitForAtLeastOneAttempt(Duration timeout) {
         Asserts.succeedsEventually(MutableMap.of("timeout", timeout), new Runnable() {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
index 15aa76e..fac3142 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
@@ -37,7 +37,9 @@ import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableSet;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.javalang.JavaClassNames;
+import org.apache.brooklyn.util.repeat.Repeater;
 import org.apache.brooklyn.util.text.StringPredicates;
+import org.apache.brooklyn.util.time.CountdownTimer;
 import org.apache.brooklyn.util.time.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,6 +48,7 @@ import com.google.common.annotations.Beta;
 import com.google.common.base.Predicate;
 import com.google.common.base.Predicates;
 import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
@@ -68,11 +71,24 @@ import com.google.common.collect.Sets;
 @Beta
 public class Asserts {
 
-    /**
-     * The default timeout for assertions - 30s.
-     * Alter in individual tests by giving a "timeout" entry in method flags.
+    /** 
+     * Timeout for use when something should happen within several seconds,
+     * but there might be network calls or computation so {@link #DEFAULT_SHORT_TIMEOUT} is not applicable.
      */
-    public static final Duration DEFAULT_TIMEOUT = Duration.THIRTY_SECONDS;
+    public static final Duration DEFAULT_LONG_TIMEOUT = Duration.THIRTY_SECONDS;
+    
+    /** 
+     * Timeout for use when waiting for other threads to finish.
+     * <p>
+     * Long enough for parallel execution to catch up, 
+     * even on overloaded mediocre test boxes most of the time,
+     * but short enough not to irritate you when your test is failing. */
+    public static final Duration DEFAULT_SHORT_TIMEOUT = Duration.ONE_SECOND;
+    
+    /** @deprecated since 0.9.0 use {@link #DEFAULT_LONG_TIMEOUT} */ @Deprecated
+    public static final Duration DEFAULT_TIMEOUT = DEFAULT_LONG_TIMEOUT;
+    
+    private static final Duration DEFAULT_SHORT_PERIOD = Repeater.DEFAULT_REAL_QUICK_PERIOD;
 
     private static final Logger log = LoggerFactory.getLogger(Asserts.class);
 
@@ -730,58 +746,99 @@ public class Asserts {
 
     // --- new routines
     
+    /**  As {@link #eventually(Supplier, Predicate, Duration, Duration, String)} with defaults. */
     public static <T> void eventually(Supplier<? extends T> supplier, Predicate<T> predicate) {
-        eventually(ImmutableMap.<String,Object>of(), supplier, predicate);
+        eventually(supplier, predicate, null, null, null);
     }
     
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void eventually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<T> predicate) {
         eventually(flags, supplier, predicate, (String)null);
     }
-    
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void eventually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<T> predicate, String errMsg) {
-        Duration timeout = toDuration(flags.get("timeout"), Duration.ONE_SECOND);
-        Duration period = toDuration(flags.get("period"), Duration.millis(10));
-        long periodMs = period.toMilliseconds();
-        long startTime = System.currentTimeMillis();
-        long expireTime = startTime+timeout.toMilliseconds();
+        eventually(supplier, predicate, toDuration(flags.get("timeout"), null), toDuration(flags.get("period"), null), errMsg);
+    }
+    
+    /**  As {@link #eventually(Supplier, Predicate, Duration, Duration, String)} with default. */
+    public static <T> void eventually(Supplier<? extends T> supplier, Predicate<T> predicate, Duration timeout) {
+        eventually(supplier, predicate, timeout, null, null);
+    }
+    
+    /** Asserts that eventually the supplier gives a value accepted by the predicate. 
+     * Tests periodically and succeeds as soon as the supplier gives an allowed value.
+     * Other arguments can be null.
+     * 
+     * @param supplier supplies the value to test, such as {@link Suppliers#ofInstance(Object)} for a constant 
+     * @param predicate the {@link Predicate} to apply to each value given by the supplier
+     * @param timeout how long to wait, default {@link #DEFAULT_SHORT_TIMEOUT}
+     * @param period how often to check, default quite often so you won't notice but letting the CPU do work
+     * @param errMsg an error message to display if not satisfied, in addition to the last-tested supplied value and the predicate
+     */
+    public static <T> void eventually(Supplier<? extends T> supplier, Predicate<T> predicate, Duration timeout, Duration period, String errMsg) {
+        if (timeout==null) timeout = DEFAULT_SHORT_TIMEOUT;
+        if (period==null) period = DEFAULT_SHORT_PERIOD;
+        CountdownTimer timeleft = timeout.countdownTimer();
         
-        boolean first = true;
-        T supplied = supplier.get();
-        while (first || System.currentTimeMillis() <= expireTime) {
+        T supplied;
+        int count = 0;
+        do {
+            if (count++ > 0) Duration.sleep(period);
             supplied = supplier.get();
-            if (predicate.apply(supplied)) {
-                return;
-            }
-            first = false;
-            if (periodMs > 0) sleep(periodMs);
-        }
-        fail("supplied="+supplied+"; predicate="+predicate+(errMsg!=null?"; "+errMsg:""));
+            if (predicate.apply(supplied)) return;
+        } while (timeleft.isNotExpired());
+
+        fail("Expected: eventually "+predicate+"; got most recently: "+supplied
+            +" (waited "+timeleft.getDurationElapsed()+", checked "+count+")"
+            +(errMsg!=null?"; "+errMsg:""));
     }
     
-    // TODO improve here -- these methods aren't very useful without timeouts
+    /**  As {@link #continually(Supplier, Predicate, Duration, Duration, String)} with defaults. */
     public static <T> void continually(Supplier<? extends T> supplier, Predicate<T> predicate) {
         continually(ImmutableMap.<String,Object>of(), supplier, predicate);
     }
 
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void continually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<? super T> predicate) {
-        continually(flags, supplier, predicate, (String)null);
+        continually(flags, supplier, predicate, null);
     }
 
+    /** @deprecated since 0.9.0 use {@link #eventually(Supplier, Predicate, Duration, Duration, String)} */ @Deprecated
     public static <T> void continually(Map<String,?> flags, Supplier<? extends T> supplier, Predicate<T> predicate, String errMsg) {
-        Duration duration = toDuration(flags.get("timeout"), Duration.ONE_SECOND);
-        Duration period = toDuration(flags.get("period"), Duration.millis(10));
-        long periodMs = period.toMilliseconds();
-        long startTime = System.currentTimeMillis();
-        long expireTime = startTime+duration.toMilliseconds();
-        
-        boolean first = true;
-        while (first || System.currentTimeMillis() <= expireTime) {
-            assertTrue(predicate.apply(supplier.get()), "supplied="+supplier.get()+"; predicate="+predicate+(errMsg!=null?"; "+errMsg:""));
-            if (periodMs > 0) sleep(periodMs);
-            first = false;
-        }
+        continually(supplier, predicate, toDuration(flags.get("timeout"), toDuration(flags.get("duration"), null)), 
+            toDuration(flags.get("period"), null), null);
     }
+    /** 
+     * Asserts that continually the supplier gives a value accepted by the predicate. 
+     * Tests periodically and fails if the supplier gives a disallowed value.
+     * Other arguments can be null.
+     * 
+     * @param supplier supplies the value to test, such as {@link Suppliers#ofInstance(Object)} for a constant 
+     * @param predicate the {@link Predicate} to apply to each value given by the supplier
+     * @param duration how long to test for, default {@link #DEFAULT_SHORT_TIMEOUT}
+     * @param period how often to check, default quite often to minimise chance of missing a flashing violation but letting the CPU do work
+     * @param errMsg an error message to display if not satisfied, in addition to the last-tested supplied value and the predicate
+     */
+    public static <T> void continually(Supplier<? extends T> supplier, Predicate<T> predicate, Duration duration, Duration period, String errMsg) {
+        if (duration==null) duration = DEFAULT_SHORT_TIMEOUT;
+        if (period==null) period = DEFAULT_SHORT_PERIOD;
 
+        CountdownTimer timeleft = duration.countdownTimer();
+        
+        T supplied;
+        int count = 0;
+        do {
+            if (count > 0) Duration.sleep(period);
+            supplied = supplier.get();
+            if (!predicate.apply(supplied)) {
+                fail("Expected: continually "+predicate+"; got violation: "+supplied
+                    // tell timing if it worked the first time and then failed
+                    +(count > 0 ? " (after "+timeleft.getDurationElapsed()+", successfully checked "+count+")" : "")
+                    +(errMsg!=null?"; "+errMsg:""));
+            }
+            count++;
+        } while (timeleft.isNotExpired());
+    }
     
     /**
      * @see #succeedsContinually(Map, Callable)
@@ -818,6 +875,9 @@ public class Asserts {
         }
     }
 
+    // TODO flags are ugly; remove this in favour of something strongly typed,
+    // e.g. extending Repeater and taking the extra semantics.
+    // TODO remove the #succeedsEventually in favour of #eventually (and same for continually)
     /**
      * Convenience method for cases where we need to test until something is true.
      *
@@ -827,7 +887,7 @@ public class Asserts {
      * <ul>
      * <li>abortOnError (boolean, default true)
      * <li>abortOnException - (boolean, default false)
-     * <li>timeout - (a Duration or an integer in millis, defaults to {@link Asserts#DEFAULT_TIMEOUT})
+     * <li>timeout - (a Duration or an integer in millis, defaults to {@link Asserts#DEFAULT_LONG_TIMEOUT})
      * <li>period - (a Duration or an integer in millis, for fixed retry time; if not set, defaults to exponentially increasing from 1 to 500ms)
      * <li>minPeriod - (a Duration or an integer in millis; only used if period not explicitly set; the minimum period when exponentially increasing; defaults to 1ms)
      * <li>maxPeriod - (a Duration or an integer in millis; only used if period not explicitly set; the maximum period when exponentially increasing; defaults to 500ms)
@@ -849,7 +909,7 @@ public class Asserts {
         boolean logException = get(flags, "logException", true);
 
         // To speed up tests, default is for the period to start small and increase...
-        Duration duration = toDuration(flags.get("timeout"), DEFAULT_TIMEOUT);
+        Duration duration = toDuration(flags.get("timeout"), DEFAULT_LONG_TIMEOUT);
         Duration fixedPeriod = toDuration(flags.get("period"), null);
         Duration minPeriod = (fixedPeriod != null) ? fixedPeriod : toDuration(flags.get("minPeriod"), Duration.millis(1));
         Duration maxPeriod = (fixedPeriod != null) ? fixedPeriod : toDuration(flags.get("maxPeriod"), Duration.millis(500));
@@ -915,7 +975,8 @@ public class Asserts {
     public static <T> T succeedsContinually(Callable<T> c) {
         return succeedsContinually(ImmutableMap.<String,Object>of(), c);
     }
-    
+
+    // TODO unify with "continually"; see also eventually, some of those options might be useful
     public static <T> T succeedsContinually(Map<?,?> flags, Callable<T> job) {
         Duration duration = toDuration(flags.get("timeout"), Duration.ONE_SECOND);
         Duration period = toDuration(flags.get("period"), Duration.millis(10));
@@ -1240,4 +1301,50 @@ public class Asserts {
         throw new RuntimeException(t);
     }
 
+    /** As {@link #eventuallyOnNotify(Object, Supplier, Predicate, Duration, boolean)} with default timeout. */
+    public static <T> void eventuallyOnNotify(Object notifyTarget, Supplier<T> supplier, Predicate<T> predicate) {
+        eventuallyOnNotify(notifyTarget, supplier, predicate, null);
+    }
+    
+    /** as {@link #eventually(Supplier, Predicate)} for cases where an object is notified;
+     * more efficient as it waits on the notify target object. 
+     * See also the simpler {@link #eventuallyOnNotify(Object, Predicate)} when looking at a collection which is getting notified.
+     * Timeout defaults to {@link #DEFAULT_SHORT_TIMEOUT}. 
+     * <p>
+     * This synchronizes on the notify target for the duration of the wait, 
+     * including while getting and checking the value, so as not to miss any notification. */
+    public static <T> void eventuallyOnNotify(Object notifyTarget, Supplier<T> supplier, Predicate<T> predicate, Duration timeout) {
+        T supplied = null;
+        if (timeout==null) timeout = DEFAULT_SHORT_TIMEOUT;
+        CountdownTimer remaining = timeout.countdownTimer();
+        int checks = 0;
+        synchronized (notifyTarget) {
+            do {
+                if (checks>0) {
+                    remaining.waitOnForExpiryUnchecked(notifyTarget);
+                }
+                supplied = supplier.get();
+                if (predicate.apply(supplied)) return;
+                checks++;
+            } while (remaining.isNotExpired());
+        }
+        
+        // should get 2 checks, 1 before and 1 after, if no notifications; if more, tell the user
+        fail("Expected: eventually "+predicate+"; got most recently: "+supplied+
+            " (waited "+remaining.getDurationElapsed()+
+                (checks>2 ? "; notification count "+(checks-2) : "")+
+            ")");
+    }
+
+    /** Convenience for {@link #eventuallyOnNotify(Object, Supplier, Predicate, Duration, boolean)} 
+     * when the notify target and the value under test are the same. */
+    public static <T> void eventuallyOnNotify(T object, Predicate<T> predicate, Duration timeout) {
+        eventuallyOnNotify(object, Suppliers.ofInstance(object), predicate, timeout);
+    }
+
+    /** As {@link #eventuallyOnNotify(Object, Predicate, Duration)} with the default duration of {@link #eventuallyOnNotify(Object, Supplier, Predicate)}. */
+    public static <T> void eventuallyOnNotify(T object, Predicate<T> predicate) {
+        eventuallyOnNotify(object, Suppliers.ofInstance(object), predicate, null);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java
index 508657d..fea8ee6 100644
--- a/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java
+++ b/brooklyn-server/utils/common/src/main/java/org/apache/brooklyn/util/time/CountdownTimer.java
@@ -55,7 +55,7 @@ public class CountdownTimer {
         return limit;
     }
 
-    /** return how long the timer has been running (longer than limit if {@link #isExpired()}) */
+    /** return how long the timer has been running (may be longer than {@link #getLimit()} if {@link #isExpired()}) */
     public Duration getDurationElapsed() {
         return Duration.nanos(stopwatch.elapsed(TimeUnit.NANOSECONDS));
     }
@@ -65,15 +65,28 @@ public class CountdownTimer {
         return Duration.millis(limit.toMilliseconds() - stopwatch.elapsed(TimeUnit.MILLISECONDS));
     }
 
-    /** true iff the timer has been running for the duration specified at creation time */
+    /** true iff the timer has run for more than the duration specified at creation time */
     public boolean isExpired() {
         return stopwatch.elapsed(TimeUnit.MILLISECONDS) > limit.toMilliseconds();
     }
-    
-    /** true iff timer is running (even if it is expired) */
-    public boolean isRunning() {
+
+    /** true iff {@link #isNotPaused()} and not {@link #isExpired()} */
+    public boolean isLive() {
+        return isNotPaused() && isNotExpired();
+    }
+
+    /** true iff not {@link #isExpired()} */
+    public boolean isNotExpired() {
+        return !isExpired();
+    }
+
+    /** false if started or paused, true otherwise (ie the timer is counting down, even if it is expired) */
+    public boolean isNotPaused() {
         return stopwatch.isRunning();
     }
+
+    /** @deprecated since 0.9.0 use better named {@link #isNotPaused()} */ @Deprecated
+    public boolean isRunning() { return isNotPaused(); }
     
     // --- constructor methods
     

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/dc0ca058/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java
----------------------------------------------------------------------
diff --git a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java
index 5541caa..45995da 100644
--- a/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java
+++ b/brooklyn-server/utils/common/src/test/java/org/apache/brooklyn/util/time/CountdownTimerTest.java
@@ -50,6 +50,9 @@ public class CountdownTimerTest {
         
         CountdownTimer timer = SIMPLE_DURATION.countdownTimer();
         assertFalse(timer.isExpired());
+        assertTrue(timer.isNotExpired());
+        assertTrue(timer.isLive());
+        assertTrue(timer.isNotPaused());
         assertTrue(timer.getDurationElapsed().toMilliseconds() <= OVERHEAD_MS, "elapsed="+timer.getDurationElapsed().toMilliseconds());
         assertTrue(timer.getDurationRemaining().toMilliseconds() >= TOTAL_TIME_MS - OVERHEAD_MS, "remaining="+timer.getDurationElapsed().toMilliseconds());
         
@@ -60,6 +63,10 @@ public class CountdownTimerTest {
         
         Time.sleep(Duration.millis(SECOND_SLEEP_TIME_MS));
         assertTrue(timer.isExpired());
+        assertFalse(timer.isNotExpired());
+        assertFalse(timer.isLive());
+        assertTrue(timer.isNotPaused());
+
     }
     
     public void testNotify() throws InterruptedException {