/** * Licensed to jclouds, Inc. (jclouds) under one or more * contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. jclouds licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jclouds.util; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.jclouds.util.Predicates2.retry; import static org.testng.Assert.fail; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.base.Stopwatch; import com.google.common.base.Supplier; import com.google.common.collect.Lists; /** * * @author Adrian Cole */ @Test(groups = "unit", singleThreaded = true) public class Predicates2Test { // Grace must be reasonably big; Thread.sleep can take a bit longer to wake up sometimes... public static int SLOW_BUILD_SERVER_GRACE = 250; // Sometimes returns sooner than timer would predict (e.g. observed 2999ms, when expected 3000ms) public static int EARLY_RETURN_GRACE = 10; private Stopwatch stopwatch; @BeforeMethod public void setUp() { stopwatch = new Stopwatch(); } @Test void testFalseOnIllegalStateExeception() { ensureImmediateReturnFor(new IllegalStateException()); } @SuppressWarnings("serial") @Test void testFalseOnExecutionException() { ensureImmediateReturnFor(new ExecutionException() { }); } @SuppressWarnings("serial") @Test void testFalseOnTimeoutException() { ensureImmediateReturnFor(new TimeoutException() { }); } @SuppressWarnings("serial") @Test(expectedExceptions = RuntimeException.class) void testPropagateOnException() { ensureImmediateReturnFor(new Exception() { }); } private void ensureImmediateReturnFor(final Exception ex) { Predicate<Supplier<String>> predicate = retry( new Predicate<Supplier<String>>() { public boolean apply(Supplier<String> input) { return "goo".equals(input.get()); } }, 3, 1, SECONDS); stopwatch.start(); assert !predicate.apply(new Supplier<String>() { @Override public String get() { throw new RuntimeException(ex); } }); long duration = stopwatch.elapsed(MILLISECONDS); assertOrdered(duration, SLOW_BUILD_SERVER_GRACE); } @Test void testAlwaysTrue() { // will call once immediately Predicate<String> predicate = retry(Predicates.<String> alwaysTrue(), 3, 1, SECONDS); stopwatch.start(); predicate.apply(""); long duration = stopwatch.elapsed(MILLISECONDS); assertOrdered(duration, SLOW_BUILD_SERVER_GRACE); } @Test void testAlwaysFalseMillis() { // maxWait=3; period=1; maxPeriod defaults to 1*10 // will call at 0, 1, 1+(1*1.5), 3 Predicate<String> predicate = retry(Predicates.<String> alwaysFalse(), 3, 1, SECONDS); stopwatch.start(); predicate.apply(""); long duration = stopwatch.elapsed(MILLISECONDS); assertOrdered(3000-EARLY_RETURN_GRACE, duration, 3000+SLOW_BUILD_SERVER_GRACE); } @Test void testThirdTimeTrue() { // maxWait=4; period=1; maxPeriod defaults to 1*10 // will call at 0, 1, 1+(1*1.5) RepeatedAttemptsPredicate rawPredicate = new RepeatedAttemptsPredicate(2); Predicate<String> predicate = retry(rawPredicate, 4, 1, SECONDS); stopwatch.start(); predicate.apply(""); long duration = stopwatch.elapsed(MILLISECONDS); assertOrdered(2500-EARLY_RETURN_GRACE, duration, 2500+SLOW_BUILD_SERVER_GRACE); assertCallTimes(rawPredicate.callTimes, 0, 1000, 1000+1500); } @Test void testThirdTimeTrueLimitedMaxInterval() { // maxWait=3; period=1; maxPeriod=1 // will call at 0, 1, 1+1 RepeatedAttemptsPredicate rawPredicate = new RepeatedAttemptsPredicate(2); Predicate<String> predicate = retry(rawPredicate, 3, 1, 1, SECONDS); stopwatch.start(); predicate.apply(""); long duration = stopwatch.elapsed(MILLISECONDS); assertOrdered(2000-EARLY_RETURN_GRACE, duration, 2000+SLOW_BUILD_SERVER_GRACE); assertCallTimes(rawPredicate.callTimes, 0, 1000, 2000); } public static class RepeatedAttemptsPredicate implements Predicate<String> { final List<Long> callTimes = Lists.newArrayList(); private final int succeedOnAttempt; private final Stopwatch stopwatch; private int count = 0; RepeatedAttemptsPredicate(int succeedOnAttempt) { this.succeedOnAttempt = succeedOnAttempt; this.stopwatch = new Stopwatch(); stopwatch.start(); } @Override public boolean apply(String input) { callTimes.add(stopwatch.elapsed(MILLISECONDS)); return count++ == succeedOnAttempt; } } @Test(enabled=false) // not a test, but picked up as such because public public static void assertCallTimes(List<Long> actual, Integer... expected) { Assert.assertEquals(actual.size(), expected.length); for (int i = 0; i < expected.length; i++) { long callTime = actual.get(i); assertOrdered(expected[i]-EARLY_RETURN_GRACE, callTime, expected[i]+SLOW_BUILD_SERVER_GRACE); } } private static void assertOrdered(long... values) { long prevVal = values[0]; for (long val : values) { if (val < prevVal) { fail(String.format("%s should be ordered", Arrays.toString(values))); } } } }