Java tutorial
/* * Copyright 2010-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cloudfoundry.tools.timeout; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import javax.servlet.http.HttpServletResponse; import org.cloudfoundry.tools.timeout.HotSwappingTimeoutProtectionStrategy.RequestCoordinator; import org.cloudfoundry.tools.timeout.HotSwappingTimeoutProtectionStrategy.RequestCoordinators; import org.cloudfoundry.tools.timeout.monitor.HttpServletResponseMonitor; import org.cloudfoundry.tools.timeout.monitor.HttpServletResponseMonitorFactory; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.util.ReflectionUtils; /** * Tests for {@link HotSwappingTimeoutProtectionStrategy}. * * @author Phillip Webb */ public class HotSwappingTimeoutProtectionStrategyTest { private static final String UUID = "xxxx-xxxx-xxxx-xxxx"; private static final long FAIL_TIMEOUT = 100; private static final long LONG_POLL_TIME = 200; private static final long THRESHOLD = 300; @Rule public ExpectedException thrown = ExpectedException.none(); private HotSwappingTimeoutProtectionStrategy strategy = new HotSwappingTimeoutProtectionStrategy(); @Mock private TimeoutProtectionHttpRequest request; @Mock private HttpServletResponse response; @Mock private RequestCoordinators requestCoordinators; @Mock private RequestCoordinator requestCoordinator; private RequestCoordinator realRequestCoordinator = new RequestCoordinator(); @Before public void setup() { MockitoAnnotations.initMocks(this); this.strategy.setRequestCoordinators(this.requestCoordinators); this.strategy.setThreshold(THRESHOLD); this.strategy.setFailTimeout(FAIL_TIMEOUT); this.strategy.setLongPollTime(LONG_POLL_TIME); given(this.requestCoordinators.get(this.request)).willReturn(this.requestCoordinator); given(this.request.getUid()).willReturn(UUID); } @Test public void shouldNotUseMonitorIfUnderThreshold() throws Exception { this.strategy.setThreshold(100); HttpServletResponseMonitorFactory monitorFactory = this.strategy.handleRequest(this.request); HttpServletResponseMonitor monitor = monitorFactory.getMonitor(); assertThat(monitor, is(nullValue())); } @Test public void shouldMonitorIfOverThreshold() throws Exception { this.strategy.setThreshold(100); given(this.requestCoordinator.consumePollResponse()).willReturn(this.response); HttpServletResponseMonitorFactory monitorFactory = this.strategy.handleRequest(this.request); Thread.sleep(150); HttpServletResponseMonitor monitor = monitorFactory.getMonitor(); assertThat(monitor, is(not(nullValue()))); } @Test public void shouldConsumePollResponseIfAlreadyAvailble() throws Exception { this.strategy.setThreshold(0); given(this.requestCoordinator.consumePollResponse()).willReturn(this.response); HttpServletResponseMonitorFactory monitorFactory = this.strategy.handleRequest(this.request); HttpServletResponseMonitor monitor = monitorFactory.getMonitor(); assertThat(monitor, is(not(nullValue()))); } @Test public void shouldAwaitPollResponse() throws Exception { this.strategy.setThreshold(0); given(this.requestCoordinator.consumePollResponse()).willReturn(null, this.response); HttpServletResponseMonitorFactory monitorFactory = this.strategy.handleRequest(this.request); HttpServletResponseMonitor monitor = monitorFactory.getMonitor(); assertThat(monitor, is(not(nullValue()))); verify(this.requestCoordinator).awaitPollResponse(FAIL_TIMEOUT); } @Test public void shouldTimeoutAwaitingPollResponse() throws Exception { this.strategy.setThreshold(0); given(this.requestCoordinator.consumePollResponse()).willReturn(null); willThrow(new InterruptedException()).given(this.requestCoordinator).awaitPollResponse(FAIL_TIMEOUT); HttpServletResponseMonitorFactory monitorFactory = this.strategy.handleRequest(this.request); this.thrown.expect(IllegalStateException.class); this.thrown.expectMessage("Timeout waiting for poll"); monitorFactory.getMonitor(); } @Test public void shouldDeleteUnconsumedPollResponse() throws Exception { HttpServletResponseMonitorFactory monitorFactory = mock(HttpServletResponseMonitorFactory.class); given(this.requestCoordinator.isPollResponseConsumed()).willReturn(false); this.strategy.afterRequest(this.request, monitorFactory); verify(this.requestCoordinator).finish(); verify(this.requestCoordinators).delete(this.request); } @Test public void shouldHandleUnconsumedPoll() throws Exception { given(this.requestCoordinator.isPollResponseConsumed()).willReturn(false); this.strategy.handlePoll(this.request, this.response); verify(this.requestCoordinator).clearPollResponse(); verify(this.response).setHeader(TimeoutProtectionHttpHeader.POLL, UUID); verify(this.response).setStatus(204); } @Test public void shouldHandleConsumedPollResponse() throws Exception { given(this.requestCoordinator.isPollResponseConsumed()).willReturn(true); this.strategy.handlePoll(this.request, this.response); verify(this.requestCoordinator).awaitFinish(FAIL_TIMEOUT); verify(this.requestCoordinators).delete(this.request); } @Test public void shouldHandleInterruptedWaitingForPollResponseConsumed() throws Exception { willThrow(new InterruptedException()).given(this.requestCoordinator) .awaitPollReponseConsumed(LONG_POLL_TIME); given(this.requestCoordinator.isPollResponseConsumed()).willReturn(false); this.strategy.handlePoll(this.request, this.response); verify(this.requestCoordinator).clearPollResponse(); verify(this.response).setHeader(TimeoutProtectionHttpHeader.POLL, UUID); verify(this.response).setStatus(204); } @Test public void shouldHandleInterruptedWatingForAfterRequest() throws Exception { given(this.requestCoordinator.isPollResponseConsumed()).willReturn(true); willThrow(new InterruptedException()).given(this.requestCoordinator).awaitFinish(FAIL_TIMEOUT); try { this.strategy.handlePoll(this.request, this.response); fail("Did not throw"); } catch (IllegalStateException e) { // Expected assertThat(e.getMessage(), is("Timeout waiting for cleanup")); } verify(this.requestCoordinators).delete(this.request); } @Test public void shouldGetRequestCoordinator() throws Exception { RequestCoordinators requestCoordinators = new RequestCoordinators(); RequestCoordinator initial = requestCoordinators.get(this.request); RequestCoordinator subsequent = requestCoordinators.get(this.request); assertThat(initial, is(sameInstance(subsequent))); } @Test public void shouldDeleteRequestCoordinator() throws Exception { RequestCoordinators requestCoordinators = new RequestCoordinators(); RequestCoordinator initial = requestCoordinators.get(this.request); requestCoordinators.delete(this.request); RequestCoordinator subsequent = requestCoordinators.get(this.request); assertThat(initial, is(not(sameInstance(subsequent)))); } @Test public void shouldCoordinateSetPollResponseBeforeAwait() throws Exception { this.realRequestCoordinator.setPollResponse(this.response); ThreadAssertion assertion = expectResponseWithin(0, 100, new Call() { public void call() throws Exception { HotSwappingTimeoutProtectionStrategyTest.this.realRequestCoordinator.awaitPollResponse(1000); } }); assertion.verify(); } @Test public void shouldCoordinateSetPollResponseAfterAwait() throws Exception { ThreadAssertion assertion = expectResponseWithin(50, 100, new Call() { public void call() throws Exception { HotSwappingTimeoutProtectionStrategyTest.this.realRequestCoordinator.awaitPollResponse(1000); } }); Thread.sleep(50); this.realRequestCoordinator.setPollResponse(this.response); assertion.verify(); } @Test public void shouldCoordinateConsumePollResponseBeforeAwait() throws Exception { this.realRequestCoordinator.setPollResponse(this.response); this.realRequestCoordinator.consumePollResponse(); ThreadAssertion assertion = expectResponseWithin(0, 100, new Call() { public void call() throws Exception { HotSwappingTimeoutProtectionStrategyTest.this.realRequestCoordinator.awaitPollReponseConsumed(1000); } }); assertion.verify(); } @Test public void shouldCoordinateConsumePollResponseAfterAwait() throws Exception { this.realRequestCoordinator.setPollResponse(this.response); ThreadAssertion assertion = expectResponseWithin(50, 100, new Call() { public void call() throws Exception { HotSwappingTimeoutProtectionStrategyTest.this.realRequestCoordinator.awaitPollReponseConsumed(1000); } }); Thread.sleep(50); this.realRequestCoordinator.consumePollResponse(); assertion.verify(); } @Test public void shouldCoordinateCleanupBeforeAwait() throws Exception { this.realRequestCoordinator.finish(); ThreadAssertion assertion = expectResponseWithin(0, 100, new Call() { public void call() throws Exception { HotSwappingTimeoutProtectionStrategyTest.this.realRequestCoordinator.awaitFinish(1000); } }); assertion.verify(); } @Test public void shouldCoordinateCleanupAfterAwait() throws Exception { ThreadAssertion assertion = expectResponseWithin(50, 100, new Call() { public void call() throws Exception { HotSwappingTimeoutProtectionStrategyTest.this.realRequestCoordinator.awaitFinish(1000); } }); Thread.sleep(50); this.realRequestCoordinator.finish(); assertion.verify(); } @Test public void shouldCoordinateIsPollResponseConsumed() throws Exception { assertThat(this.realRequestCoordinator.isPollResponseConsumed(), is(false)); // Consume without set this.realRequestCoordinator.consumePollResponse(); assertThat(this.realRequestCoordinator.isPollResponseConsumed(), is(false)); this.realRequestCoordinator.setPollResponse(this.response); assertThat(this.realRequestCoordinator.isPollResponseConsumed(), is(false)); this.realRequestCoordinator.consumePollResponse(); assertThat(this.realRequestCoordinator.isPollResponseConsumed(), is(true)); } @Test public void shouldCoordinateClearPollResponse() throws Exception { this.realRequestCoordinator.setPollResponse(this.response); this.realRequestCoordinator.clearPollResponse(); assertThat(this.realRequestCoordinator.consumePollResponse(), is(nullValue())); } @Test public void shouldNotCoordinateClearPollResponseIfConsumed() throws Exception { this.realRequestCoordinator.setPollResponse(this.response); assertThat(this.realRequestCoordinator.consumePollResponse(), is(this.response)); this.thrown.expect(IllegalStateException.class); this.thrown.expectMessage("Unable to clear an already consumed poll response"); this.realRequestCoordinator.clearPollResponse(); } private ThreadAssertion expectResponseWithin(long min, long max, Call call) { return new RunWithinTimeThreadAssertion(min, max, call); } private static interface Call { public void call() throws Exception; } private static interface ThreadAssertion { public void verify() throws Exception; } private static class RunWithinTimeThreadAssertion implements ThreadAssertion { private long min; private long max; private Thread thread; private long runTime; private Throwable exception; public RunWithinTimeThreadAssertion(final long min, final long max, final Call call) { this.min = min; this.max = max; this.thread = new Thread() { @Override public void run() { try { long startTime = System.currentTimeMillis(); call.call(); RunWithinTimeThreadAssertion.this.runTime = System.currentTimeMillis() - startTime; } catch (Throwable e) { RunWithinTimeThreadAssertion.this.exception = e; } } }; this.thread.start(); } public void verify() throws Exception { this.thread.join(); if (this.exception != null) { ReflectionUtils.rethrowRuntimeException(this.exception); } assertThat(this.runTime, is(greaterThanOrEqualTo(this.min))); assertThat(this.runTime, is(lessThan(this.max))); } } }