package org.infinispan.xsite.offline; import static java.lang.String.format; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedDeque; import org.infinispan.configuration.cache.TakeOfflineConfiguration; import org.infinispan.configuration.cache.TakeOfflineConfigurationBuilder; import org.infinispan.test.AbstractInfinispanTest; import org.infinispan.util.ControlledTimeService; import org.infinispan.xsite.OfflineStatus; import org.infinispan.xsite.notification.SiteStatusListener; import org.testng.annotations.Test; /** * @author Mircea Markus * @since 5.2 */ @Test (groups = "xsite", testName = "xsite.offline.OfflineStatusTest") public class OfflineStatusTest extends AbstractInfinispanTest { public void timeBasedTakeOffline() { //Tests if the offline status changes when the minimum time has elapsed and after failure counter. final long minTimeWait = 3000; final int minFailures = 10; final TestContext context = createNew(minTimeWait, minFailures); //first part, we reached the min failures but not the min time. for (int i = 0; i < minFailures + 1; i++) { assertOffline(context, false); addCommunicationFailure(context); } assertMinFailureCount(context, minFailures + 1); assertMinTimeElapsed(context, false); assertOffline(context, false); context.timeService.advance(minTimeWait + 1); assertMinTimeElapsed(context, true); assertMinFailureCount(context, minFailures + 1); assertOffline(context, true); context.offlineStatus.reset(); //reset everything //second part, we reached the min time, but not the failures- for (int i = 0; i < minFailures -1; i++) { assertOffline(context, false); addCommunicationFailure(context); } assertMinFailureCount(context, minFailures -1); assertMinTimeElapsed(context, false); assertOffline(context, false); context.timeService.advance(minTimeWait + 1); assertMinFailureCount(context, minFailures -1); assertMinTimeElapsed(context, true); assertOffline(context, false); addCommunicationFailure(context); assertMinTimeElapsed(context, true); assertMinFailureCount(context, minFailures); assertOffline(context, true); context.listener.check(SiteStatus.OFFLINE, SiteStatus.ONLINE, SiteStatus.OFFLINE); } public void testFailureBasedOnly() throws Throwable { //note: can't check the min time since it throws an IllegalStateException when disabled final int minFailures = 10; final TestContext context = createNew(0, minFailures); for (int i = 0; i < minFailures - 1; i++) { assertOffline(context, false); addCommunicationFailure(context); } assertMinFailureCount(context, minFailures - 1); assertOffline(context, false); context.timeService.advance(1); assertMinFailureCount(context, minFailures - 1); assertOffline(context, false); addCommunicationFailure(context); assertMinFailureCount(context, minFailures); assertOffline(context, true); context.listener.check(SiteStatus.OFFLINE); } public void testTimeBasedOnly() throws Throwable { final long minWaitTime = 3000; final int minFailures = 10; final TestContext context = createNew(minWaitTime, -1); for (int i = 0; i < minFailures; i++) { assertOffline(context, false); addCommunicationFailure(context); } assertMinFailureCount(context, minFailures); assertMinTimeElapsed(context, false); assertOffline(context, false); context.timeService.advance(minWaitTime + 1); assertMinFailureCount(context, minFailures); assertMinTimeElapsed(context, true); assertOffline(context, true); context.listener.check(SiteStatus.OFFLINE); } public void testForceOffline() { //note: can't check the min time since it throws an IllegalStateException when disabled final TestContext context = createNew(-1, -1); addCommunicationFailure(context); context.timeService.advance(1); assertMinFailureCount(context, 1); assertOffline(context, false); context.offlineStatus.forceOffline(); assertMinFailureCount(context, 1); assertOffline(context, true); //test bring online context.offlineStatus.bringOnline(); assertMinFailureCount(context, 0); assertOffline(context, false); addCommunicationFailure(context); context.timeService.advance(1); assertMinFailureCount(context, 1); assertOffline(context, false); context.offlineStatus.forceOffline(); assertMinFailureCount(context, 1); assertOffline(context, true); //test reset context.offlineStatus.reset(); assertMinFailureCount(context, 0); assertOffline(context, false); context.listener.check(SiteStatus.OFFLINE, SiteStatus.ONLINE, SiteStatus.OFFLINE, SiteStatus.ONLINE); } private static void assertOffline(TestContext context, boolean expected) { assertEquals("Checking offline.", expected, context.offlineStatus.isOffline()); } private static void assertMinFailureCount(TestContext context, int expected) { assertEquals("Check failure count.", expected, context.offlineStatus.getFailureCount()); } private static void assertMinTimeElapsed(TestContext context, boolean expected) { assertEquals(format("Check min time has elapsed. Current time=%d. Time elapsed=%d", context.timeService.time(), context.offlineStatus.millisSinceFirstFailure()), expected, context.offlineStatus.minTimeHasElapsed()); } private static void addCommunicationFailure(TestContext context) { context.offlineStatus.updateOnCommunicationFailure(context.timeService.wallClockTime()); } private static TestContext createNew(long minWait, int afterFailures) { ControlledTimeService t = new ControlledTimeService(); ListenerImpl l = new ListenerImpl(); TakeOfflineConfiguration c = new TakeOfflineConfigurationBuilder(null, null) .afterFailures(afterFailures) .minTimeToWait(minWait) .create(); return new TestContext(new OfflineStatus(c, t, l), t, l); } private static class TestContext { private final OfflineStatus offlineStatus; private final ControlledTimeService timeService; private final ListenerImpl listener; private TestContext(OfflineStatus offlineStatus, ControlledTimeService timeService, ListenerImpl listener) { this.offlineStatus = offlineStatus; this.timeService = timeService; this.listener = listener; } } private static class ListenerImpl implements SiteStatusListener { private final Queue<SiteStatus> notifications; private ListenerImpl() { notifications = new ConcurrentLinkedDeque<>(); } @Override public void siteOnline() { notifications.add(SiteStatus.ONLINE); } @Override public void siteOffline() { notifications.add(SiteStatus.OFFLINE); } private void check(SiteStatus first) { assertEquals("Check first site status.", first, notifications.poll()); assertTrue("Check notifications is empty.", notifications.isEmpty()); } private void check(SiteStatus first, SiteStatus... remaining) { assertEquals("Check first site status.", first, notifications.poll()); int i = 2; for (SiteStatus status : remaining) { assertEquals(format("Check %d(\"th\") site status", i), status, notifications.poll()); i++; } assertTrue("Check notifications is empty.", notifications.isEmpty()); } } private enum SiteStatus { ONLINE, OFFLINE } }