// =================================================================================================
// 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);
}
}