/* * ApplicationInsights-Java * Copyright (c) Microsoft Corporation * All rights reserved. * * MIT License * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the ""Software""), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ package com.microsoft.applicationinsights.internal.channel.common; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import org.junit.Test; import org.mockito.Mockito; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertEquals; public final class SenderThreadsBackOffManagerTest { private static class TimeMeasure { private Long start; private int elapsed; public void start() {} { if (start == null) { synchronized (this) { if (start == null) { start = new Long(System.nanoTime()); } } } } public int stop() { elapsed = (int)((double)(System.nanoTime() - start) / 1000000.0); return elapsed; } } @Test public void testOneBackOff() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100}); verifyBackOff(manager, 1, 100, true); } @Test public void testTwoBackOffs() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100, 100}); verifyBackOff(manager, 2, 200, true, true); } @Test public void testExhaustedBackOffsWithOne() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100}); verifyBackOff(manager, 2, 100, true, false); } @Test public void testExhaustedBackOffsWithTwo() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100, 100}); verifyBackOff(manager, 3, 200, true, true, false); } @Test public void testExhaustedBackOffsWithOneAndRestarted() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100}); verifyBackOff(manager, 3, 200, true, false, true); } @Test public void testExhaustedBackOffsWithTwoAndRestarted() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100, 100}); verifyBackOff(manager, 4, 300, true, true, false, true); } @Test public void testOneThreadStoppedBeforeBackOff() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {500}); verifyMultipleThreadsStoppedBeforeBackOff(manager, 1, 200); } @Test public void testTwoThreadsStoppedBeforeBackOff() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {500}); verifyMultipleThreadsStoppedBeforeBackOff(manager, 2, 200); } @Test public void testSevenThreadsStoppedBeforeBackOff() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[]{500}); verifyMultipleThreadsStoppedBeforeBackOff(manager, 7, 200); } @Test public void testTwoBackOffsAndDoneSendingInTheMiddle() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100, 500}); verifyBackOffWithDoneSending(manager, 2, 200, 1, true, true); } @Test public void testTwoBackOffsAndDoneSendingBefore() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100, 100}); verifyBackOffWithDoneSending(manager, 2, 200, 0, true, true); } @Test public void testTwoBackOffsAndDoneSendingAfterLast() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100, 100}); verifyBackOffWithDoneSending(manager, 2, 200, 0, true, true); } @Test public void testTwoBackOffsAndDoneSendingAfterFirstOfNewRound() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {100, 100}); verifyBackOffWithDoneSending(manager, 5, 400, 3, true, true, false, true, true); } @Test public void testStopWhileWaiting() throws InterruptedException { SenderThreadsBackOffManager manager = createManager(new long[] {500, 200}); verifyMultipleThreadsStoppedWhileBackOff(manager, 1, 0); } private static SenderThreadsBackOffManager createManager(long[] backOffTimeouts) { BackOffTimesPolicy container = Mockito.mock(BackOffTimesPolicy.class); Mockito.doReturn(backOffTimeouts).when(container).getBackOffTimeoutsInMillis(); SenderThreadsBackOffManager manager = new SenderThreadsBackOffManager(container); return manager; } private static void verifyBackOff(SenderThreadsBackOffManager manager, int backOffTimes, long expectedMilliseconds, boolean... expectedBackOffResult) throws InterruptedException { verifyBackOffWithDoneSending(manager, backOffTimes, expectedMilliseconds, null, expectedBackOffResult); } private static void verifyBackOffWithDoneSending(SenderThreadsBackOffManager manager, int backOffTimes, long expectedMilliseconds, Integer doneSendingAfter, boolean... expectedBackOffResult) throws InterruptedException { int limit = backOffTimes + (doneSendingAfter == null ? 0 : 1); int reduce = 0; long start = new Long(System.nanoTime()); for (int i = 0; i < limit; ++i) { if (doneSendingAfter != null && i == doneSendingAfter) { manager.onDoneSending(); reduce = 1; continue; } boolean done = manager.backOffCurrentSenderThread(); assertEquals(done, expectedBackOffResult[i - reduce]); } int elapsed = (int)((double)(System.nanoTime() - start) / 1000000.0); assertTrue(String.format("BackOff lasted %d which is less than expected %d", elapsed, expectedMilliseconds), elapsed >= expectedMilliseconds); assertTrue(String.format("BackOff lasted %d which is more than expected %d", elapsed, expectedMilliseconds), elapsed <= expectedMilliseconds + 2000); } private static void verifyMultipleThreadsStoppedBeforeBackOff(SenderThreadsBackOffManager manager, int numberOfThreads, int expectedMaxSeconds) throws InterruptedException { verifyMultipleThreadsWithStopBackOff(manager, numberOfThreads, expectedMaxSeconds, true); } private static void verifyMultipleThreadsStoppedWhileBackOff(SenderThreadsBackOffManager manager, int numberOfThreads, long expectedMaxMilliseconds) throws InterruptedException { verifyMultipleThreadsWithStopBackOff(manager, numberOfThreads, expectedMaxMilliseconds, false); } private static void verifyMultipleThreadsWithStopBackOff(final SenderThreadsBackOffManager manager, int numberOfThreads, long expectedMaxMilliseconds, boolean stopBefore) throws InterruptedException { final Thread[] threads = new Thread[numberOfThreads]; final CyclicBarrier barrier = new CyclicBarrier(numberOfThreads); final TimeMeasure measure = new TimeMeasure(); for (int i = 0; i < numberOfThreads; ++i) { Thread thread = new Thread(new Runnable() { @Override public void run() { try { barrier.await(); measure.start(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } manager.backOffCurrentSenderThread(); } }); thread.setDaemon(true); threads[i] = thread; } if (stopBefore) { manager.stopAllSendersBackOffActivities(); } for (Thread thread : threads) { thread.start(); } if (!stopBefore) { Thread.sleep(1000); expectedMaxMilliseconds += 1000; manager.stopAllSendersBackOffActivities(); } for (Thread thread : threads) { thread.join(); } int elapsed = measure.stop(); assertTrue(String.format("BackOff lasted %d which is more than expected %d", elapsed, expectedMaxMilliseconds), elapsed <= expectedMaxMilliseconds + 2000); } }