core/test: sanitize FakeClock

- privatize public final fields and add public setters
- add javadoc and tests
This commit is contained in:
ZHANG Dapeng 2016-09-30 16:18:37 -07:00 committed by GitHub
parent e46cb8193f
commit 93dd02ca9c
10 changed files with 233 additions and 42 deletions

View File

@ -236,7 +236,7 @@ public class ContextsTest {
public void statusFromCancelled_TimeoutExceptionShouldMapToDeadlineExceeded() {
FakeClock fakeClock = new FakeClock();
Context.CancellableContext cancellableContext = Context.current()
.withDeadlineAfter(100, TimeUnit.MILLISECONDS, fakeClock.scheduledExecutorService);
.withDeadlineAfter(100, TimeUnit.MILLISECONDS, fakeClock.getScheduledExecutorService());
fakeClock.forwardTime(System.nanoTime(), TimeUnit.NANOSECONDS);
fakeClock.forwardMillis(100);

View File

@ -107,7 +107,7 @@ public class ClientCallImplTest {
private final FakeClock fakeClock = new FakeClock();
private final ScheduledExecutorService deadlineCancellationExecutor =
fakeClock.scheduledExecutorService;
fakeClock.getScheduledExecutorService();
private final DecompressorRegistry decompressorRegistry =
DecompressorRegistry.getDefaultInstance().with(new Codec.Gzip(), true);
private final MethodDescriptor<Void, Void> method = MethodDescriptor.create(

View File

@ -72,7 +72,7 @@ public class ConnectivityStateManagerTest {
public void run() {
sink.add(state.getState());
}
}, executor.scheduledExecutorService, ConnectivityState.CONNECTING);
}, executor.getScheduledExecutorService(), ConnectivityState.CONNECTING);
assertEquals(0, executor.numPendingTasks());
state.gotoState(ConnectivityState.TRANSIENT_FAILURE);
@ -94,7 +94,7 @@ public class ConnectivityStateManagerTest {
public void run() {
sink.add(state.getState());
}
}, executor.scheduledExecutorService, ConnectivityState.IDLE);
}, executor.getScheduledExecutorService(), ConnectivityState.IDLE);
// Make sure the callback is run in the executor
assertEquals(0, sink.size());
@ -111,7 +111,7 @@ public class ConnectivityStateManagerTest {
public void run() {
sink.add(state.getState());
}
}, executor.scheduledExecutorService, ConnectivityState.IDLE);
}, executor.getScheduledExecutorService(), ConnectivityState.IDLE);
state.gotoState(ConnectivityState.IDLE);
assertEquals(0, executor.numPendingTasks());
@ -127,7 +127,7 @@ public class ConnectivityStateManagerTest {
}
};
state.notifyWhenStateChanged(callback, executor.scheduledExecutorService,
state.notifyWhenStateChanged(callback, executor.getScheduledExecutorService(),
ConnectivityState.IDLE);
// First transition triggers the callback
state.gotoState(ConnectivityState.CONNECTING);
@ -142,7 +142,7 @@ public class ConnectivityStateManagerTest {
assertEquals(0, executor.numPendingTasks());
// Register another callback
state.notifyWhenStateChanged(callback, executor.scheduledExecutorService,
state.notifyWhenStateChanged(callback, executor.getScheduledExecutorService(),
ConnectivityState.TRANSIENT_FAILURE);
state.gotoState(ConnectivityState.READY);
@ -164,21 +164,21 @@ public class ConnectivityStateManagerTest {
sink.add(state.getState());
callbackRuns.add("callback1");
}
}, executor.scheduledExecutorService, ConnectivityState.IDLE);
}, executor.getScheduledExecutorService(), ConnectivityState.IDLE);
state.notifyWhenStateChanged(new Runnable() {
@Override
public void run() {
sink.add(state.getState());
callbackRuns.add("callback2");
}
}, executor.scheduledExecutorService, ConnectivityState.IDLE);
}, executor.getScheduledExecutorService(), ConnectivityState.IDLE);
state.notifyWhenStateChanged(new Runnable() {
@Override
public void run() {
sink.add(state.getState());
callbackRuns.add("callback3");
}
}, executor.scheduledExecutorService, ConnectivityState.READY);
}, executor.getScheduledExecutorService(), ConnectivityState.READY);
// callback3 is run immediately because the source state is already different from the current
// state.
@ -213,8 +213,8 @@ public class ConnectivityStateManagerTest {
@Test
public void registerCallbackFromCallback() {
state.notifyWhenStateChanged(newRecursiveCallback(executor.scheduledExecutorService),
executor.scheduledExecutorService, state.getState());
state.notifyWhenStateChanged(newRecursiveCallback(executor.getScheduledExecutorService()),
executor.getScheduledExecutorService(), state.getState());
state.gotoState(ConnectivityState.CONNECTING);
assertEquals(1, executor.runDueTasks());

View File

@ -97,7 +97,7 @@ public class DelayedClientTransportTest {
private final FakeClock fakeExecutor = new FakeClock();
private final DelayedClientTransport delayedTransport = new DelayedClientTransport(
fakeExecutor.scheduledExecutorService);
fakeExecutor.getScheduledExecutorService());
@Before public void setUp() {
MockitoAnnotations.initMocks(this);

View File

@ -82,7 +82,7 @@ public class DnsNameResolverTest {
new Resource<ScheduledExecutorService>() {
@Override
public ScheduledExecutorService create() {
return fakeClock.scheduledExecutorService;
return fakeClock.getScheduledExecutorService();
}
@Override
@ -95,7 +95,7 @@ public class DnsNameResolverTest {
new Resource<ExecutorService>() {
@Override
public ExecutorService create() {
return fakeExecutor.scheduledExecutorService;
return fakeExecutor.getScheduledExecutorService();
}
@Override

View File

@ -57,20 +57,23 @@ import java.util.concurrent.TimeUnit;
*/
public final class FakeClock {
public final ScheduledExecutorService scheduledExecutorService = new ScheduledExecutorImpl();
final Ticker ticker = new Ticker() {
@Override public long read() {
return currentTimeNanos;
}
};
final Supplier<Stopwatch> stopwatchSupplier = new Supplier<Stopwatch>() {
@Override public Stopwatch get() {
return Stopwatch.createUnstarted(ticker);
}
};
private final ScheduledExecutorService scheduledExecutorService = new ScheduledExecutorImpl();
private final PriorityQueue<ScheduledTask> tasks = new PriorityQueue<ScheduledTask>();
private final Ticker ticker =
new Ticker() {
@Override public long read() {
return currentTimeNanos;
}
};
private final Supplier<Stopwatch> stopwatchSupplier =
new Supplier<Stopwatch>() {
@Override public Stopwatch get() {
return Stopwatch.createUnstarted(ticker);
}
};
private long currentTimeNanos;
private class ScheduledTask extends AbstractFuture<Void> implements ScheduledFuture<Void> {
@ -189,6 +192,21 @@ public final class FakeClock {
}
}
/**
* Provides a partially implemented instance of {@link ScheduledExecutorService} that uses the
* fake clock ticker for testing.
*/
public ScheduledExecutorService getScheduledExecutorService() {
return scheduledExecutorService;
}
/**
* Provides a stopwatch instance that uses the fake clock ticker.
*/
public Supplier<Stopwatch> getStopwatchSupplier() {
return stopwatchSupplier;
}
/**
* Run all due tasks.
*

View File

@ -0,0 +1,172 @@
/*
* Copyright 2016, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.grpc.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.google.common.base.Stopwatch;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/** Unit tests for {@link FakeClock}. */
@RunWith(JUnit4.class)
public class FakeClockTest {
@Test
public void testScheduledExecutorService_sameInstance() {
FakeClock fakeClock = new FakeClock();
ScheduledExecutorService scheduledExecutorService1 = fakeClock.getScheduledExecutorService();
ScheduledExecutorService scheduledExecutorService2 = fakeClock.getScheduledExecutorService();
assertTrue(scheduledExecutorService1 == scheduledExecutorService2);
}
@Test
public void testScheduledExecutorService_isDone() {
FakeClock fakeClock = new FakeClock();
ScheduledFuture<?> future = fakeClock.getScheduledExecutorService()
.schedule(newRunnable(), 100L, TimeUnit.MILLISECONDS);
fakeClock.forwardMillis(99L);
assertFalse(future.isDone());
fakeClock.forwardMillis(2L);
assertTrue(future.isDone());
}
@Test
public void testScheduledExecutorService_cancel() {
FakeClock fakeClock = new FakeClock();
ScheduledFuture<?> future = fakeClock.getScheduledExecutorService()
.schedule(newRunnable(), 100L, TimeUnit.MILLISECONDS);
fakeClock.forwardMillis(99L);
future.cancel(false);
fakeClock.forwardMillis(2);
assertTrue(future.isCancelled());
}
@Test
public void testScheduledExecutorService_getDelay() {
FakeClock fakeClock = new FakeClock();
ScheduledFuture<?> future = fakeClock.getScheduledExecutorService()
.schedule(newRunnable(), 100L, TimeUnit.MILLISECONDS);
fakeClock.forwardMillis(90L);
assertEquals(10L, future.getDelay(TimeUnit.MILLISECONDS));
}
@Test
public void testScheduledExecutorService_result() {
FakeClock fakeClock = new FakeClock();
final boolean[] result = new boolean[]{false};
fakeClock.getScheduledExecutorService().schedule(
new Runnable() {
@Override
public void run() {
result[0] = true;
}
},
100L,
TimeUnit.MILLISECONDS);
fakeClock.forwardMillis(100L);
assertTrue(result[0]);
}
@Test
public void testStopWatch() {
FakeClock fakeClock = new FakeClock();
Stopwatch stopwatch = fakeClock.getStopwatchSupplier().get();
long expectedElapsedMillis = 0L;
stopwatch.start();
fakeClock.forwardMillis(100L);
expectedElapsedMillis += 100L;
assertEquals(expectedElapsedMillis, stopwatch.elapsed(TimeUnit.MILLISECONDS));
fakeClock.forwardTime(10L, TimeUnit.MINUTES);
expectedElapsedMillis += TimeUnit.MINUTES.toMillis(10L);
assertEquals(expectedElapsedMillis, stopwatch.elapsed(TimeUnit.MILLISECONDS));
stopwatch.stop();
fakeClock.forwardMillis(1000L);
assertEquals(expectedElapsedMillis, stopwatch.elapsed(TimeUnit.MILLISECONDS));
stopwatch.reset();
expectedElapsedMillis = 0L;
assertEquals(expectedElapsedMillis, stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
@Test
public void testPendingAndDueTasks() {
FakeClock fakeClock = new FakeClock();
ScheduledExecutorService scheduledExecutorService = fakeClock.getScheduledExecutorService();
scheduledExecutorService.execute(newRunnable());
scheduledExecutorService.schedule(newRunnable(), 0L, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(newRunnable(), 100L, TimeUnit.MILLISECONDS);
scheduledExecutorService.schedule(newRunnable(), 200L, TimeUnit.MILLISECONDS);
assertEquals(4, fakeClock.numPendingTasks());
assertEquals(2, fakeClock.getDueTasks().size());
fakeClock.runDueTasks();
assertEquals(2, fakeClock.numPendingTasks());
assertEquals(0, fakeClock.getDueTasks().size());
fakeClock.forwardMillis(100L);
assertEquals(1, fakeClock.numPendingTasks());
assertEquals(0, fakeClock.getDueTasks().size());
}
private Runnable newRunnable() {
return new Runnable() {
@Override
public void run() {
}
};
}
}

View File

@ -124,7 +124,7 @@ public class ManagedChannelImplIdlenessTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(timerService.create()).thenReturn(timer.scheduledExecutorService);
when(timerService.create()).thenReturn(timer.getScheduledExecutorService());
when(mockLoadBalancerFactory
.newLoadBalancer(anyString(), Matchers.<TransportManager<ClientTransport>>any()))
.thenReturn(mockLoadBalancer);
@ -136,9 +136,9 @@ public class ManagedChannelImplIdlenessTest {
channel = new ManagedChannelImpl("fake://target", new FakeBackoffPolicyProvider(),
mockNameResolverFactory, Attributes.EMPTY, mockLoadBalancerFactory,
mockTransportFactory, DecompressorRegistry.getDefaultInstance(),
CompressorRegistry.getDefaultInstance(), timerService, timer.stopwatchSupplier,
CompressorRegistry.getDefaultInstance(), timerService, timer.getStopwatchSupplier(),
TimeUnit.SECONDS.toMillis(IDLE_TIMEOUT_SECONDS),
executor.scheduledExecutorService, userAgent,
executor.getScheduledExecutorService(), userAgent,
Collections.<ClientInterceptor>emptyList());
newTransports = TestUtils.captureTransports(mockTransportFactory);

View File

@ -159,9 +159,9 @@ public class ManagedChannelImplTest {
channel = new ManagedChannelImpl(target, new FakeBackoffPolicyProvider(),
nameResolverFactory, NAME_RESOLVER_PARAMS, loadBalancerFactory,
mockTransportFactory, DecompressorRegistry.getDefaultInstance(),
CompressorRegistry.getDefaultInstance(), timerService, timer.stopwatchSupplier,
CompressorRegistry.getDefaultInstance(), timerService, timer.getStopwatchSupplier(),
ManagedChannelImpl.IDLE_TIMEOUT_MILLIS_DISABLE,
executor.scheduledExecutorService, userAgent, interceptors);
executor.getScheduledExecutorService(), userAgent, interceptors);
// Force-exit the initial idle-mode
channel.exitIdleMode();
// Will start NameResolver in the scheduled executor
@ -175,7 +175,7 @@ public class ManagedChannelImplTest {
when(mockTransportFactory.newClientTransport(
any(SocketAddress.class), any(String.class), any(String.class)))
.thenReturn(mockTransport);
when(timerService.create()).thenReturn(timer.scheduledExecutorService);
when(timerService.create()).thenReturn(timer.getScheduledExecutorService());
}
@After
@ -506,7 +506,8 @@ public class ManagedChannelImplTest {
.thenReturn(mockStream);
FakeClock callExecutor = new FakeClock();
createChannel(new FakeNameResolverFactory(true), NO_INTERCEPTOR);
CallOptions options = CallOptions.DEFAULT.withExecutor(callExecutor.scheduledExecutorService);
CallOptions options =
CallOptions.DEFAULT.withExecutor(callExecutor.getScheduledExecutorService());
ClientCall<String, Integer> call = channel.newCall(method, options);
call.start(mockCallListener, headers);
@ -886,7 +887,7 @@ public class ManagedChannelImplTest {
ArgumentCaptor<Attributes> attrsCaptor = ArgumentCaptor.forClass(Attributes.class);
ArgumentCaptor<MetadataApplier> applierCaptor = ArgumentCaptor.forClass(MetadataApplier.class);
verify(creds).applyRequestMetadata(same(method), attrsCaptor.capture(),
same(executor.scheduledExecutorService), applierCaptor.capture());
same(executor.getScheduledExecutorService()), applierCaptor.capture());
assertEquals("testValue", testKey.get(credsApplyContexts.poll()));
assertEquals(authority, attrsCaptor.getValue().get(CallCredentials.ATTR_AUTHORITY));
assertEquals(SecurityLevel.NONE,
@ -909,7 +910,7 @@ public class ManagedChannelImplTest {
call.start(mockCallListener, new Metadata());
verify(creds, times(2)).applyRequestMetadata(same(method), attrsCaptor.capture(),
same(executor.scheduledExecutorService), applierCaptor.capture());
same(executor.getScheduledExecutorService()), applierCaptor.capture());
assertEquals("testValue", testKey.get(credsApplyContexts.poll()));
assertEquals(authority, attrsCaptor.getValue().get(CallCredentials.ATTR_AUTHORITY));
assertEquals(SecurityLevel.NONE,

View File

@ -758,14 +758,14 @@ public class TransportSetTest {
transportSet.shutdown();
startBackoffAndShutdownAreCalled[0] = true;
}
fakeExecutor.scheduledExecutorService.execute(command);
fakeExecutor.getScheduledExecutorService().execute(command);
}
};
SocketAddress addr = mock(SocketAddress.class);
addressGroup = new EquivalentAddressGroup(Arrays.asList(addr));
transportSet = new TransportSet(addressGroup, AUTHORITY, USER_AGENT, mockLoadBalancer,
mockBackoffPolicyProvider, mockTransportFactory, fakeClock.scheduledExecutorService,
fakeClock.stopwatchSupplier, executor, mockTransportSetCallback);
mockBackoffPolicyProvider, mockTransportFactory, fakeClock.getScheduledExecutorService(),
fakeClock.getStopwatchSupplier(), executor, mockTransportSetCallback);
// Attempt and fail, scheduleBackoff should be triggered,
// and transportSet.shutdown should be triggered by setup
@ -782,8 +782,8 @@ public class TransportSetTest {
private void createTransportSet(SocketAddress ... addrs) {
addressGroup = new EquivalentAddressGroup(Arrays.asList(addrs));
transportSet = new TransportSet(addressGroup, AUTHORITY, USER_AGENT, mockLoadBalancer,
mockBackoffPolicyProvider, mockTransportFactory, fakeClock.scheduledExecutorService,
fakeClock.stopwatchSupplier, fakeExecutor.scheduledExecutorService,
mockBackoffPolicyProvider, mockTransportFactory, fakeClock.getScheduledExecutorService(),
fakeClock.getStopwatchSupplier(), fakeExecutor.getScheduledExecutorService(),
mockTransportSetCallback);
}
}