package rocks.inspectit.agent.java.sensor.method.timer;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import rocks.inspectit.agent.java.AbstractLogSupport;
import rocks.inspectit.agent.java.config.IPropertyAccessor;
import rocks.inspectit.agent.java.config.impl.RegisteredSensorConfig;
import rocks.inspectit.agent.java.core.ICoreService;
import rocks.inspectit.agent.java.core.IObjectStorage;
import rocks.inspectit.agent.java.core.IPlatformManager;
import rocks.inspectit.agent.java.util.Timer;
import rocks.inspectit.shared.all.communication.DefaultData;
import rocks.inspectit.shared.all.communication.data.TimerData;
import rocks.inspectit.shared.all.communication.valueobject.TimerRawVO;
import rocks.inspectit.shared.all.util.ObjectUtils;
@SuppressWarnings("PMD")
public class TimerHookTest extends AbstractLogSupport {
@Mock
private Timer timer;
@Mock
private IPlatformManager platformManager;
@Mock
private IPropertyAccessor propertyAccessor;
@Mock
private ICoreService coreService;
@Mock
private RegisteredSensorConfig registeredSensorConfig;
@Mock
private ThreadMXBean threadMXBean;
private TimerHook timerHook;
@BeforeMethod
public void initTestClass() {
Map<String, Object> settings = new HashMap<String, Object>();
settings.put("mode", "raw");
when(threadMXBean.isThreadCpuTimeEnabled()).thenReturn(true);
when(threadMXBean.isThreadCpuTimeSupported()).thenReturn(true);
timerHook = new TimerHook(timer, platformManager, propertyAccessor, settings, threadMXBean);
}
@Test
public void sameMethodTwice() {
// set up data
long platformId = 1L;
long methodId = 3L;
long sensorTypeId = 11L;
Object object = mock(Object.class);
Object[] parameters = new Object[0];
Object result = mock(Object.class);
Double firstTimerValue = 1000.0d;
Double secondTimerValue = 1323.0d;
Double thirdTimerValue = 1894.0d;
Double fourthTimerValue = 2812.0d;
when(timer.getCurrentTime()).thenReturn(firstTimerValue).thenReturn(secondTimerValue).thenReturn(thirdTimerValue).thenReturn(fourthTimerValue);
when(platformManager.getPlatformId()).thenReturn(platformId);
when(registeredSensorConfig.getSettings()).thenReturn(Collections.<String, Object> singletonMap("charting", Boolean.TRUE));
// First call
timerHook.beforeBody(methodId, sensorTypeId, object, parameters, registeredSensorConfig);
verify(timer, times(1)).getCurrentTime();
timerHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(timer, times(2)).getCurrentTime();
timerHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(platformManager).getPlatformId();
verify(coreService).getObjectStorage(sensorTypeId, methodId, null);
verify(registeredSensorConfig).isPropertyAccess();
PlainTimerStorage plainTimerStorage = new PlainTimerStorage(null, platformId, sensorTypeId, methodId, null, true);
plainTimerStorage.addData(secondTimerValue - firstTimerValue, 0.0d);
verify(coreService).addObjectStorage(eq(sensorTypeId), eq(methodId), (String) eq(null), argThat(new PlainTimerStorageVerifier(plainTimerStorage)));
// second one
timerHook.beforeBody(methodId, sensorTypeId, object, parameters, registeredSensorConfig);
verify(timer, times(3)).getCurrentTime();
timerHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(timer, times(4)).getCurrentTime();
when(coreService.getObjectStorage(sensorTypeId, methodId, null)).thenReturn(plainTimerStorage);
timerHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(coreService, times(2)).getObjectStorage(sensorTypeId, methodId, null);
verify(registeredSensorConfig, times(2)).isPropertyAccess();
verify(registeredSensorConfig, times(1)).getSettings();
TimerRawVO timerRawVO = (TimerRawVO) plainTimerStorage.finalizeDataObject();
assertThat(timerRawVO.getPlatformIdent(), is(equalTo(platformId)));
assertThat(timerRawVO.getMethodIdent(), is(equalTo(methodId)));
assertThat(timerRawVO.getSensorTypeIdent(), is(equalTo(sensorTypeId)));
assertThat(timerRawVO.getData().get(0).getData()[0], is(equalTo(secondTimerValue - firstTimerValue)));
assertThat(timerRawVO.getData().get(0).getData()[1], is(equalTo(fourthTimerValue - thirdTimerValue)));
verifyNoMoreInteractions(timer, platformManager, coreService, registeredSensorConfig);
verifyZeroInteractions(propertyAccessor, object, result);
}
/**
* Inner class used to verify the contents of PlainTimerData objects.
*/
private static class PlainTimerStorageVerifier extends ArgumentMatcher<PlainTimerStorage> {
private final ITimerStorage timerStorage;
public PlainTimerStorageVerifier(PlainTimerStorage timerStorage) {
this.timerStorage = timerStorage;
}
@Override
public boolean matches(Object object) {
if (!PlainTimerStorage.class.isInstance(object)) {
return false;
}
PlainTimerStorage otherPlainTimerStorage = (PlainTimerStorage) object;
// we receive the raw vo by calling finalize on the object storage!
TimerRawVO timerRawVO = (TimerRawVO) timerStorage.finalizeDataObject();
TimerRawVO otherTimerRawVO = (TimerRawVO) otherPlainTimerStorage.finalizeDataObject();
if (timerRawVO.getPlatformIdent() != otherTimerRawVO.getPlatformIdent()) {
return false;
} else if (timerRawVO.getMethodIdent() != otherTimerRawVO.getMethodIdent()) {
return false;
} else if (timerRawVO.getSensorTypeIdent() != otherTimerRawVO.getSensorTypeIdent()) {
return false;
} else if (!ObjectUtils.equals(timerRawVO.getParameterContentData(), otherTimerRawVO.getParameterContentData())) {
return false;
} else if (!timerRawVO.getData().equals(otherTimerRawVO.getData())) {
return false;
}
return true;
}
}
@Test
public void propertyAccess() {
// set up data
long platformId = 1L;
long methodId = 3L;
long sensorTypeId = 11L;
Object object = mock(Object.class);
Object[] parameters = new Object[2];
Object result = mock(Object.class);
Double firstTimerValue = 1000.453d;
Double secondTimerValue = 1323.675d;
when(timer.getCurrentTime()).thenReturn(firstTimerValue).thenReturn(secondTimerValue);
when(platformManager.getPlatformId()).thenReturn(platformId);
when(registeredSensorConfig.isPropertyAccess()).thenReturn(true);
timerHook.beforeBody(methodId, sensorTypeId, object, parameters, registeredSensorConfig);
timerHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
timerHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(registeredSensorConfig, times(1)).isPropertyAccess();
verify(propertyAccessor, times(1)).getParameterContentData(registeredSensorConfig.getPropertyAccessorList(), object, parameters, result);
}
@Test
public void charting() {
// set up data
long platformId = 1L;
long methodId = 3L;
long sensorTypeId = 11L;
Object object = mock(Object.class);
Object[] parameters = new Object[2];
Object result = mock(Object.class);
Double firstTimerValue = 1000.453d;
Double secondTimerValue = 1323.675d;
when(timer.getCurrentTime()).thenReturn(firstTimerValue).thenReturn(secondTimerValue);
when(platformManager.getPlatformId()).thenReturn(platformId);
when(registeredSensorConfig.getSettings()).thenReturn(Collections.<String, Object> singletonMap("charting", Boolean.TRUE));
timerHook.beforeBody(methodId, sensorTypeId, object, parameters, registeredSensorConfig);
timerHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
timerHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
ArgumentCaptor<IObjectStorage> capture = ArgumentCaptor.forClass(IObjectStorage.class);
verify(coreService).addObjectStorage(eq(sensorTypeId), eq(methodId), anyString(), capture.capture());
DefaultData finalizedDataObject = capture.getValue().finalizeDataObject();
assertThat(finalizedDataObject, is(instanceOf(TimerRawVO.class)));
TimerData timerData = (TimerData) ((TimerRawVO) finalizedDataObject).finalizeData();
assertThat(timerData.isCharting(), is(true));
}
@Test
public void aggregateStorage() {
Map<String, Object> settings = new HashMap<String, Object>();
settings.put("mode", "aggregate");
timerHook = new TimerHook(timer, platformManager, propertyAccessor, settings, ManagementFactory.getThreadMXBean());
// set up data
long platformId = 1L;
long methodId = 3L;
long sensorTypeId = 11L;
Object object = mock(Object.class);
Object[] parameters = new Object[0];
Object result = mock(Object.class);
Double firstTimerValue = 1000.453d;
Double secondTimerValue = 1323.675d;
when(timer.getCurrentTime()).thenReturn(firstTimerValue).thenReturn(secondTimerValue);
when(platformManager.getPlatformId()).thenReturn(platformId);
when(registeredSensorConfig.getSettings()).thenReturn(Collections.<String, Object> singletonMap("charting", Boolean.TRUE));
timerHook.beforeBody(methodId, sensorTypeId, object, parameters, registeredSensorConfig);
verify(timer, times(1)).getCurrentTime();
timerHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(timer, times(2)).getCurrentTime();
timerHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(platformManager).getPlatformId();
verify(coreService).getObjectStorage(sensorTypeId, methodId, null);
verify(registeredSensorConfig).isPropertyAccess();
verify(registeredSensorConfig).getSettings();
AggregateTimerStorage aggregateTimerStorage = new AggregateTimerStorage(null, platformId, sensorTypeId, methodId, null, true);
aggregateTimerStorage.addData(secondTimerValue - firstTimerValue, -1.0d);
verify(coreService).addObjectStorage(eq(sensorTypeId), eq(methodId), (String) eq(null), argThat(new AggregateTimerStorageVerifier(aggregateTimerStorage)));
verifyNoMoreInteractions(timer, platformManager, coreService, registeredSensorConfig);
verifyZeroInteractions(propertyAccessor, object, result);
}
/**
* Inner class used to verify the contents of AggregateTimerStorage objects.
*/
private static class AggregateTimerStorageVerifier extends ArgumentMatcher<AggregateTimerStorage> {
private final ITimerStorage timerStorage;
public AggregateTimerStorageVerifier(AggregateTimerStorage timerStorage) {
this.timerStorage = timerStorage;
}
@Override
public boolean matches(Object object) {
if (!AggregateTimerStorage.class.isInstance(object)) {
return false;
}
try {
AggregateTimerStorage otherAggregateTimerStorage = (AggregateTimerStorage) object;
// we have to use reflection here
Field field = AggregateTimerStorage.class.getDeclaredField("timerRawVO");
field.setAccessible(true);
TimerRawVO timerRawVO = (TimerRawVO) field.get(timerStorage);
TimerRawVO otherTimerRawVO = (TimerRawVO) field.get(otherAggregateTimerStorage);
if (timerRawVO.getPlatformIdent() != otherTimerRawVO.getPlatformIdent()) {
return false;
} else if (timerRawVO.getMethodIdent() != otherTimerRawVO.getMethodIdent()) {
return false;
} else if (timerRawVO.getSensorTypeIdent() != otherTimerRawVO.getSensorTypeIdent()) {
return false;
} else if (!ObjectUtils.equals(timerRawVO.getParameterContentData(), otherTimerRawVO.getParameterContentData())) {
return false;
}
return true;
} catch (Exception exception) {
exception.printStackTrace();
return false;
}
}
}
@Test
public void optimizedStorage() {
Map<String, Object> settings = new HashMap<String, Object>();
settings.put("mode", "optimized");
timerHook = new TimerHook(timer, platformManager, propertyAccessor, settings, ManagementFactory.getThreadMXBean());
// set up data
long platformId = 1L;
long methodId = 3L;
long sensorTypeId = 11L;
Object object = mock(Object.class);
Object[] parameters = new Object[0];
Object result = mock(Object.class);
Double firstTimerValue = 1000.453d;
Double secondTimerValue = 1323.675d;
when(timer.getCurrentTime()).thenReturn(firstTimerValue).thenReturn(secondTimerValue);
when(platformManager.getPlatformId()).thenReturn(platformId);
when(registeredSensorConfig.getSettings()).thenReturn(Collections.<String, Object> singletonMap("charting", Boolean.TRUE));
timerHook.beforeBody(methodId, sensorTypeId, object, parameters, registeredSensorConfig);
verify(timer, times(1)).getCurrentTime();
timerHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(timer, times(2)).getCurrentTime();
timerHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(platformManager).getPlatformId();
verify(coreService).getObjectStorage(sensorTypeId, methodId, null);
verify(registeredSensorConfig).isPropertyAccess();
verify(registeredSensorConfig).getSettings();
OptimizedTimerStorage optimizedTimerStorage = new OptimizedTimerStorage(null, platformId, sensorTypeId, methodId, null, true);
optimizedTimerStorage.addData(secondTimerValue - firstTimerValue, -1.0d);
verify(coreService).addObjectStorage(eq(sensorTypeId), eq(methodId), (String) eq(null), argThat(new OptimizedTimerStorageVerifier(optimizedTimerStorage)));
verifyNoMoreInteractions(timer, platformManager, coreService, registeredSensorConfig);
verifyZeroInteractions(propertyAccessor, object, result);
}
/**
* Inner class used to verify the contents of AggregateTimerStorage objects.
*/
private static class OptimizedTimerStorageVerifier extends ArgumentMatcher<OptimizedTimerStorage> {
private final ITimerStorage timerStorage;
public OptimizedTimerStorageVerifier(OptimizedTimerStorage timerStorage) {
this.timerStorage = timerStorage;
}
@Override
public boolean matches(Object object) {
if (!OptimizedTimerStorage.class.isInstance(object)) {
return false;
}
TimerData timerData = (TimerData) timerStorage.finalizeDataObject();
TimerData otherTimerData = (TimerData) ((OptimizedTimerStorage) object).finalizeDataObject();
if (timerData.getPlatformIdent() != otherTimerData.getPlatformIdent()) {
return false;
} else if (timerData.getMethodIdent() != otherTimerData.getMethodIdent()) {
return false;
} else if (timerData.getSensorTypeIdent() != otherTimerData.getSensorTypeIdent()) {
return false;
} else if (timerData.getCount() != otherTimerData.getCount()) {
return false;
} else if (timerData.getDuration() != otherTimerData.getDuration()) {
return false;
} else if (timerData.getMax() != otherTimerData.getMax()) {
return false;
} else if (timerData.getMin() != otherTimerData.getMin()) {
return false;
} else if (timerData.getAverage() != otherTimerData.getAverage()) {
return false;
} else if (!ObjectUtils.equals(timerData.getParameterContentData(), otherTimerData.getParameterContentData())) {
return false;
} else if (timerData.getVariance() != otherTimerData.getVariance()) {
return false;
}
return true;
}
}
@Test
public void oneRecordWithCpuTime() {
// set up data
long platformId = 1L;
long methodId = 3L;
long sensorTypeId = 11L;
Object object = mock(Object.class);
Object[] parameters = new Object[0];
Object result = mock(Object.class);
Double firstTimerValue = 1000.453d;
Double secondTimerValue = 1323.675d;
Long firstCpuTimerValue = 5000L;
Long secondCpuTimerValue = 6872L;
when(timer.getCurrentTime()).thenReturn(firstTimerValue).thenReturn(secondTimerValue);
when(threadMXBean.getCurrentThreadCpuTime()).thenReturn(firstCpuTimerValue).thenReturn(secondCpuTimerValue);
when(platformManager.getPlatformId()).thenReturn(platformId);
when(registeredSensorConfig.getSettings()).thenReturn(Collections.<String, Object> singletonMap("charting", Boolean.TRUE));
timerHook.beforeBody(methodId, sensorTypeId, object, parameters, registeredSensorConfig);
verify(timer, times(1)).getCurrentTime();
timerHook.firstAfterBody(methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(timer, times(2)).getCurrentTime();
timerHook.secondAfterBody(coreService, methodId, sensorTypeId, object, parameters, result, registeredSensorConfig);
verify(platformManager).getPlatformId();
verify(coreService).getObjectStorage(sensorTypeId, methodId, null);
verify(registeredSensorConfig).isPropertyAccess();
verify(registeredSensorConfig).getSettings();
PlainTimerStorage plainTimerStorage = new PlainTimerStorage(null, platformId, sensorTypeId, methodId, null, true);
plainTimerStorage.addData(secondTimerValue - firstTimerValue, (secondCpuTimerValue - firstCpuTimerValue) / 1000000.0d);
verify(coreService).addObjectStorage(eq(sensorTypeId), eq(methodId), (String) eq(null), argThat(new PlainTimerStorageVerifier(plainTimerStorage)));
verifyNoMoreInteractions(timer, platformManager, coreService, registeredSensorConfig);
verifyZeroInteractions(propertyAccessor, object, result);
}
@Test
public void twoRecordsWithCpuTime() {
long platformId = 1L;
long methodIdOne = 3L;
long methodIdTwo = 9L;
long sensorTypeId = 11L;
Object object = mock(Object.class);
Object[] parameters = new Object[0];
Object result = mock(Object.class);
Double firstTimerValue = 1000.453d;
Double secondTimerValue = 1323.675d;
Double thirdTimerValue = 1578.92d;
Double fourthTimerValue = 2319.712d;
Long firstCpuTimerValue = 5000L;
Long secondCpuTimerValue = 6872L;
Long thirdCpuTimerValue = 8412L;
Long fourthCpuTimerValue = 15932L;
when(timer.getCurrentTime()).thenReturn(firstTimerValue).thenReturn(secondTimerValue).thenReturn(thirdTimerValue).thenReturn(fourthTimerValue);
when(threadMXBean.getCurrentThreadCpuTime()).thenReturn(firstCpuTimerValue).thenReturn(secondCpuTimerValue).thenReturn(thirdCpuTimerValue).thenReturn(fourthCpuTimerValue);
when(platformManager.getPlatformId()).thenReturn(platformId);
timerHook.beforeBody(methodIdOne, sensorTypeId, object, parameters, registeredSensorConfig);
timerHook.beforeBody(methodIdTwo, sensorTypeId, object, parameters, registeredSensorConfig);
timerHook.firstAfterBody(methodIdTwo, sensorTypeId, object, parameters, result, registeredSensorConfig);
timerHook.secondAfterBody(coreService, methodIdTwo, sensorTypeId, object, parameters, result, registeredSensorConfig);
PlainTimerStorage plainTimerStorageTwo = new PlainTimerStorage(null, platformId, sensorTypeId, methodIdTwo, null, true);
plainTimerStorageTwo.addData(thirdTimerValue - secondTimerValue, (thirdCpuTimerValue - secondCpuTimerValue) / 1000000.0d);
verify(coreService).addObjectStorage(eq(sensorTypeId), eq(methodIdTwo), (String) eq(null), argThat(new PlainTimerStorageVerifier(plainTimerStorageTwo)));
timerHook.firstAfterBody(methodIdOne, sensorTypeId, object, parameters, result, registeredSensorConfig);
timerHook.secondAfterBody(coreService, methodIdOne, sensorTypeId, object, parameters, result, registeredSensorConfig);
PlainTimerStorage plainTimerStorageOne = new PlainTimerStorage(null, platformId, sensorTypeId, methodIdOne, null, true);
plainTimerStorageOne.addData(fourthTimerValue - firstTimerValue, (fourthCpuTimerValue - firstCpuTimerValue) / 1000000.0d);
verify(coreService).addObjectStorage(eq(sensorTypeId), eq(methodIdOne), (String) eq(null), argThat(new PlainTimerStorageVerifier(plainTimerStorageOne)));
}
}