You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by dr...@apache.org on 2017/04/10 11:43:50 UTC

[2/4] brooklyn-server git commit: Adds TestEffector.maxAttempts

Adds TestEffector.maxAttempts

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

Branch: refs/heads/master
Commit: 2b3c24110f9a53986f92e2fea30e1a5070eb6870
Parents: 23392d4
Author: Aled Sage <al...@gmail.com>
Authored: Wed Apr 5 18:27:39 2017 +0100
Committer: Aled Sage <al...@gmail.com>
Committed: Wed Apr 5 18:27:39 2017 +0100

----------------------------------------------------------------------
 .../brooklyn/test/framework/TestEffector.java   |  5 ++
 .../test/framework/TestEffectorImpl.java        | 87 +++++++++++++++++---
 .../brooklyn/test/framework/TestHttpCall.java   |  2 +-
 .../test/framework/TestEffectorTest.java        | 17 ++++
 .../org/apache/brooklyn/util/time/TimeTest.java | 30 +++++++
 5 files changed, 127 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2b3c2411/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java
----------------------------------------------------------------------
diff --git a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java
index 5c996d6..dc38256 100644
--- a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java
+++ b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffector.java
@@ -45,5 +45,10 @@ public interface TestEffector extends BaseTest {
             "The parameters to pass to the effector", 
             ImmutableMap.<String, Object>of());
 
+    /**
+     * The maximum number of times to execute the http call, before throwing an exception.
+     */
+    ConfigKey<Integer> MAX_ATTEMPTS = ConfigKeys.newIntegerConfigKey("maxAttempts", "Maximum number of attempts");
+
     AttributeSensorAndConfigKey<Object, Object> EFFECTOR_RESULT = ConfigKeys.newSensorAndConfigKey(Object.class, "result", "The result of invoking the effector");
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2b3c2411/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffectorImpl.java
----------------------------------------------------------------------
diff --git a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffectorImpl.java b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffectorImpl.java
index 8a08086..074dedf 100644
--- a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffectorImpl.java
+++ b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestEffectorImpl.java
@@ -23,7 +23,10 @@ import static org.apache.brooklyn.test.framework.TestFrameworkAssertions.getAsse
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.Entity;
@@ -33,15 +36,21 @@ import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
 import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic;
 import org.apache.brooklyn.core.mgmt.internal.EffectorUtils;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.test.framework.TestFrameworkAssertions.AssertionOptions;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.exceptions.RuntimeInterruptedException;
 import org.apache.brooklyn.util.guava.Maybe;
+import org.apache.brooklyn.util.repeat.Repeater;
 import org.apache.brooklyn.util.time.Duration;
