You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by zm...@apache.org on 2015/08/25 20:19:17 UTC
[03/37] aurora git commit: Import of Twitter Commons.
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/thrift/callers/DeadlineCallerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/thrift/callers/DeadlineCallerTest.java b/commons/src/test/java/com/twitter/common/thrift/callers/DeadlineCallerTest.java
new file mode 100644
index 0000000..99e7b86
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/thrift/callers/DeadlineCallerTest.java
@@ -0,0 +1,122 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.thrift.callers;
+
+import com.google.common.testing.TearDown;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.thrift.TTimeoutException;
+import org.apache.thrift.async.AsyncMethodCallback;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.annotation.Nullable;
+import java.lang.reflect.Method;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * TODO(William Farner): Test async.
+ *
+ * @author William Farner
+ */
+public class DeadlineCallerTest extends AbstractCallerTest {
+
+ private static final Amount<Long, Time> DEADLINE = Amount.of(100L, Time.MILLISECONDS);
+
+ private ExecutorService executorService;
+
+ private DeadlineCaller makeDeadline(final boolean shouldTimeOut) {
+ final CountDownLatch cancelled = new CountDownLatch(1);
+ if (shouldTimeOut) {
+ addTearDown(new TearDown() {
+ @Override public void tearDown() throws Exception {
+ // This will block forever if cancellation does not occur and interrupt the ~indefinite
+ // sleep.
+ cancelled.await();
+ }
+ });
+ }
+
+ Caller sleepyCaller = new CallerDecorator(caller, false) {
+ @Override public Object call(Method method, Object[] args,
+ @Nullable AsyncMethodCallback callback,
+ @Nullable Amount<Long, Time> connectTimeoutOverride) throws Throwable {
+
+ if (shouldTimeOut) {
+ try {
+ Thread.sleep(Long.MAX_VALUE);
+ fail("Expected late work to be cancelled and interrupted");
+ } catch (InterruptedException e) {
+ cancelled.countDown();
+ }
+ }
+
+ return caller.call(method, args, callback, connectTimeoutOverride);
+ }
+ };
+
+ return new DeadlineCaller(sleepyCaller, false, executorService, DEADLINE);
+ }
+
+ @Before
+ public void setUp() {
+ executorService = Executors.newSingleThreadExecutor();
+ }
+
+ @Test
+ public void testSuccess() throws Throwable {
+ DeadlineCaller deadline = makeDeadline(false);
+ expectCall("foo");
+
+ control.replay();
+
+ assertThat(call(deadline), is("foo"));
+ }
+
+ @Test
+ public void testException() throws Throwable {
+ DeadlineCaller deadline = makeDeadline(false);
+ Throwable exception = new IllegalArgumentException();
+ expectCall(exception);
+
+ control.replay();
+
+ try {
+ call(deadline);
+ fail();
+ } catch (Throwable t) {
+ assertThat(t, is(exception));
+ }
+ }
+
+ @Test(expected = TTimeoutException.class)
+ public void testExceedsDeadline() throws Throwable {
+ DeadlineCaller deadline = makeDeadline(true);
+
+ // No call expected, since we time out before it can be made.
+
+ control.replay();
+
+ call(deadline);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/thrift/callers/RetryingCallerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/thrift/callers/RetryingCallerTest.java b/commons/src/test/java/com/twitter/common/thrift/callers/RetryingCallerTest.java
new file mode 100644
index 0000000..62a2bab
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/thrift/callers/RetryingCallerTest.java
@@ -0,0 +1,153 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.thrift.callers;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.stats.StatsProvider;
+
+import static org.easymock.EasyMock.expect;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+/**
+ * TODO(William Farner): Test async.
+ *
+ * @author William Farner
+ */
+public class RetryingCallerTest extends AbstractCallerTest {
+
+ private static final int NUM_RETRIES = 2;
+
+ private static final ImmutableSet<Class<? extends Exception>> NO_RETRYABLE =
+ ImmutableSet.of();
+ private static final ImmutableSet<Class<? extends Exception>> RETRYABLE =
+ ImmutableSet.<Class<? extends Exception>>of(IllegalArgumentException.class);
+
+ private StatsProvider statsProvider;
+
+ @Before
+ public void mySetUp() {
+ statsProvider = createMock(StatsProvider.class);
+ }
+
+ @Test
+ public void testSuccess() throws Throwable {
+ expectCall("foo");
+
+ control.replay();
+
+ RetryingCaller retry = makeRetry(false, NO_RETRYABLE);
+ assertThat(call(retry), is("foo"));
+ assertThat(memoizeGetCounter.get(methodA).get(), is(0L));
+ }
+
+ @Test
+ public void testException() throws Throwable {
+ Throwable exception = nonRetryable();
+ expectCall(exception);
+
+ control.replay();
+
+ RetryingCaller retry = makeRetry(false, NO_RETRYABLE);
+ try {
+ call(retry);
+ fail();
+ } catch (Throwable t) {
+ assertThat(t, is(exception));
+ }
+ assertThat(memoizeGetCounter.get(methodA).get(), is(0L));
+ }
+
+ @Test
+ public void testRetriesSuccess() throws Throwable {
+ expectCall(retryable());
+ expectCall(retryable());
+ expectCall("foo");
+
+ control.replay();
+
+ RetryingCaller retry = makeRetry(false, RETRYABLE);
+ assertThat(call(retry), is("foo"));
+ assertThat(memoizeGetCounter.get(methodA).get(), is((long) NUM_RETRIES));
+ }
+
+ @Test
+ public void testRetryLimit() throws Throwable {
+ expectCall(retryable());
+ expectCall(retryable());
+ Throwable exception = retryable();
+ expectCall(exception);
+
+ control.replay();
+
+ RetryingCaller retry = makeRetry(false, RETRYABLE);
+ try {
+ call(retry);
+ fail();
+ } catch (Throwable t) {
+ assertThat(t, is(exception));
+ }
+ assertThat(memoizeGetCounter.get(methodA).get(), is(2L));
+ }
+
+ private Throwable retryable() {
+ return new IllegalArgumentException();
+ }
+
+ private Throwable nonRetryable() {
+ return new NullPointerException();
+ }
+
+ private LoadingCache<Method, AtomicLong> memoizeGetCounter = CacheBuilder.newBuilder().build(
+ new CacheLoader<Method, AtomicLong>() {
+ @Override public AtomicLong load(Method method) {
+ AtomicLong atomicLong = new AtomicLong();
+ expect(statsProvider.makeCounter("test_" + method.getName() + "_retries"))
+ .andReturn(atomicLong);
+ return atomicLong;
+ }
+ });
+
+ @Override
+ protected void expectCall(String returnValue) throws Throwable {
+ super.expectCall(returnValue);
+ memoizeGetCounter.get(methodA);
+ }
+
+ @Override
+ protected void expectCall(Throwable thrown) throws Throwable {
+ super.expectCall(thrown);
+ memoizeGetCounter.get(methodA);
+ }
+
+ private RetryingCaller makeRetry(boolean async,
+ ImmutableSet<Class<? extends Exception>> retryableExceptions) {
+ return new RetryingCaller(caller, async, statsProvider, "test", NUM_RETRIES,
+ retryableExceptions, false);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/BackoffDeciderTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/BackoffDeciderTest.java b/commons/src/test/java/com/twitter/common/util/BackoffDeciderTest.java
new file mode 100644
index 0000000..b24452d
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/BackoffDeciderTest.java
@@ -0,0 +1,327 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import com.google.common.collect.Sets;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.util.testing.FakeClock;
+import org.easymock.IMocksControl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createControl;
+import static org.easymock.EasyMock.expect;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author William Farner
+ */
+public class BackoffDeciderTest {
+ private static final String NAME = "test_decider";
+
+ private IMocksControl control;
+
+ private FakeClock clock;
+ private Random random;
+
+ @Before
+ public void setUp() {
+ control = createControl();
+ random = control.createMock(Random.class);
+
+ clock = new FakeClock();
+ }
+
+ private BackoffDecider.Builder builder(String name) {
+ return new BackoffDecider.Builder(name)
+ .withSeedSize(1)
+ .withRequestWindow(Amount.of(10L, Time.SECONDS))
+ .withBucketCount(100)
+ .withClock(clock)
+ .withRandom(random);
+ }
+
+ @After
+ public void verify() {
+ control.verify();
+ }
+
+ @Test
+ public void testAllSuccess() {
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+ run(decider, 10, Result.SUCCESS, State.NORMAL);
+ }
+
+ @Test
+ public void testAllFailures() {
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+ run(decider, 10, Result.FAILURE, State.BACKOFF);
+ }
+
+ @Test
+ public void testSingleFailure() {
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+ run(decider, 10, Result.SUCCESS, State.NORMAL);
+ run(decider, 1, Result.FAILURE, State.NORMAL);
+ }
+
+ @Test
+ public void testBelowThreshold() {
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).withTolerateFailureRate(0.5).build();
+ run(decider, 5, Result.SUCCESS, State.NORMAL);
+ run(decider, 5, Result.FAILURE, State.NORMAL);
+ }
+
+ @Test
+ public void testAtThreshold() {
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).withTolerateFailureRate(0.49).build();
+ run(decider, 51, Result.SUCCESS, State.NORMAL);
+ run(decider, 49, Result.FAILURE, State.NORMAL);
+ }
+
+ @Test
+ public void testAboveThreshold() {
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).withTolerateFailureRate(0.49).build();
+ run(decider, 51, Result.SUCCESS, State.NORMAL);
+ run(decider, 49, Result.FAILURE, State.NORMAL);
+ run(decider, 1, Result.FAILURE, State.BACKOFF);
+ }
+
+ @Test
+ public void testRecoversFromBackoff() {
+ // Backoff for the single request during the recovery period.
+ expect(random.nextDouble()).andReturn(1d);
+
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+ decider.addFailure();
+ assertThat(decider.shouldBackOff(), is(true));
+
+ // Enter recovery period.
+ clock.waitFor(101);
+ assertThat(decider.shouldBackOff(), is(true));
+
+ // Enter normal period.
+ clock.waitFor(101);
+ assertThat(decider.shouldBackOff(), is(false));
+ }
+
+ @Test
+ public void testLinearRecovery() {
+ for (int i = 0; i < 10; i++) {
+ expect(random.nextDouble()).andReturn(0.1 * i + 0.01); // Above threshold - back off.
+ expect(random.nextDouble()).andReturn(0.1 * i - 0.01); // Below threshold - allow request.
+ }
+
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+ decider.addFailure(); // Moves into backoff state.
+ assertThat(decider.shouldBackOff(), is(true));
+
+ // Enter recovery period.
+ clock.waitFor(101);
+
+ // Step linearly through recovery period (100 ms).
+ for (int i = 0; i < 10; i++) {
+ clock.waitFor(10);
+ assertThat(decider.shouldBackOff(), is(true));
+ assertThat(decider.shouldBackOff(), is(false));
+ }
+ }
+
+ @Test
+ public void testExponentialBackoff() {
+ // Don't back off during recovery period.
+ expect(random.nextDouble()).andReturn(0d).atLeastOnce();
+
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+ List<Integer> backoffDurationsMs = Arrays.asList(
+ 100, 200, 400, 800, 1600, 3200, 6400, 10000, 10000);
+
+ assertThat(decider.shouldBackOff(), is(false));
+
+ // normal -> backoff
+ decider.addFailure();
+ assertThat(decider.shouldBackOff(), is(true));
+
+ for (int backoffDurationMs : backoffDurationsMs) {
+ assertThat(decider.shouldBackOff(), is(true));
+
+ // backoff -> recovery
+ clock.waitFor(backoffDurationMs + 1);
+ assertThat(decider.shouldBackOff(), is(false));
+
+ // recovery -> backoff
+ decider.addFailure();
+ }
+ }
+
+ @Test
+ public void testRequestsExpire() {
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+ run(decider, 10, Result.SUCCESS, State.NORMAL);
+ run(decider, 10, Result.FAILURE, State.NORMAL);
+ assertThat(decider.shouldBackOff(), is(false));
+
+ // Depends on request window of 10 seconds, with 100 buckets.
+ clock.waitFor(10000);
+ run(decider, 1, Result.SUCCESS, State.NORMAL);
+ assertThat(decider.shouldBackOff(), is(false));
+ assertThat(decider.requests.totalRequests, is(21L));
+ assertThat(decider.requests.totalFailures, is(10L));
+
+ // Requests should have decayed out of the window.
+ clock.waitFor(101);
+ run(decider, 1, Result.SUCCESS, State.NORMAL);
+ assertThat(decider.shouldBackOff(), is(false));
+ assertThat(decider.requests.totalRequests, is(2L));
+ assertThat(decider.requests.totalFailures, is(0L));
+ }
+
+ @Test
+ public void testAllBackendsDontBackoff() {
+ // Back off for all requests during recovery period.
+ expect(random.nextDouble()).andReturn(1d); // decider2 in recovery.
+ expect(random.nextDouble()).andReturn(0d); // decider3 in recovery.
+
+ control.replay();
+
+ Set<BackoffDecider> group = Sets.newHashSet();
+ BackoffDecider decider1 = builder(NAME + 1).groupWith(group).build();
+ BackoffDecider decider2 = builder(NAME + 2).groupWith(group).build();
+ BackoffDecider decider3 = builder(NAME + 3).groupWith(group).build();
+
+ // Two of three backends start backing off.
+ decider1.addFailure();
+ assertThat(decider1.shouldBackOff(), is(true));
+
+ decider2.addFailure();
+ assertThat(decider2.shouldBackOff(), is(true));
+
+ // Since all but 1 backend is backing off, we switch out of backoff mode.
+ assertThat(decider3.shouldBackOff(), is(false));
+ decider3.addFailure();
+ assertThat(decider1.shouldBackOff(), is(false));
+ assertThat(decider2.shouldBackOff(), is(false));
+ assertThat(decider3.shouldBackOff(), is(false));
+
+ // Begin recovering one backend, others will return to recovery.
+ decider1.addSuccess();
+ assertThat(decider1.shouldBackOff(), is(false)); // Still thinks others are backing off.
+ assertThat(decider2.shouldBackOff(), is(false)); // Realizes decider1 is up, moves to recovery.
+ assertThat(decider2.shouldBackOff(), is(true)); // In recovery.
+ assertThat(decider3.shouldBackOff(), is(false)); // Realizes 1 & 2 are up, moves to recovery.
+ assertThat(decider3.shouldBackOff(), is(false)); // In recovery.
+ }
+
+ @Test
+ public void testOneBackendDoesntAffectOthers() {
+ control.replay();
+
+ Set<BackoffDecider> group = Sets.newHashSet();
+ BackoffDecider decider1 = builder(NAME + 1).groupWith(group).build();
+ BackoffDecider decider2 = builder(NAME + 2).groupWith(group).build();
+ BackoffDecider decider3 = builder(NAME + 3).groupWith(group).build();
+
+ // One backend starts failing.
+ run(decider1, 10, Result.SUCCESS, State.NORMAL);
+ run(decider2, 10, Result.SUCCESS, State.NORMAL);
+ run(decider3, 10, Result.FAILURE, State.BACKOFF);
+
+ // Other backends should remain normal.
+ run(decider1, 10, Result.SUCCESS, State.NORMAL);
+ run(decider2, 10, Result.SUCCESS, State.NORMAL);
+ }
+
+ @Test
+ public void testPreventsBackoffFlapping() {
+ // Permit requests during the backoff period.
+ expect(random.nextDouble()).andReturn(0d).atLeastOnce();
+
+ control.replay();
+
+ BackoffDecider decider = builder(NAME).build();
+
+ // Simulate 20 threads being permitted to send a request.
+ for (int i = 0; i < 20; i++) assertThat(decider.shouldBackOff(), is(false));
+
+ // The first 4 threads succeed.
+ for (int i = 0; i < 4; i++) decider.addSuccess();
+ assertThat(decider.shouldBackOff(), is(false));
+
+ // The next 6 fail, triggering backoff mode.
+ for (int i = 0; i < 6; i++) decider.addFailure();
+ assertThat(decider.shouldBackOff(), is(true));
+
+ // The next 10 succeed, but we are already backing off...so we should not move out of backoff.
+ for (int i = 0; i < 10; i++) decider.addSuccess();
+ assertThat(decider.shouldBackOff(), is(true));
+
+ // Attempt to push the decider into a higher backoff period.
+ for (int i = 0; i < 10; i++) decider.addFailure();
+
+ // Verify that the initial backoff period is in effect.
+ clock.waitFor(101);
+ assertThat(decider.shouldBackOff(), is(false));
+ }
+
+ private enum Result {
+ SUCCESS, FAILURE
+ }
+
+ private enum State {
+ BACKOFF, NORMAL
+ }
+
+ private void run(BackoffDecider decider, int numRequests, Result result, State state) {
+ for (int i = 0; i < numRequests; i++) {
+ if (result == Result.SUCCESS) {
+ decider.addSuccess();
+ } else {
+ decider.addFailure();
+ }
+
+ boolean backingOff = state == State.BACKOFF;
+ assertThat(decider.shouldBackOff(), is(backingOff));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/BackoffHelperTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/BackoffHelperTest.java b/commons/src/test/java/com/twitter/common/util/BackoffHelperTest.java
new file mode 100644
index 0000000..274878c
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/BackoffHelperTest.java
@@ -0,0 +1,176 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import com.twitter.common.base.ExceptionalSupplier;
+import com.twitter.common.base.Supplier;
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import org.easymock.IMocksControl;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.easymock.EasyMock.createControl;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.*;
+
+/**
+ * @author John Sirois
+ */
+public class BackoffHelperTest extends EasyMockTest {
+ private Clock clock;
+ private BackoffStrategy backoffStrategy;
+ private BackoffHelper backoffHelper;
+
+ @Before
+ public void setUp() {
+ clock = createMock(Clock.class);
+ backoffStrategy = createMock(BackoffStrategy.class);
+ backoffHelper = new BackoffHelper(clock, backoffStrategy);
+ }
+
+ @Test
+ public void testDoUntilSuccess() throws Exception {
+ Supplier<Boolean> task = createMock(new Clazz<Supplier<Boolean>>() { });
+
+ expect(task.get()).andReturn(false);
+ expect(backoffStrategy.shouldContinue(0L)).andReturn(true);
+ expect(backoffStrategy.calculateBackoffMs(0L)).andReturn(42L);
+ clock.waitFor(42L);
+ expect(task.get()).andReturn(true);
+
+ control.replay();
+
+ backoffHelper.doUntilSuccess(task);
+
+ control.verify();
+ }
+
+ @Test
+ public void testDoUntilResult() throws Exception {
+ Supplier<String> task = createMock(new Clazz<Supplier<String>>() { });
+
+ expect(task.get()).andReturn(null);
+ expect(backoffStrategy.shouldContinue(0)).andReturn(true);
+ expect(backoffStrategy.calculateBackoffMs(0)).andReturn(42L);
+ clock.waitFor(42L);
+ expect(task.get()).andReturn(null);
+ expect(backoffStrategy.shouldContinue(42L)).andReturn(true);
+ expect(backoffStrategy.calculateBackoffMs(42L)).andReturn(37L);
+ clock.waitFor(37L);
+ expect(task.get()).andReturn("jake");
+
+ control.replay();
+
+ assertEquals("jake", backoffHelper.doUntilResult(task));
+
+ control.verify();
+ }
+
+ @Test
+ public void testDoUntilResultTransparentException() throws Exception {
+ ExceptionalSupplier<String, IOException> task =
+ createMock(new Clazz<ExceptionalSupplier<String, IOException>>() { });
+
+ IOException thrown = new IOException();
+ expect(task.get()).andThrow(thrown);
+
+ control.replay();
+
+ try {
+ backoffHelper.doUntilResult(task);
+ fail("Expected exception to be bubbled");
+ } catch (IOException e) {
+ assertSame(thrown, e);
+ }
+
+ control.verify();
+ }
+
+ @Test
+ public void testDoUntilResultMaxSuccess() throws Exception {
+ Supplier<String> task = createMock(new Clazz<Supplier<String>>() { });
+
+ BackoffHelper maxBackoffHelper = new BackoffHelper(clock, backoffStrategy);
+
+ expect(task.get()).andReturn(null);
+ expect(backoffStrategy.shouldContinue(0L)).andReturn(true);
+ expect(backoffStrategy.calculateBackoffMs(0)).andReturn(42L);
+ clock.waitFor(42L);
+ expect(task.get()).andReturn(null);
+ expect(backoffStrategy.shouldContinue(42L)).andReturn(true);
+ expect(backoffStrategy.calculateBackoffMs(42L)).andReturn(37L);
+ clock.waitFor(37L);
+ expect(task.get()).andReturn("jake");
+
+ control.replay();
+
+ assertEquals("jake", maxBackoffHelper.doUntilResult(task));
+
+ control.verify();
+ }
+
+ @Test
+ public void testDoUntilResultMaxReached() throws Exception {
+ Supplier<String> task = createMock(new Clazz<Supplier<String>>() { });
+
+ BackoffHelper maxBackoffHelper = new BackoffHelper(clock, backoffStrategy);
+
+ expect(task.get()).andReturn(null);
+ expect(backoffStrategy.shouldContinue(0L)).andReturn(true);
+ expect(backoffStrategy.calculateBackoffMs(0)).andReturn(42L);
+ clock.waitFor(42L);
+ expect(task.get()).andReturn(null);
+ expect(backoffStrategy.shouldContinue(42L)).andReturn(true);
+ expect(backoffStrategy.calculateBackoffMs(42L)).andReturn(37L);
+ clock.waitFor(37L);
+ expect(task.get()).andReturn(null);
+ expect(backoffStrategy.shouldContinue(37L)).andReturn(false);
+
+ control.replay();
+
+ try {
+ maxBackoffHelper.doUntilResult(task);
+ fail("Expected max retry failure");
+ } catch (BackoffHelper.BackoffStoppedException e) {
+ // expected
+ }
+
+ control.verify();
+ }
+
+ @Test
+ public void testDoUntilSuccessTransparentException() throws Exception {
+ Supplier<Boolean> task = createMock(new Clazz<Supplier<Boolean>>() { });
+
+ IllegalArgumentException thrown = new IllegalArgumentException();
+ expect(task.get()).andThrow(thrown);
+
+ control.replay();
+
+ try {
+ backoffHelper.doUntilSuccess(task);
+ fail("Expected exception to be bubbled");
+ } catch (IllegalArgumentException e) {
+ assertSame(thrown, e);
+ }
+
+ control.verify();
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/LowResClockTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/LowResClockTest.java b/commons/src/test/java/com/twitter/common/util/LowResClockTest.java
new file mode 100644
index 0000000..d8961ed
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/LowResClockTest.java
@@ -0,0 +1,172 @@
+// =================================================================================================
+// Copyright 2014 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.easymock.Capture;
+import org.easymock.IAnswer;
+import org.junit.Test;
+
+import com.twitter.common.base.Command;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.util.testing.FakeClock;
+
+import static org.easymock.EasyMock.anyBoolean;
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.captureLong;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class LowResClockTest {
+
+ /**
+ * A FakeClock that overrides the {@link FakeClock#advance(Amount) advance} method to allow a
+ * co-operating thread to execute a synchronous action via {@link #doOnAdvance(Command)}.
+ */
+ static class WaitingFakeClock extends FakeClock {
+ private final SynchronousQueue<CountDownLatch> signalQueue =
+ new SynchronousQueue<CountDownLatch>();
+
+ @Override
+ public void advance(Amount<Long, Time> period) {
+ super.advance(period);
+ CountDownLatch signal = new CountDownLatch(1);
+ try {
+ signalQueue.put(signal);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ try {
+ signal.await();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ void doOnAdvance(Command action) throws InterruptedException {
+ CountDownLatch signal = signalQueue.take();
+ action.execute();
+ signal.countDown();
+ }
+ }
+
+ static class Tick implements Command {
+ private final Clock clock;
+ private final long period;
+ private final Runnable advancer;
+ private long time;
+
+ Tick(Clock clock, long startTime, long period, Runnable advancer) {
+ this.clock = clock;
+ time = startTime;
+ this.period = period;
+ this.advancer = advancer;
+ }
+
+ @Override
+ public void execute() {
+ if (clock.nowMillis() >= time + period) {
+ advancer.run();
+ time = clock.nowMillis();
+ }
+ }
+ }
+
+ @Test
+ public void testLowResClock() {
+ final WaitingFakeClock clock = new WaitingFakeClock();
+ final long start = clock.nowMillis();
+
+ ScheduledExecutorService mockExecutor = createMock(ScheduledExecutorService.class);
+ final Capture<Runnable> runnable = new Capture<Runnable>();
+ final Capture<Long> period = new Capture<Long>();
+ mockExecutor.scheduleAtFixedRate(capture(runnable), anyLong(), captureLong(period),
+ eq(TimeUnit.MILLISECONDS));
+
+ expectLastCall().andAnswer(new IAnswer<ScheduledFuture<?>>() {
+ public ScheduledFuture<?> answer() {
+ final Thread ticker = new Thread() {
+ @Override
+ public void run() {
+ Tick tick = new Tick(clock, start, period.getValue(), runnable.getValue());
+ try {
+ while (true) {
+ clock.doOnAdvance(tick);
+ }
+ } catch (InterruptedException e) {
+ /* terminate */
+ }
+ }
+ };
+ ticker.setDaemon(true);
+ ticker.start();
+ final ScheduledFuture<?> future = createMock(ScheduledFuture.class);
+ final AtomicBoolean stopped = new AtomicBoolean(false);
+ expect(future.isCancelled()).andAnswer(new IAnswer<Boolean>() {
+ @Override
+ public Boolean answer() throws Throwable {
+ return stopped.get();
+ }
+ }).anyTimes();
+ expect(future.cancel(anyBoolean())).andAnswer(new IAnswer<Boolean>() {
+ @Override
+ public Boolean answer() throws Throwable {
+ ticker.interrupt();
+ stopped.set(true);
+ return true;
+ }
+ });
+ replay(future);
+ return future;
+ }
+ });
+ replay(mockExecutor);
+
+ LowResClock lowRes = new LowResClock(Amount.of(1L, Time.SECONDS), mockExecutor, clock);
+
+ long t = lowRes.nowMillis();
+ clock.advance(Amount.of(100L, Time.MILLISECONDS));
+ assertEquals(t, lowRes.nowMillis());
+
+ clock.advance(Amount.of(900L, Time.MILLISECONDS));
+ assertEquals(t + 1000, lowRes.nowMillis());
+
+ clock.advance(Amount.of(100L, Time.MILLISECONDS));
+ assertEquals(t + 1000, lowRes.nowMillis());
+
+ lowRes.close();
+ try {
+ lowRes.nowMillis();
+ fail("Closed clock should throw exception!");
+ } catch (IllegalStateException e) {
+ /* expected */
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/QueueDrainerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/QueueDrainerTest.java b/commons/src/test/java/com/twitter/common/util/QueueDrainerTest.java
new file mode 100644
index 0000000..88f76fa
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/QueueDrainerTest.java
@@ -0,0 +1,62 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Executor;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+
+/**
+ * @author Srinivasan Rajagopal
+ */
+public class QueueDrainerTest extends EasyMockTest {
+ private Executor taskExecutor;
+ private BlockingQueue<RetryingRunnable> blockingQueue;
+ private QueueDrainer queueDrainer;
+
+ @Before
+ public void setUp() {
+ taskExecutor = createMock(Executor.class);
+ blockingQueue = createMock(new Clazz<BlockingQueue<RetryingRunnable>>() { });
+ queueDrainer = new QueueDrainer<RetryingRunnable>(taskExecutor, blockingQueue);
+ }
+
+ @Test
+ public void testDrainsQueue() throws Exception {
+ RetryingRunnable task = createMock(RetryingRunnable.class);
+ expect(blockingQueue.poll()).andReturn(task);
+ taskExecutor.execute(task);
+ control.replay();
+ replay();
+ queueDrainer.run();
+ }
+
+ @Test
+ public void testEmptyQueue() throws Exception {
+ expect(blockingQueue.poll()).andReturn(null);
+ control.replay();
+ replay();
+ queueDrainer.run();
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/RateLimitedCommandExecutorTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/RateLimitedCommandExecutorTest.java b/commons/src/test/java/com/twitter/common/util/RateLimitedCommandExecutorTest.java
new file mode 100644
index 0000000..2f4d479
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/RateLimitedCommandExecutorTest.java
@@ -0,0 +1,118 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.easymock.Capture;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.base.Command;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expectLastCall;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Srinivasan Rajagopal
+ */
+public class RateLimitedCommandExecutorTest extends EasyMockTest {
+ private ScheduledExecutorService taskExecutor;
+ private Amount<Long, Time> intervalBetweenRequests;
+ private RateLimitedCommandExecutor rateLimiter;
+ private Command command;
+ private BlockingQueue<RetryingRunnable<?>> queue;
+ private Runnable queueDrainer;
+
+ @Before
+ public void setUp() throws Exception {
+ command = createMock(Command.class);
+ taskExecutor = createMock(ScheduledExecutorService.class);
+ queue = createMock(new Clazz<BlockingQueue<RetryingRunnable<?>>>() {});
+ queueDrainer = createMock(Runnable.class);
+ intervalBetweenRequests = Amount.of(100L, Time.MILLISECONDS);
+ }
+
+ private RateLimitedCommandExecutor createLimiter() {
+ return new RateLimitedCommandExecutor(
+ taskExecutor,
+ intervalBetweenRequests,
+ queueDrainer,
+ queue);
+ }
+
+ @Test
+ public void testFixedRateClientDequeueIsInvoked() throws Exception {
+ Capture<Runnable> runnableCapture = createCapture();
+ expect(taskExecutor.scheduleWithFixedDelay(
+ capture(runnableCapture),
+ eq(0L),
+ eq((long) intervalBetweenRequests.as(Time.MILLISECONDS)),
+ eq(TimeUnit.MILLISECONDS))).andReturn(null);
+ control.replay();
+
+ rateLimiter = createLimiter();
+ assertTrue(runnableCapture.hasCaptured());
+ assertNotNull(runnableCapture.getValue());
+ }
+
+ @Test
+ public void testEnqueue() throws Exception {
+ expect(taskExecutor.scheduleWithFixedDelay((Runnable) anyObject(),
+ eq(0L),
+ eq((long) intervalBetweenRequests.as(Time.MILLISECONDS)),
+ eq(TimeUnit.MILLISECONDS))).andReturn(null);
+
+ Capture<RetryingRunnable> runnableTaskCapture = createCapture();
+ expect(queue.add(capture(runnableTaskCapture))).andReturn(true);
+ control.replay();
+
+ rateLimiter = createLimiter();
+ rateLimiter.execute("name", command, RuntimeException.class, 1, intervalBetweenRequests);
+ assertTrue(runnableTaskCapture.hasCaptured());
+ }
+
+ @Test
+ public void testDrainQueueCommandHandlesException() {
+ Capture<Runnable> runnableCapture = createCapture();
+ expect(taskExecutor.scheduleWithFixedDelay(
+ capture(runnableCapture),
+ eq(0L),
+ eq((long) intervalBetweenRequests.as(Time.MILLISECONDS)),
+ eq(TimeUnit.MILLISECONDS))).andReturn(null);
+ queueDrainer.run();
+ expectLastCall().andThrow(new RuntimeException());
+
+ control.replay();
+ rateLimiter = createLimiter();
+
+ //Execute the runnable to ensure the exception does not propagate
+ // and potentially kill threads in the executor service.
+ runnableCapture.getValue().run();
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/SamplerTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/SamplerTest.java b/commons/src/test/java/com/twitter/common/util/SamplerTest.java
new file mode 100644
index 0000000..f798513
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/SamplerTest.java
@@ -0,0 +1,70 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import org.easymock.IMocksControl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+import static org.hamcrest.CoreMatchers.*;
+
+/**
+ * @author William Farner
+ */
+public class SamplerTest {
+
+ private IMocksControl control;
+
+ private Random random;
+
+ @Before
+ public void setUp() throws Exception {
+ control = createControl();
+
+ random = control.createMock(Random.class);
+ }
+
+ @After
+ public void verify() {
+ control.verify();
+ }
+
+ @Test
+ public void testThresholdWorks() {
+ for (int i = 0; i <= 100; i++) {
+ expect(random.nextDouble()).andReturn(0.01 * i);
+ }
+
+ control.replay();
+
+ Sampler sampler = new Sampler(25, random);
+
+ for (int i = 0; i <= 100; i++) {
+ assertThat(sampler.select(), is(i < 25));
+ }
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testRejectsNegativePercent() {
+ control.replay();
+
+ new Sampler(-10, random);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/StateMachineTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/StateMachineTest.java b/commons/src/test/java/com/twitter/common/util/StateMachineTest.java
new file mode 100644
index 0000000..fe59928
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/StateMachineTest.java
@@ -0,0 +1,420 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import com.google.common.collect.ImmutableSet;
+
+import com.twitter.common.base.Closure;
+import com.twitter.common.base.Closures;
+import com.twitter.common.base.Command;
+import com.twitter.common.base.Commands;
+import com.twitter.common.base.ExceptionalSupplier;
+import com.twitter.common.base.Supplier;
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.StateMachine.Transition;
+import com.twitter.common.util.StateMachine.Rule;
+
+import org.junit.Test;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Tests the functionality of StateMachine.
+ *
+ * @author William Farner
+ */
+public class StateMachineTest extends EasyMockTest {
+ private static final String NAME = "State machine.";
+
+ private static final String A = "A";
+ private static final String B = "B";
+ private static final String C = "C";
+ private static final String D = "D";
+
+ @Test
+ public void testEmptySM() {
+ control.replay();
+
+ try {
+ StateMachine.builder(NAME).build();
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testMachineNoInit() {
+ control.replay();
+
+ try {
+ StateMachine.<String>builder(NAME)
+ .addState(Rule.from(A).to(B))
+ .build();
+ fail();
+ } catch (IllegalStateException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testBasicFSM() {
+ control.replay();
+
+ StateMachine<String> fsm = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .addState(Rule.from(B).to(C))
+ .addState(Rule.from(C).to(D))
+ .build();
+
+ assertThat(fsm.getState(), is(A));
+ changeState(fsm, B);
+ changeState(fsm, C);
+ changeState(fsm, D);
+ }
+
+ @Test
+ public void testLoopingFSM() {
+ control.replay();
+
+ StateMachine<String> fsm = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .addState(Rule.from(B).to(C))
+ .addState(Rule.from(C).to(B, D))
+ .build();
+
+ assertThat(fsm.getState(), is(A));
+ changeState(fsm, B);
+ changeState(fsm, C);
+ changeState(fsm, B);
+ changeState(fsm, C);
+ changeState(fsm, B);
+ changeState(fsm, C);
+ changeState(fsm, D);
+ }
+
+ @Test
+ public void testMachineUnknownState() {
+ control.replay();
+
+ StateMachine<String> fsm = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .addState(Rule.from(B).to(C))
+ .build();
+
+ assertThat(fsm.getState(), is(A));
+ changeState(fsm, B);
+ changeState(fsm, C);
+ changeStateFail(fsm, D);
+ }
+
+ @Test
+ public void testMachineBadTransition() {
+ control.replay();
+
+ StateMachine<String> fsm = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .addState(Rule.from(B).to(C))
+ .build();
+
+ assertThat(fsm.getState(), is(A));
+ changeState(fsm, B);
+ changeState(fsm, C);
+ changeStateFail(fsm, B);
+ }
+
+ @Test
+ public void testMachineSelfTransitionAllowed() {
+ control.replay();
+
+ StateMachine<String> fsm = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(A))
+ .build();
+
+ assertThat(fsm.getState(), is(A));
+ changeState(fsm, A);
+ changeState(fsm, A);
+ }
+
+ @Test
+ public void testMachineSelfTransitionDisallowed() {
+ control.replay();
+
+ StateMachine<String> fsm = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .build();
+
+ assertThat(fsm.getState(), is(A));
+ changeStateFail(fsm, A);
+ changeStateFail(fsm, A);
+ }
+
+ @Test
+ public void testCheckStateMatches() {
+ control.replay();
+
+ StateMachine<String> stateMachine = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .build();
+ stateMachine.checkState(A);
+ stateMachine.transition(B);
+ stateMachine.checkState(B);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testCheckStateFails() {
+ control.replay();
+
+ StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .build()
+ .checkState(B);
+ }
+
+ @Test
+ public void testDoInStateMatches() {
+ control.replay();
+
+ StateMachine<String> stateMachine = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B))
+ .build();
+
+ int amount = stateMachine.doInState(A, new Supplier<Integer>() {
+ @Override public Integer get() {
+ return 42;
+ }
+ });
+ assertThat(amount, is(42));
+
+ stateMachine.transition(B);
+
+ String name = stateMachine.doInState(B, new Supplier<String>() {
+ @Override public String get() {
+ return "jake";
+ }
+ });
+ assertThat(name, is("jake"));
+ }
+
+ @Test
+ public void testDoInStateConcurrently() throws InterruptedException {
+ control.replay();
+
+ final StateMachine<String> stateMachine = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(A, B)
+ .build();
+
+ final BlockingQueue<Integer> results = new LinkedBlockingQueue<Integer>();
+
+ final CountDownLatch supplier1Proceed = new CountDownLatch(1);
+ final ExceptionalSupplier<Void, RuntimeException> supplier1 =
+ Commands.asSupplier(new Command() {
+ @Override public void execute() {
+ results.offer(1);
+ try {
+ supplier1Proceed.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ final CountDownLatch supplier2Proceed = new CountDownLatch(1);
+ final ExceptionalSupplier<Void, RuntimeException> supplier2 =
+ Commands.asSupplier(new Command() {
+ @Override public void execute() {
+ results.offer(2);
+ try {
+ supplier2Proceed.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ Thread thread1 = new Thread(new Runnable() {
+ @Override public void run() {
+ stateMachine.doInState(A, supplier1);
+ }
+ });
+
+ Thread thread2 = new Thread(new Runnable() {
+ @Override public void run() {
+ stateMachine.doInState(A, supplier2);
+ }
+ });
+
+ Thread thread3 = new Thread(new Runnable() {
+ @Override public void run() {
+ stateMachine.transition(B);
+ }
+ });
+
+ thread1.start();
+ thread2.start();
+
+ Integer result1 = results.take();
+ Integer result2 = results.take();
+ // we know 1 and 2 have the read lock held
+
+ thread3.start(); // should be blocked by read locks in place
+
+ assertThat(ImmutableSet.of(result1, result2), is(ImmutableSet.of(1, 2)));
+ assertTrue(results.isEmpty());
+
+ supplier1Proceed.countDown();
+ supplier2Proceed.countDown();
+
+ thread1.join();
+ thread2.join();
+ thread3.join();
+
+ assertThat(B, is(stateMachine.getState()));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testDoInStateFails() {
+ control.replay();
+
+ StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(A, B)
+ .build()
+ .doInState(B, Commands.asSupplier(Commands.NOOP));
+ }
+
+ @Test
+ public void testNoThrowOnInvalidTransition() {
+ control.replay();
+
+ StateMachine<String> machine = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(A, B)
+ .throwOnBadTransition(false)
+ .build();
+
+ machine.transition(C);
+ assertThat(machine.getState(), is(A));
+ }
+
+ private static final Clazz<Closure<Transition<String>>> TRANSITION_CLOSURE_CLZ =
+ new Clazz<Closure<Transition<String>>>() {};
+
+ @Test
+ public void testTransitionCallbacks() {
+ Closure<Transition<String>> anyTransition = createMock(TRANSITION_CLOSURE_CLZ);
+ Closure<Transition<String>> fromA = createMock(TRANSITION_CLOSURE_CLZ);
+ Closure<Transition<String>> fromB = createMock(TRANSITION_CLOSURE_CLZ);
+
+ Transition<String> aToB = new Transition<String>(A, B, true);
+ anyTransition.execute(aToB);
+ fromA.execute(aToB);
+
+ Transition<String> bToB = new Transition<String>(B, B, false);
+ anyTransition.execute(bToB);
+ fromB.execute(bToB);
+
+ Transition<String> bToC = new Transition<String>(B, C, true);
+ anyTransition.execute(bToC);
+ fromB.execute(bToC);
+
+ anyTransition.execute(new Transition<String>(C, B, true));
+
+ Transition<String> bToD = new Transition<String>(B, D, true);
+ anyTransition.execute(bToD);
+ fromB.execute(bToD);
+
+ control.replay();
+
+ StateMachine<String> machine = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule.from(A).to(B).withCallback(fromA))
+ .addState(Rule.from(B).to(C, D).withCallback(fromB))
+ .addState(Rule.from(C).to(B))
+ .addState(Rule.from(D).noTransitions())
+ .onAnyTransition(anyTransition)
+ .throwOnBadTransition(false)
+ .build();
+
+ machine.transition(B);
+ machine.transition(B);
+ machine.transition(C);
+ machine.transition(B);
+ machine.transition(D);
+ }
+
+ @Test
+ public void testFilteredTransitionCallbacks() {
+ Closure<Transition<String>> aToBHandler = createMock(TRANSITION_CLOSURE_CLZ);
+ Closure<Transition<String>> impossibleHandler = createMock(TRANSITION_CLOSURE_CLZ);
+
+ aToBHandler.execute(new Transition<String>(A, B, true));
+
+ control.replay();
+
+ StateMachine<String> machine = StateMachine.<String>builder(NAME)
+ .initialState(A)
+ .addState(Rule
+ .from(A).to(B, C)
+ .withCallback(Closures.filter(Transition.to(B), aToBHandler)))
+ .addState(Rule.from(B).to(A)
+ .withCallback(Closures.filter(Transition.to(B), impossibleHandler)))
+ .addState(Rule.from(C).noTransitions())
+ .build();
+
+ machine.transition(B);
+ machine.transition(A);
+ machine.transition(C);
+ }
+
+ private static void changeState(StateMachine<String> machine, String to, boolean expectAllowed) {
+ boolean allowed = true;
+ try {
+ machine.transition(to);
+ assertThat(machine.getState(), is(to));
+ } catch (StateMachine.IllegalStateTransitionException e) {
+ allowed = false;
+ }
+
+ assertThat(allowed, is(expectAllowed));
+ }
+
+ private static void changeState(StateMachine<String> machine, String to) {
+ changeState(machine, to, true);
+ }
+
+ private static void changeStateFail(StateMachine<String> machine, String to) {
+ changeState(machine, to, false);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/TruncatedBinaryBackoffTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/TruncatedBinaryBackoffTest.java b/commons/src/test/java/com/twitter/common/util/TruncatedBinaryBackoffTest.java
new file mode 100644
index 0000000..1932be5
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/TruncatedBinaryBackoffTest.java
@@ -0,0 +1,93 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util;
+
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author John Sirois
+ */
+public class TruncatedBinaryBackoffTest {
+ @Test(expected = NullPointerException.class)
+ public void testNullInitialBackoffRejected() {
+ new TruncatedBinaryBackoff(null, Amount.of(1L, Time.SECONDS));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testZeroInitialBackoffRejected() {
+ new TruncatedBinaryBackoff(Amount.of(0L, Time.SECONDS), Amount.of(1L, Time.SECONDS));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testNegativeInitialBackoffRejected() {
+ new TruncatedBinaryBackoff(Amount.of(-1L, Time.SECONDS), Amount.of(1L, Time.SECONDS));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNullMaximumBackoffRejected() {
+ new TruncatedBinaryBackoff(Amount.of(1L, Time.SECONDS), null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMaximumBackoffLessThanInitialBackoffRejected() {
+ new TruncatedBinaryBackoff(Amount.of(2L, Time.SECONDS), Amount.of(1L, Time.SECONDS));
+ }
+
+ @Test
+ public void testCalculateBackoffMs() {
+ TruncatedBinaryBackoff backoff =
+ new TruncatedBinaryBackoff(Amount.of(1L, Time.MILLISECONDS),
+ Amount.of(6L, Time.MILLISECONDS));
+
+ try {
+ backoff.calculateBackoffMs(-1L);
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ assertEquals(1, backoff.calculateBackoffMs(0));
+ assertEquals(2, backoff.calculateBackoffMs(1));
+ assertEquals(4, backoff.calculateBackoffMs(2));
+ assertEquals(6, backoff.calculateBackoffMs(4));
+ assertEquals(6, backoff.calculateBackoffMs(8));
+ }
+
+ @Test
+ public void testCalculateBackoffStop() {
+ TruncatedBinaryBackoff backoff =
+ new TruncatedBinaryBackoff(Amount.of(1L, Time.MILLISECONDS),
+ Amount.of(6L, Time.MILLISECONDS), true);
+
+ assertTrue(backoff.shouldContinue(0));
+ assertEquals(1, backoff.calculateBackoffMs(0));
+ assertTrue(backoff.shouldContinue(1));
+ assertEquals(2, backoff.calculateBackoffMs(1));
+ assertTrue(backoff.shouldContinue(2));
+ assertEquals(4, backoff.calculateBackoffMs(2));
+ assertTrue(backoff.shouldContinue(4));
+ assertEquals(6, backoff.calculateBackoffMs(4));
+ assertFalse(backoff.shouldContinue(6));
+ assertEquals(6, backoff.calculateBackoffMs(8));
+ assertFalse(backoff.shouldContinue(8));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/caching/CachingMethodProxyTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/caching/CachingMethodProxyTest.java b/commons/src/test/java/com/twitter/common/util/caching/CachingMethodProxyTest.java
new file mode 100644
index 0000000..a942182
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/caching/CachingMethodProxyTest.java
@@ -0,0 +1,260 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util.caching;
+
+import com.google.common.base.Predicate;
+import org.easymock.IMocksControl;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.easymock.EasyMock.createControl;
+import static org.easymock.EasyMock.expect;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @author William Farner
+ */
+public class CachingMethodProxyTest {
+
+ private CachingMethodProxy<Math> proxyBuilder;
+ private Math uncachedMath;
+ private Math cachedMath;
+ private Cache<List, Integer> intCache;
+ private Predicate<Integer> intFilter;
+
+ private IMocksControl control;
+
+ @Before
+ @SuppressWarnings("unchecked")
+ public void setUp() {
+ control = createControl();
+ uncachedMath = control.createMock(Math.class);
+ intCache = control.createMock(Cache.class);
+ intFilter = control.createMock(Predicate.class);
+
+ proxyBuilder = CachingMethodProxy.proxyFor(uncachedMath, Math.class);
+ cachedMath = proxyBuilder.getCachingProxy();
+ }
+
+ @After
+ public void verifyControl() {
+ control.verify();
+ }
+
+ @Test
+ public void testCaches() throws Exception {
+ expectUncachedAdd(1, 2, true);
+ expectUncachedAdd(3, 4, true);
+ expect(intCache.get(Arrays.asList(1, 2))).andReturn(3);
+ expect(intCache.get(Arrays.asList(3, 4))).andReturn(7);
+
+ control.replay();
+
+ proxyBuilder.cache(cachedMath.sum(0, 0), intCache, intFilter)
+ .prepare();
+ assertThat(cachedMath.sum(1, 2), is(3));
+ assertThat(cachedMath.sum(3, 4), is(7));
+ assertThat(cachedMath.sum(1, 2), is(3));
+ assertThat(cachedMath.sum(3, 4), is(7));
+ }
+
+ @Test
+ public void testIgnoresUncachedMethod() throws Exception {
+ expect(uncachedMath.sub(2, 1)).andReturn(1);
+ expect(uncachedMath.sub(2, 1)).andReturn(1);
+
+ control.replay();
+
+ proxyBuilder.cache(cachedMath.sum(0, 0), intCache, intFilter)
+ .prepare();
+ assertThat(cachedMath.sub(2, 1), is(1));
+ assertThat(cachedMath.sub(2, 1), is(1));
+ }
+
+ @Test
+ public void testFilterValue() throws Exception {
+ expectUncachedAdd(1, 2, true);
+ expectUncachedAdd(3, 4, false);
+ expect(intCache.get(Arrays.asList(1, 2))).andReturn(3);
+
+ control.replay();
+
+ proxyBuilder.cache(cachedMath.sum(0, 0), intCache, intFilter)
+ .prepare();
+ assertThat(cachedMath.sum(1, 2), is(3));
+ assertThat(cachedMath.sum(3, 4), is(7));
+ assertThat(cachedMath.sum(1, 2), is(3));
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testRequiresOneCache() throws Exception {
+ control.replay();
+
+ proxyBuilder.prepare();
+ }
+
+ @Test
+ public void testExceptionThrown() throws Exception {
+ List<Integer> args = Arrays.asList(1, 2);
+ expect(intCache.get(args)).andReturn(null);
+
+ Math.AddException thrown = new Math.AddException();
+ expect(uncachedMath.sum(1, 2)).andThrow(thrown);
+
+ control.replay();
+
+ proxyBuilder.cache(cachedMath.sum(0, 0), intCache, intFilter)
+ .prepare();
+ try {
+ cachedMath.sum(1, 2);
+ } catch (Math.AddException e) {
+ assertSame(e, thrown);
+ }
+ }
+
+ /* TODO(William Farner): Re-enable once the TODO for checking return value/cache value types is done.
+ @Test(expected = IllegalArgumentException.class)
+ public void testCacheValueAndMethodReturnTypeMismatch() throws Exception {
+ control.replay();
+
+ cachedMath.addDouble(0, 0);
+ proxyBuilder.cache(1, intCache, intFilter)
+ .prepare();
+ }
+ */
+
+ @Test(expected = IllegalStateException.class)
+ public void testRejectsCacheSetupAfterPrepare() throws Exception {
+ control.replay();
+
+ proxyBuilder.cache(cachedMath.sum(0, 0), intCache, intFilter)
+ .prepare();
+ proxyBuilder.cache(null, intCache, intFilter);
+ }
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testIgnoresNullValues() throws Exception {
+ // Null return values should not even be considered for entry into the cache, and therefore
+ // should not be passed to the filter.
+
+ Cache<List, Math> crazyCache = control.createMock(Cache.class);
+ Predicate<Math> crazyFilter = control.createMock(Predicate.class);
+
+ expect(crazyCache.get(Arrays.asList(null, null))).andReturn(null);
+ expect(uncachedMath.crazyMath(null, null)).andReturn(null);
+
+ control.replay();
+
+ proxyBuilder.cache(cachedMath.crazyMath(null, null), crazyCache, crazyFilter)
+ .prepare();
+
+ cachedMath.crazyMath(null, null);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ @SuppressWarnings("unchecked")
+ public void testRejectsVoidReturn() throws Exception {
+ Cache<List, Void> voidCache = control.createMock(Cache.class);
+ Predicate<Void> voidFilter = control.createMock(Predicate.class);
+
+ control.replay();
+
+ cachedMath.doSomething(null);
+ proxyBuilder.cache(null, voidCache, voidFilter);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ @SuppressWarnings("unchecked")
+ public void testFailsNoCachedCall() throws Exception {
+ Cache<List, Void> voidCache = control.createMock(Cache.class);
+ Predicate<Void> voidFilter = control.createMock(Predicate.class);
+
+ control.replay();
+
+ // No method call was recorded on the proxy, so the builder doesn't know what to cache.
+ proxyBuilder.cache(null, voidCache, voidFilter);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ @SuppressWarnings("unchecked")
+ public void testRejectsZeroArgMethods() throws Exception {
+ Cache<List, Math> mathCache = control.createMock(Cache.class);
+ Predicate<Math> mathFilter = control.createMock(Predicate.class);
+
+ control.replay();
+
+ proxyBuilder.cache(cachedMath.doNothing(), mathCache, mathFilter);
+ }
+
+ @Test
+ public void testAllowsSuperclassMethod() throws Exception {
+ SubMath subMath = control.createMock(SubMath.class);
+
+ List<Integer> args = Arrays.asList(1, 2);
+ expect(intCache.get(args)).andReturn(null);
+ expect(subMath.sum(1, 2)).andReturn(3);
+ expect(intFilter.apply(3)).andReturn(true);
+ intCache.put(args, 3);
+
+ control.replay();
+
+ Method add = SubMath.class.getMethod("sum", int.class, int.class);
+
+ CachingMethodProxy<SubMath> proxyBuilder = CachingMethodProxy.proxyFor(subMath, SubMath.class);
+ SubMath cached = proxyBuilder.getCachingProxy();
+ proxyBuilder.cache(cached.sum(0, 0), intCache, intFilter)
+ .prepare();
+
+ cached.sum(1, 2);
+ }
+
+ private void expectUncachedAdd(int a, int b, boolean addToCache) throws Math.AddException {
+ List<Integer> args = Arrays.asList(a, b);
+ expect(intCache.get(args)).andReturn(null);
+ expect(uncachedMath.sum(a, b)).andReturn(a + b);
+ expect(intFilter.apply(a + b)).andReturn(addToCache);
+ if (addToCache) intCache.put(args, a + b);
+ }
+
+ private interface Math {
+ public int sum(int a, int b) throws AddException;
+
+ public double addDouble(double a, double b) throws AddException;
+
+ public int sub(int a, int b);
+
+ public Math crazyMath(Math a, Math b);
+
+ public Math doNothing();
+
+ public void doSomething(Math a);
+
+ class AddException extends Exception {}
+ }
+
+ private interface SubMath extends Math {
+ public int otherSum(int a, int b);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/caching/LRUCacheTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/caching/LRUCacheTest.java b/commons/src/test/java/com/twitter/common/util/caching/LRUCacheTest.java
new file mode 100644
index 0000000..42af0f1
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/caching/LRUCacheTest.java
@@ -0,0 +1,84 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util.caching;
+
+import com.google.common.collect.Lists;
+import com.twitter.common.base.Closure;
+import com.twitter.common.collections.Pair;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Tests the LRUCache class.
+ *
+ * @author William Farner
+ */
+public class LRUCacheTest {
+
+ @Test
+ public void testEvicts() {
+ int cacheSize = 10;
+ int inserts = 100;
+ LRUCache<Integer, Integer> cache = LRUCache.<Integer, Integer>builder()
+ .maxSize(cacheSize)
+ .build();
+ for (int i = 0; i < inserts; i++) {
+ cache.put(i, i);
+ assertThat(cache.size(), is(Math.min(i + 1, cacheSize)));
+ }
+ }
+
+ @Test
+ public void testEvictsLRU() {
+ int cacheSize = 10;
+
+ final List<Integer> evictedKeys = Lists.newLinkedList();
+
+ Closure<Pair<Integer, Integer>> listener = new Closure<Pair<Integer, Integer>>() {
+ @Override public void execute(Pair<Integer, Integer> evicted) {
+ evictedKeys.add(evicted.getFirst());
+ }
+ };
+
+ LRUCache<Integer, Integer> cache = LRUCache.<Integer, Integer>builder()
+ .maxSize(cacheSize)
+ .evictionListener(listener)
+ .build();
+
+ for (int i = 0; i < cacheSize; i++) {
+ cache.put(i, i);
+ }
+
+ // Access all elements except '3'.
+ for (int access : Arrays.asList(0, 7, 2, 8, 4, 6, 9, 1, 5)) {
+ assertNotNull(cache.get(access));
+ }
+
+ assertThat(evictedKeys.isEmpty(), is(true));
+
+ // This should trigger the eviction.
+ cache.put(cacheSize + 1, cacheSize + 1);
+
+ assertThat(evictedKeys, is(Arrays.asList(3)));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingExecutorServiceTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingExecutorServiceTest.java b/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingExecutorServiceTest.java
new file mode 100644
index 0000000..59a335f
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingExecutorServiceTest.java
@@ -0,0 +1,111 @@
+package com.twitter.common.util.concurrent;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import com.google.common.testing.TearDown;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ExceptionHandlingExecutorServiceTest extends EasyMockTest {
+ private static final RuntimeException EXCEPTION = new RuntimeException();
+
+ private ExecutorService executorService;
+ private Thread.UncaughtExceptionHandler signallingHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ signallingHandler = createMock(Thread.UncaughtExceptionHandler.class);
+ executorService = MoreExecutors.exceptionHandlingExecutor(
+ Executors.newCachedThreadPool(new ThreadFactoryBuilder()
+ .setNameFormat("ExceptionHandlingExecutorServiceTest-%d")
+ .build()),
+ signallingHandler);
+
+ final ExecutorServiceShutdown executorServiceShutdown = new ExecutorServiceShutdown(
+ executorService, Amount.of(3L, Time.SECONDS));
+
+ addTearDown(new TearDown() {
+ @Override
+ public void tearDown() throws Exception {
+ executorServiceShutdown.execute();
+ }
+ });
+ }
+
+ @Test
+ public void testSubmitRunnable() throws Exception {
+ Runnable runnable = createMock(Runnable.class);
+ runnable.run();
+
+ control.replay();
+
+ executorService.submit(runnable).get();
+ }
+
+ @Test
+ public void testSubmitFailingRunnable() throws Exception {
+ signallingHandler.uncaughtException(anyObject(Thread.class), eq(EXCEPTION));
+ Runnable runnable = createMock(Runnable.class);
+ runnable.run();
+ expectLastCall().andThrow(EXCEPTION);
+
+ control.replay();
+
+ try {
+ executorService.submit(runnable).get();
+ fail(EXCEPTION.getClass().getSimpleName() + " should be thrown.");
+ } catch (ExecutionException e) {
+ assertEquals(EXCEPTION, e.getCause());
+ }
+ }
+
+ @Test
+ public void testSubmitCallable() throws Exception {
+ Integer returnValue = 123;
+ Callable<Integer> callable = createMock(new Clazz<Callable<Integer>>() {});
+ callable.call();
+ expectLastCall().andReturn(returnValue);
+
+ control.replay();
+
+ assertEquals(returnValue, executorService.submit(callable).get());
+ }
+
+ @Test
+ public void testSubmitFailingCallable() throws Exception {
+ signallingHandler.uncaughtException(anyObject(Thread.class), eq(EXCEPTION));
+ Callable<Void> callable = createMock(new Clazz<Callable<Void>>() {});
+ expect(callable.call()).andThrow(EXCEPTION);
+
+ control.replay();
+
+ try {
+ executorService.submit(callable).get();
+ fail(EXCEPTION.getClass().getSimpleName() + " should be thrown.");
+ } catch (ExecutionException e) {
+ assertEquals(EXCEPTION, e.getCause());
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNullHandler() throws Exception {
+ control.replay();
+ MoreExecutors.exceptionHandlingExecutor(executorService, null);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingScheduledExecutorServiceTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingScheduledExecutorServiceTest.java b/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingScheduledExecutorServiceTest.java
new file mode 100644
index 0000000..6abe260
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/concurrent/ExceptionHandlingScheduledExecutorServiceTest.java
@@ -0,0 +1,124 @@
+package com.twitter.common.util.concurrent;
+
+import java.lang.Exception;
+import java.lang.IllegalArgumentException;
+import java.lang.NullPointerException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import com.google.common.testing.TearDown;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.testing.easymock.EasyMockTest;
+import com.twitter.common.util.concurrent.MoreExecutors;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ExceptionHandlingScheduledExecutorServiceTest extends EasyMockTest {
+ private static final RuntimeException EXCEPTION = new RuntimeException();
+
+ private ScheduledExecutorService executorService;
+ private Thread.UncaughtExceptionHandler signallingHandler;
+
+ @Before
+ public void setUp() throws Exception {
+ signallingHandler = createMock(Thread.UncaughtExceptionHandler.class);
+ executorService = MoreExecutors.exceptionHandlingExecutor(
+ Executors.newSingleThreadScheduledExecutor(), signallingHandler);
+
+ final ExecutorServiceShutdown executorServiceShutdown = new ExecutorServiceShutdown(
+ executorService, Amount.of(3L, Time.SECONDS));
+
+ addTearDown(new TearDown() {
+ @Override
+ public void tearDown() throws Exception {
+ executorServiceShutdown.execute();
+ }
+ });
+ }
+
+ @Test
+ public void testSubmitRunnable() throws Exception {
+ signallingHandler.uncaughtException(anyObject(Thread.class), eq(EXCEPTION));
+ Runnable runnable = createMock(Runnable.class);
+ runnable.run();
+ expectLastCall().andThrow(EXCEPTION);
+
+ control.replay();
+
+ try {
+ executorService.submit(runnable).get();
+ fail(EXCEPTION.getClass().getSimpleName() + " should be thrown.");
+ } catch (ExecutionException e) {
+ assertEquals(EXCEPTION, e.getCause());
+ }
+ }
+
+ @Test
+ public void testSubmitCallable() throws Exception {
+ signallingHandler.uncaughtException(anyObject(Thread.class), eq(EXCEPTION));
+ Callable<Void> callable = createMock(new Clazz<Callable<Void>>() {});
+ expect(callable.call()).andThrow(EXCEPTION);
+
+ control.replay();
+
+ try {
+ executorService.submit(callable).get();
+ fail(EXCEPTION.getClass().getSimpleName() + " should be thrown.");
+ } catch (ExecutionException e) {
+ assertEquals(EXCEPTION, e.getCause());
+ }
+ }
+
+ @Test
+ public void testScheduleAtFixedRate() throws Exception {
+ signallingHandler.uncaughtException(anyObject(Thread.class), eq(EXCEPTION));
+ Runnable runnable = createMock(Runnable.class);
+ runnable.run();
+ expectLastCall().andThrow(EXCEPTION);
+
+ control.replay();
+
+ try {
+ executorService.scheduleAtFixedRate(runnable, 0, 10, TimeUnit.MILLISECONDS).get();
+ fail(EXCEPTION.getClass().getSimpleName() + " should be thrown.");
+ } catch (ExecutionException e) {
+ assertEquals(EXCEPTION, e.getCause());
+ }
+ }
+
+ @Test
+ public void testScheduleWithFixedDelay() throws Exception {
+ signallingHandler.uncaughtException(anyObject(Thread.class), eq(EXCEPTION));
+ Runnable runnable = createMock(Runnable.class);
+ runnable.run();
+ expectLastCall().andThrow(EXCEPTION);
+
+ control.replay();
+
+ try {
+ executorService.scheduleWithFixedDelay(runnable, 0, 10, TimeUnit.MILLISECONDS).get();
+ fail(EXCEPTION.getClass().getSimpleName() + " should be thrown.");
+ } catch (ExecutionException e) {
+ assertEquals(EXCEPTION, e.getCause());
+ }
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNullHandler() throws Exception {
+ control.replay();
+ MoreExecutors.exceptionHandlingExecutor(executorService, null);
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/templating/StringTemplateHelperTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/templating/StringTemplateHelperTest.java b/commons/src/test/java/com/twitter/common/util/templating/StringTemplateHelperTest.java
new file mode 100644
index 0000000..306288c
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/templating/StringTemplateHelperTest.java
@@ -0,0 +1,80 @@
+package com.twitter.common.util.templating;
+
+import java.io.StringWriter;
+import java.util.Arrays;
+
+import org.antlr.stringtemplate.StringTemplate;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.twitter.common.base.Closure;
+import com.twitter.common.util.templating.StringTemplateHelper.TemplateException;
+
+import static org.junit.Assert.assertEquals;
+
+public class StringTemplateHelperTest {
+
+ private StringTemplateHelper templateHelper;
+
+ @Before
+ public void setUp() {
+ templateHelper = new StringTemplateHelper(getClass(), "template", false);
+ }
+
+ private static class Item {
+ final String name;
+ final int price;
+
+ private Item(String name, int price) {
+ this.name = name;
+ this.price = price;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getPrice() {
+ return price;
+ }
+ }
+
+ @Test
+ public void testFillTemplate() throws Exception {
+ StringWriter output = new StringWriter();
+ templateHelper.writeTemplate(output, new Closure<StringTemplate>() {
+ @Override public void execute(StringTemplate template) {
+ template.setAttribute("header", "Prices");
+ template.setAttribute("items", Arrays.asList(
+ new Item("banana", 50),
+ new Item("car", 2),
+ new Item("jupiter", 200)
+ ));
+ template.setAttribute("footer", "The End");
+ }
+ });
+ String expected = "Prices\n"
+ + "\n The banana costs $50."
+ + "\n The car costs $2."
+ + "\n The jupiter costs $200.\n"
+ + "\n\nThe End";
+ assertEquals(expected, output.toString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testMissingTemplate() throws Exception {
+ new StringTemplateHelper(getClass(), "missing_template", false);
+ }
+
+ private static class CustomException extends RuntimeException {
+ }
+
+ @Test(expected = CustomException.class)
+ public void testClosureError() throws Exception {
+ templateHelper.writeTemplate(new StringWriter(), new Closure<StringTemplate>() {
+ @Override public void execute(StringTemplate template) {
+ throw new CustomException();
+ }
+ });
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/86a547b9/commons/src/test/java/com/twitter/common/util/testing/FakeClockTest.java
----------------------------------------------------------------------
diff --git a/commons/src/test/java/com/twitter/common/util/testing/FakeClockTest.java b/commons/src/test/java/com/twitter/common/util/testing/FakeClockTest.java
new file mode 100644
index 0000000..9ea14fe
--- /dev/null
+++ b/commons/src/test/java/com/twitter/common/util/testing/FakeClockTest.java
@@ -0,0 +1,73 @@
+// =================================================================================================
+// Copyright 2011 Twitter, Inc.
+// -------------------------------------------------------------------------------------------------
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this work except in compliance with the License.
+// You may obtain a copy of the License in the LICENSE file, or 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 com.twitter.common.util.testing;
+
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author John Sirois
+ */
+public class FakeClockTest {
+ private FakeTicker fakeTicker;
+
+ @Before
+ public void setUp() {
+ fakeTicker = new FakeTicker();
+ }
+
+ @Test
+ public void testNow() throws InterruptedException {
+ assertEquals("A fake clock should start out at time 0", 0, fakeTicker.read());
+
+ fakeTicker.setNowNanos(42L);
+ assertEquals("A fake clock's time should only be controled by setNow", 42L, fakeTicker.read());
+
+ long start = System.nanoTime();
+ Thread.sleep(10L);
+ assertTrue(System.nanoTime() - start > 0);
+ assertEquals("A fake clock's time should only be controled by setNow", 42L, fakeTicker.read());
+ }
+
+ @Test
+ public void testWaitFor() {
+ fakeTicker.waitNanos(42L);
+ assertEquals(42L, fakeTicker.read());
+
+ fakeTicker.waitNanos(42L);
+ assertEquals(84L, fakeTicker.read());
+ }
+
+ @Test
+ public void testAdvance() {
+ fakeTicker.advance(Amount.of(42L, Time.NANOSECONDS));
+ assertEquals(42L, fakeTicker.read());
+
+ fakeTicker.advance(Amount.of(42L, Time.NANOSECONDS));
+ assertEquals(84L, fakeTicker.read());
+
+ fakeTicker.advance(Amount.of(-42L, Time.NANOSECONDS));
+ assertEquals(42L, fakeTicker.read());
+
+ fakeTicker.advance(Amount.of(-43L, Time.NANOSECONDS));
+ assertEquals(-1L,fakeTicker.read());
+ }
+}