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;
+ }
+ }
}