+import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+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.Lists;
 
 public class TestEffectorImpl extends TargetableTestComponentImpl implements TestEffector {
@@ -60,6 +69,7 @@ public class TestEffectorImpl extends TargetableTestComponentImpl implements Tes
             final String effectorName = getRequiredConfig(EFFECTOR_NAME);
             final Map<String, ?> effectorParams = getConfig(EFFECTOR_PARAMS);
             final Duration timeout = getConfig(TIMEOUT);
+            final Integer maxAttempts = getConfig(MAX_ATTEMPTS);
             final Duration backoffToPeriod = getConfig(BACKOFF_TO_PERIOD);
             if (!getChildren().isEmpty()) {
                 throw new RuntimeException(String.format("The entity [%s] cannot have child entities", getClass().getName()));
@@ -69,20 +79,8 @@ public class TestEffectorImpl extends TargetableTestComponentImpl implements Tes
             if (effector.isAbsentOrNull()) {
                 throw new AssertionError(String.format("No effector with name [%s]", effectorName));
             }
-            final Task<?> effectorTask;
-            if (effectorParams == null || effectorParams.isEmpty()) {
-                effectorTask = Entities.invokeEffector(this, targetEntity, effector.get());
-            } else {
-                effectorTask = Entities.invokeEffector(this, targetEntity, effector.get(), effectorParams);
-            }
             
-            final Object effectorResult;
-            try {
-                effectorResult = effectorTask.get(timeout);
-            } catch (TimeoutException e) {
-                effectorTask.cancel(true);
-                throw new AssertionError("Effector "+effectorName+" timed out after "+timeout, e);
-            }
+            Object effectorResult = invokeEffector(targetEntity, effector.get(), effectorParams, maxAttempts, timeout, backoffToPeriod);
 
             final List<Map<String, Object>> assertions = getAssertions(this, ASSERTIONS);
             if(assertions != null && !assertions.isEmpty()){
@@ -119,4 +117,67 @@ public class TestEffectorImpl extends TargetableTestComponentImpl implements Tes
         stop();
         start(locations);
     }
+    
+    /**
+     * Invokes the effector. On failure, it repeats the invocation up to a maximum of 
+     * {@code maxAttempts} times (defaulting to one attempt if that parameter is null).
+     * 
+     * The total invocation time is capped at {@code timeout} (if multiple attempts are
+     * permitted, this is the total time for all attempts).
+     * 
+     * @throws AssertionError if the invocation times out
+     * @throws ExecutionException if the last invocation attempt fails
+     */
+    protected Object invokeEffector(final Entity targetEntity, final Effector<?> effector, final Map<String, ?> effectorParams, 
+            Integer maxAttempts, final Duration timeout, Duration backoffToPeriod) {
+        
+        Duration timeLimit = (timeout != null) ? timeout : (maxAttempts == null ? Asserts.DEFAULT_LONG_TIMEOUT : Duration.PRACTICALLY_FOREVER);
+        int iterationLimit = (maxAttempts != null) ? maxAttempts : 1;
+        final Long startTime = System.currentTimeMillis();
+        final AtomicReference<Object> effectorResult = new AtomicReference<Object>();
+        
+        Repeater.create()
+                .until(new Callable<Boolean>() {
+                    @Override
+                    public Boolean call() throws ExecutionException {
+                        try {
+                            long timeRemaining = Time.timeRemaining(startTime, timeout.toMilliseconds());
+                            Object result = invokeEffector(targetEntity, effector, effectorParams, Duration.millis(timeRemaining));
+                            effectorResult.set(result);
+                            return true;
+                        } catch (TimeoutException e) {
+                            throw new AssertionError("Effector "+effector.getName()+" timed out after "+timeout, e);
+                        }
+                    }})
+                .limitIterationsTo(iterationLimit)
+                .limitTimeTo(timeLimit)
+                .backoffTo(backoffToPeriod)
+                .rethrowExceptionImmediately(Predicates.or(ImmutableList.of(
+                        Predicates.instanceOf(TimeoutException.class), 
+                        Predicates.instanceOf(AbortError.class), 
+                        Predicates.instanceOf(InterruptedException.class), 
+                        Predicates.instanceOf(RuntimeInterruptedException.class))))
+                .runRequiringTrue();
+
+        return effectorResult.get();
+    }
+    
+    protected Object invokeEffector(Entity targetEntity, Effector<?> effector, Map<String, ?> effectorParams, Duration timeout) throws ExecutionException, TimeoutException {
+        Task<?> task;
+        if (effectorParams == null || effectorParams.isEmpty()) {
+            task = Entities.invokeEffector(TestEffectorImpl.this, targetEntity, effector);
+        } else {
+            task = Entities.invokeEffector(TestEffectorImpl.this, targetEntity, effector, effectorParams);
+        }
+        
+        try {
+            return task.get(timeout);
+        } catch (InterruptedException e) {
+            task.cancel(true);
+            throw Exceptions.propagate(e);
+        } catch (TimeoutException e) {
+            task.cancel(true);
+            throw e;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2b3c2411/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCall.java
----------------------------------------------------------------------
diff --git a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCall.java b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCall.java
index 3e4867b..e71e506 100644
--- a/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCall.java
+++ b/test-framework/src/main/java/org/apache/brooklyn/test/framework/TestHttpCall.java
@@ -65,7 +65,7 @@ public interface TestHttpCall extends BaseTest {
         "The HTTP field to apply the assertion to [body,status]", HttpAssertionTarget.body);
 
     /**
-     * The duration to wait for an assertion to succeed or fail before throwing an exception.
+     * The maximum number of times to execute the http call, before throwing an exception.
      */
     ConfigKey<Integer> MAX_ATTEMPTS = ConfigKeys.newIntegerConfigKey("maxAttempts", "Maximum number of attempts");
 

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2b3c2411/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java
----------------------------------------------------------------------
diff --git a/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java b/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java
index 022effd..c100c0d 100644
--- a/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java
+++ b/test-framework/src/test/java/org/apache/brooklyn/test/framework/TestEffectorTest.java
@@ -231,6 +231,23 @@ public class TestEffectorTest extends BrooklynAppUnitTestSupport {
     }
 
     @Test
+    public void testEffectorFailureRetriedUpToMaxAttempts() throws Exception {
+        testCase.addChild(EntitySpec.create(TestEffector.class)
+                .configure(TestEffector.MAX_ATTEMPTS, 2)
+                .configure(TestEffector.TIMEOUT, Duration.minutes(1))
+                .configure(TestEffector.TARGET_ENTITY, testEntity)
+                .configure(TestEffector.EFFECTOR_NAME, "effectorFails"));
+        Stopwatch stopwatch = Stopwatch.createStarted();
+
+        assertStartFails(app, TestEntity.EffectorFailureException.class, Asserts.DEFAULT_LONG_TIMEOUT);
+
+        Duration duration = Duration.of(stopwatch);
+        assertTrue(duration.isShorterThan(Asserts.DEFAULT_LONG_TIMEOUT), "duration="+duration);
+        
+        assertEquals(testEntity.sensors().get(TestEntity.FAILING_EFFECTOR_INVOCATION_COUNT), Integer.valueOf(2));
+    }
+
+    @Test
     public void testFailFastIfNoTargetEntity() throws Exception {
         testCase.addChild(EntitySpec.create(TestEffector.class)
                 .configure(TestEffector.EFFECTOR_NAME, "simpleEffector"));

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/2b3c2411/utils/common/src/test/java/org/apache/brooklyn/util/time/TimeTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/org/apache/brooklyn/util/time/TimeTest.java b/utils/common/src/test/java/org/apache/brooklyn/util/time/TimeTest.java
index e3c8167..fc9fd9f 100644
--- a/utils/common/src/test/java/org/apache/brooklyn/util/time/TimeTest.java
+++ b/utils/common/src/test/java/org/apache/brooklyn/util/time/TimeTest.java
@@ -18,7 +18,10 @@
  */
 package org.apache.brooklyn.util.time;
 
+import static org.testng.Assert.assertTrue;
+
 import java.util.Date;
+import java.util.List;
 import java.util.TimeZone;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
@@ -28,10 +31,27 @@ import org.testng.annotations.Test;
 
 import com.google.common.base.Stopwatch;
 import com.google.common.base.Ticker;
+import com.google.common.collect.ImmutableList;
 
 @Test
 public class TimeTest {
 
+    public void testTimeRemaining() {
+        long gracePeriod = 5000;
+        long maxTime = 30000;
+        long now = System.currentTimeMillis();
+        long remaining = Time.timeRemaining(now, maxTime);
+        assertOrdered(ImmutableList.<Long>of(maxTime-gracePeriod, remaining, maxTime), "");
+    }
+    
+    public void testTimeRemainingMaxLong() {
+        long gracePeriod = 5000;
+        long maxTime = Long.MAX_VALUE;
+        long now = System.currentTimeMillis();
+        long remaining = Time.timeRemaining(now, maxTime);
+        assertOrdered(ImmutableList.<Long>of(maxTime-gracePeriod, remaining, maxTime), "");
+    }
+    
     public void testMakeStringExact_secondsAndMillis() {
         check(1, "1ms");
         check(1000, "1s");
@@ -370,4 +390,14 @@ public class TimeTest {
     private void assertDatesParseToEqual(String input, String expected) {
         Assert.assertEquals(Time.parseDate(input).toString(), Time.parseDate(expected).toString(), "for: "+input+" ("+expected+")");
     }
+    
+    private <T extends Comparable<T>> void assertOrdered(List<? extends T> vals, String errMsg) {
+        T prev = null;
+        for (T val : vals) {
+            if (prev != null) {
+                assertTrue(prev.compareTo(val) <= 0, errMsg);
+            }
+            prev = val;
+        }
+    }
 }