package org.dcache.services.info.base;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.BDDMockito.*;
/**
* A set of tests to check that the StateMaintainer works as expected.
*/
public class StateMaintainerTests
{
private ExpungeAwareStateMaintainer _maintainer;
private StateCaretaker _caretaker;
/**
* A StateUpdate that allows waiting for an update to be
* processed.
*/
private class AwaitableStateUpdate extends StateUpdate
{
private final CountDownLatch _latch = new CountDownLatch(1);
@Override
public void updateComplete()
{
_latch.countDown();
}
public void await() throws InterruptedException
{
_latch.await();
}
}
/**
* A StateMaintainer that allows awaiting for at least one expunge
* cycle.
*/
private class ExpungeAwareStateMaintainer extends StateMaintainer
{
private final CountDownLatch _latch = new CountDownLatch(1);
@Override
public void expungeCompleted()
{
_latch.countDown();
}
public void awaitExpunge() throws InterruptedException
{
_latch.await();
}
}
@Before
public void setUp()
{
_caretaker = mock(StateCaretaker.class);
_maintainer = new ExpungeAwareStateMaintainer();
_maintainer.setCaretaker(_caretaker);
_maintainer.setExecutor(Executors.newSingleThreadScheduledExecutor());
}
@After
public void tearDown()
{
_maintainer.shutdown();
}
@Test
public void shouldBeInitiallyEmptyQueueSize() throws InterruptedException
{
assertThat(_maintainer.countPendingUpdates(), is(0));
}
@Test(timeout = 10_000)
public void shouldIncrementAfterSubmittingUpdate() throws InterruptedException
{
Object monitor = new Object();
willAnswer(a -> {
synchronized (monitor) {
monitor.wait();
}
return null;
}).given(_caretaker).processUpdate(anyObject());
_maintainer.enqueueUpdate(new StateUpdate());
assertThat(_maintainer.countPendingUpdates(), is(1));
}
@Test(timeout = 10_000)
public void shouldIncreaseAfterSubmittingUpdateWhenQueuedUpdate() throws InterruptedException
{
willAnswer(i -> {wait(); return null;}).given(_caretaker).processUpdate(anyObject());
_maintainer.enqueueUpdate(new StateUpdate());
_maintainer.enqueueUpdate(new StateUpdate());
assertThat(_maintainer.countPendingUpdates(), is(2));
}
@Test(timeout = 10_000)
public void shouldDecreaseAfterProcessed() throws InterruptedException
{
AwaitableStateUpdate update = new AwaitableStateUpdate();
_maintainer.enqueueUpdate(update);
update.await();
assertThat(_maintainer.countPendingUpdates(), is(0));
}
@Test(timeout = 10_000)
public void shouldDecreaseAfterProcessingTwoUpdates() throws InterruptedException
{
AwaitableStateUpdate update = new AwaitableStateUpdate();
_maintainer.enqueueUpdate(new StateUpdate());
_maintainer.enqueueUpdate(update);
update.await();
assertThat(_maintainer.countPendingUpdates(), is(0));
}
@Test(timeout = 10_000)
public void shouldCheckExpiring() throws InterruptedException
{
// We need an update to trigger the update of expire date.
AwaitableStateUpdate update = new AwaitableStateUpdate();
_maintainer.enqueueUpdate(update);
update.await();
verify(_caretaker).getEarliestMetricExpiryDate();
verify(_caretaker, never()).removeExpiredMetrics();
}
@Test(timeout = 10_000)
public void shouldExpireAfterDelay() throws InterruptedException
{
given(_caretaker.getEarliestMetricExpiryDate()).willReturn(new Date());
// We need to enqueue something to trigger the update of expire date.
AwaitableStateUpdate update = new AwaitableStateUpdate();
_maintainer.enqueueUpdate(update);
update.await();
_maintainer.awaitExpunge();
verify(_caretaker, atLeast(1)).getEarliestMetricExpiryDate();
verify(_caretaker, atLeast(1)).removeExpiredMetrics();
}
}