/*
* 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.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import com.microsoft.applicationinsights.internal.channel.TelemetriesTransmitter;
import com.microsoft.applicationinsights.internal.util.LimitsEnforcer;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.anyCollection;
import static org.mockito.Matchers.anyCollectionOf;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
public final class TelemetryBufferTest {
private final static String MOCK_PROPERTY_NAME = "MockProperty";
private static class MockSender implements TelemetriesTransmitter {
private static class ScheduledSendResult {
public final boolean result;
public final String message;
private ScheduledSendResult(boolean result, String message) {
this.result = result;
this.message = message;
}
}
private AtomicInteger sendNowCallCounter = new AtomicInteger(0);
private AtomicInteger scheduleSendCallCounter = new AtomicInteger(0);
private AtomicInteger scheduleSendActualCallCounter = new AtomicInteger(0);
private int expectedTelemetriesNumberInSendNow;
private int expectedTelemetriesNumberInScheduleSend;
private int expectedNumberOfSendNowCalls;
private int expectedNumberOfScheduleSendCalls;
private int expectedNumberOfScheduleSendRequests = 1;
private ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1);
private final BlockingQueue<ScheduledSendResult> queue = new ArrayBlockingQueue<ScheduledSendResult>(4);
public MockSender setExpectedTelemetriesNumberInSendNow(int expectedTelemetriesNumberInSendNow) {
this.expectedTelemetriesNumberInSendNow = expectedTelemetriesNumberInSendNow;
return this;
}
public MockSender setExpectedTelemetriesNumberInScheduleSend(int expectedTelemetriesNumberInScheduleSend) {
this.expectedTelemetriesNumberInScheduleSend = expectedTelemetriesNumberInScheduleSend;
return this;
}
public MockSender setExpectedNumberOfSendNowCalls(int expectedNumberOfSendNowCalls) {
this.expectedNumberOfSendNowCalls = expectedNumberOfSendNowCalls;
return this;
}
public MockSender setExpectedNumberOfScheduleSendRequests(int expectedNumberOfScheduleSendRequests) {
this.expectedNumberOfScheduleSendRequests = expectedNumberOfScheduleSendRequests;
return this;
}
public MockSender setExpectedNumberOfScheduleSendCalls(int expectedNumberOfScheduleSendCalls) {
this.expectedNumberOfScheduleSendCalls = expectedNumberOfScheduleSendCalls;
return this;
}
@Override
public boolean scheduleSend(final TelemetriesTransmitter.TelemetriesFetcher telemetriesFetcher, long value, TimeUnit timeUnit) {
assertNotNull(telemetriesFetcher);
scheduleSendCallCounter.incrementAndGet();
assertEquals(timeUnit, TimeUnit.SECONDS);
scheduler.schedule(new Runnable() {
@Override
public void run() {
scheduleSendActualCallCounter.incrementAndGet();
Collection<String> telemetries = telemetriesFetcher.fetch();
if (telemetries == null) {
queue.offer(new ScheduledSendResult(false, "Telemetries is null"));
return;
}
if (telemetries.size() != expectedTelemetriesNumberInScheduleSend) {
queue.offer(new ScheduledSendResult(false, "Telemetries size is wrong"));
return;
}
queue.offer(new ScheduledSendResult(true, ""));
}
}, value, timeUnit);
return true;
}
@Override
public boolean sendNow(Collection<String> telemetries) {
int called = sendNowCallCounter.incrementAndGet();
assertEquals("Wrong number of scheduled sends by the TransmissionBuffer", called, expectedNumberOfSendNowCalls);
assertNotNull("Unexpected null value for telemetries container", telemetries);
assertEquals("Wrong size of telemetries container", expectedTelemetriesNumberInSendNow, telemetries.size());
return true;
}
@Override
public void stop(long timeout, TimeUnit timeUnit) {
}
public void waitForFinish(long timeToWaitInSeconds) {
try {
ScheduledSendResult result = queue.poll(timeToWaitInSeconds, TimeUnit.SECONDS);
scheduler.shutdownNow();
assertEquals("Wrong number of calls by timer", scheduleSendActualCallCounter.get(), expectedNumberOfScheduleSendCalls);
if (expectedNumberOfScheduleSendCalls == 0) {
assertNull("Result should be null", result);
} else {
assertTrue(result.message, result.result);
}
assertEquals("Wrong number of calls of send now", sendNowCallCounter.get(), expectedNumberOfSendNowCalls);
assertEquals("Wrong number of scheduled sends by the TransmissionBuffer", scheduleSendCallCounter.get(), expectedNumberOfScheduleSendRequests);
assertEquals("Wrong number of sending full buffers by the TransmissionBuffer", sendNowCallCounter.get(), expectedNumberOfSendNowCalls);
} catch (InterruptedException e) {
assertTrue(false);
}
}
};
@Test(expected = NullPointerException.class)
public void testNullMaxTelemetriesEnforcer() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer sendEnforcer = LimitsEnforcer.createWithClosestLimitOnError(MOCK_PROPERTY_NAME, 1, 200, 20, null);
new TelemetryBuffer(mockSender, null, sendEnforcer);
}
@Test(expected = NullPointerException.class)
public void testNullSenderTimeoutEnforcer() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer maxEnforcer = createDefaultBatchSizeEnforcer();
new TelemetryBuffer(mockSender, maxEnforcer, null);
}
@Test(expected = IllegalArgumentException.class)
public void testNegativeBufferSizeSenderIsSet() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(-1);
LimitsEnforcer sendEnforcer = createDefaultSenderTimeoutEnforcer();
new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
}
@Test(expected = IllegalArgumentException.class)
public void testZeroBufferSizeSenderIsSet() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(0);
LimitsEnforcer sendEnforcer = createDefaultSenderTimeoutEnforcer();
new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
}
@Test(expected = IllegalArgumentException.class)
public void testNegativeBufferTimeoutSenderIsSet() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer maxEnforcer = createDefaultBatchSizeEnforcer();
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(-1);
new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
}
@Test(expected = IllegalArgumentException.class)
public void testZeroBufferTimeoutSenderIsSet() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer maxEnforcer = createDefaultBatchSizeEnforcer();
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(0);
new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
}
@Test(expected = NullPointerException.class)
public void testNoSenderIsSet() throws Exception {
LimitsEnforcer maxEnforcer = createDefaultBatchSizeEnforcer();
LimitsEnforcer sendEnforcer = createDefaultSenderTimeoutEnforcer();
new TelemetryBuffer(null, maxEnforcer, sendEnforcer);
}
@Test
public void testAddOneTelemetry() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(128);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(2);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
testedBuffer.add("mockTelemetry");
Mockito.verify(mockSender, Mockito.times(1)).scheduleSend((TelemetriesTransmitter.TelemetriesFetcher) any(), anyLong(), (TimeUnit) anyObject());
}
// Ignore warning from mock
@SuppressWarnings("unchecked")
@Test
public void testSendWhenBufferIsFullInNonDeveloperMode() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
Mockito.doReturn(true).when(mockSender).sendNow(anyCollection());
Mockito.doReturn(true).when(mockSender).scheduleSend(any(TelemetriesTransmitter.TelemetriesFetcher.class), anyLong(), any(TimeUnit.class));
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(2);
LimitsEnforcer sendEnforcer = createDefaultSenderTimeoutEnforcer();
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 2; ++i) {
testedBuffer.add("mockTelemetry");
}
Mockito.verify(mockSender, Mockito.times(1)).scheduleSend((TelemetriesTransmitter.TelemetriesFetcher) any(), anyLong(), (TimeUnit) anyObject());
Mockito.verify(mockSender, Mockito.times(1)).sendNow(anyCollectionOf(String.class));
}
@Test
public void testSendReturnsFalseOnScheduleSend() throws Exception {
class StubTelemetriesTransmitter implements TelemetriesTransmitter {
private int scheduleSendCounter = 2;
private Collection<String> sendNowCollection;
@Override
public boolean scheduleSend(TelemetriesFetcher telemetriesFetcher, long value, TimeUnit timeUnit) {
--scheduleSendCounter;
if (scheduleSendCounter > 0) {
return false;
}
return true;
}
@Override
public boolean sendNow(Collection<String> telemetries) {
sendNowCollection = telemetries;
return true;
}
@Override
public void stop(long timeout, TimeUnit timeUnit) {
}
public Collection<String> getSendNowCollection() {
return sendNowCollection;
}
};
List<String> all = new ArrayList<String>();
List<String> expected = new ArrayList<String>();
for (int i = 0; i < 4; ++i) {
String mockSerializedTelemetry = "mockTelemtry" + String.valueOf(i);
all.add(mockSerializedTelemetry);
if (i != 0) {
expected.add(mockSerializedTelemetry);
}
}
StubTelemetriesTransmitter mockSender = new StubTelemetriesTransmitter();
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(3);
LimitsEnforcer sendEnforcer = createDefaultSenderTimeoutEnforcer();
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (String telemetry : all) {
testedBuffer.add(telemetry);
}
Collection<String> sendNowCollection = mockSender.getSendNowCollection();
assertEquals(sendNowCollection.size(), expected.size());
int i = 0;
for (String telemetry : sendNowCollection) {
assertEquals(telemetry, expected.get(i));
++i;
}
}
@Test
public void testSendWhenBufferIsFullInDeveloperMode() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(1);
LimitsEnforcer sendEnforcer = createDefaultSenderTimeoutEnforcer();
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 2; ++i) {
testedBuffer.add("mockTelemetry");
}
Mockito.verify(mockSender, Mockito.never()).scheduleSend((TelemetriesTransmitter.TelemetriesFetcher)any(), anyLong(), (TimeUnit)anyObject());
Mockito.verify(mockSender, Mockito.times(2)).sendNow(anyCollectionOf(String.class));
}
@Test
public void testSendBufferAfterTimeoutExpires() throws Exception {
MockSender mockSender = new MockSender()
.setExpectedNumberOfScheduleSendCalls(1)
.setExpectedNumberOfSendNowCalls(0)
.setExpectedTelemetriesNumberInScheduleSend(1)
.setExpectedTelemetriesNumberInSendNow(1);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(3);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 1; ++i) {
testedBuffer.add("mockTelemetry");
}
mockSender.waitForFinish(6L);
}
@Test
public void testSendBufferAfterTimeoutExpiresButBufferWasAlreadySent() throws Exception {
MockSender mockSender = new MockSender()
.setExpectedNumberOfScheduleSendCalls(1)
.setExpectedNumberOfSendNowCalls(1)
.setExpectedTelemetriesNumberInScheduleSend(0)
.setExpectedTelemetriesNumberInSendNow(10);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(3);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 10; ++i) {
testedBuffer.add("mockTelemetry");
}
mockSender.waitForFinish(6L);
}
@Test
public void testFlushWithZero() throws Exception {
TelemetriesTransmitter mockSender = Mockito.mock(TelemetriesTransmitter.class);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(3);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
testedBuffer.flush();
Mockito.verify(mockSender, Mockito.never()).sendNow(anyCollectionOf(String.class));
}
@Test
public void testFlushWithOneInTheBuffer() throws Exception {
testFlushWithData(1);
}
@Test
public void testFlushWithSevenInTheBuffer() throws Exception {
testFlushWithData(7);
}
@Test
public void testSetTransmitBufferTimeoutInSecondsShorterTime() {
MockSender mockSender = new MockSender()
.setExpectedNumberOfScheduleSendCalls(0)
.setExpectedNumberOfSendNowCalls(1)
.setExpectedTelemetriesNumberInScheduleSend(0)
.setExpectedTelemetriesNumberInSendNow(2);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(1, 30);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 2; ++i) {
testedBuffer.add("mockTelemetry");
}
testedBuffer.setTransmitBufferTimeoutInSeconds(1);
mockSender.waitForFinish(1L);
}
@Test
public void testSetMaxTelemetriesInBatchWithSmallerSize() {
MockSender mockSender = new MockSender()
.setExpectedNumberOfScheduleSendCalls(0)
.setExpectedNumberOfSendNowCalls(1)
.setExpectedTelemetriesNumberInScheduleSend(0)
.setExpectedTelemetriesNumberInSendNow(2);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(1, 10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(30);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 2; ++i) {
testedBuffer.add("mockTelemetry");
}
testedBuffer.setMaxTelemetriesInBatch(1);
mockSender.waitForFinish(1L);
}
@Test
public void testSetMaxTelemetriesInBatchWithSmallerSizeButLargerThanWhatInBuffer() {
MockSender mockSender = new MockSender()
.setExpectedNumberOfScheduleSendCalls(0)
.setExpectedNumberOfSendNowCalls(1)
.setExpectedTelemetriesNumberInScheduleSend(0)
.setExpectedTelemetriesNumberInSendNow(3)
.setExpectedNumberOfScheduleSendRequests(2);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(1, 10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(30);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 2; ++i) {
testedBuffer.add("mockTelemetry");
}
testedBuffer.setMaxTelemetriesInBatch(3);
for (int i = 0; i < 2; ++i) {
testedBuffer.add("mockTelemetry");
}
mockSender.waitForFinish(1L);
}
@Test
public void testSetMaxTelemetriesInBatchWithBiggerSize() {
MockSender mockSender = new MockSender()
.setExpectedNumberOfScheduleSendCalls(0)
.setExpectedNumberOfSendNowCalls(1)
.setExpectedTelemetriesNumberInScheduleSend(0)
.setExpectedTelemetriesNumberInSendNow(11);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(1, 10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(30);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < 1; ++i) {
testedBuffer.add("mockTelemetry");
}
testedBuffer.setMaxTelemetriesInBatch(11);
for (int i = 0; i < 10; ++i) {
testedBuffer.add("mockTelemetry");
}
mockSender.waitForFinish(1L);
}
private void testFlushWithData(int expectedTelemetriesNumberInSendNow) {
MockSender mockSender = new MockSender()
.setExpectedNumberOfScheduleSendCalls(1)
.setExpectedNumberOfSendNowCalls(1)
.setExpectedTelemetriesNumberInScheduleSend(0)
.setExpectedTelemetriesNumberInSendNow(expectedTelemetriesNumberInSendNow);
// Create a buffer with max buffer size of 10 and timeout of 10 seconds
LimitsEnforcer maxEnforcer = createEnforcerWithCurrentValue(1, 10);
LimitsEnforcer sendEnforcer = createEnforcerWithCurrentValue(1, 3);
TelemetryBuffer testedBuffer = new TelemetryBuffer(mockSender, maxEnforcer, sendEnforcer);
for (int i = 0; i < expectedTelemetriesNumberInSendNow; ++i) {
testedBuffer.add("mockTelemetry");
}
testedBuffer.flush();
mockSender.waitForFinish(6L);
}
private LimitsEnforcer createDefaultBatchSizeEnforcer() {
return LimitsEnforcer.createWithClosestLimitOnError(MOCK_PROPERTY_NAME, 1, 10000, 100, null);
}
private LimitsEnforcer createDefaultSenderTimeoutEnforcer() {
return LimitsEnforcer.createWithClosestLimitOnError(MOCK_PROPERTY_NAME, 1, 12000, 1200, 1200);
}
private LimitsEnforcer createEnforcerWithCurrentValue(int minimum) {
return createEnforcerWithCurrentValue(minimum, minimum);
}
private LimitsEnforcer createEnforcerWithCurrentValue(int minimum, int defaultValue) {
return LimitsEnforcer.createWithClosestLimitOnError(MOCK_PROPERTY_NAME, minimum, 10000, defaultValue, null);
}
}