package rocks.inspectit.server.processor.impl; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.mockito.Mockito.RETURNS_SMART_NULLS; 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.util.Collections; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import org.mockito.ArgumentCaptor; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.slf4j.Logger; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import rocks.inspectit.server.cache.IBuffer; import rocks.inspectit.server.cache.IBufferElement; import rocks.inspectit.server.dao.impl.TimerDataAggregator; import rocks.inspectit.server.processor.AbstractCmrDataProcessor; import rocks.inspectit.server.storage.CmrStorageManager; import rocks.inspectit.server.util.CacheIdGenerator; import rocks.inspectit.shared.all.communication.DefaultData; import rocks.inspectit.shared.all.communication.ExceptionEvent; import rocks.inspectit.shared.all.communication.MethodSensorData; import rocks.inspectit.shared.all.communication.data.CpuInformationData; import rocks.inspectit.shared.all.communication.data.ExceptionSensorData; import rocks.inspectit.shared.all.communication.data.HttpInfo; import rocks.inspectit.shared.all.communication.data.HttpTimerData; import rocks.inspectit.shared.all.communication.data.InvocationAwareData; import rocks.inspectit.shared.all.communication.data.InvocationSequenceData; import rocks.inspectit.shared.all.communication.data.SqlStatementData; import rocks.inspectit.shared.all.communication.data.SystemInformationData; import rocks.inspectit.shared.all.communication.data.TimerData; import rocks.inspectit.shared.all.serializer.SerializationException; import rocks.inspectit.shared.all.serializer.impl.SerializationManager; import rocks.inspectit.shared.cs.indexing.buffer.IBufferTreeComponent; import rocks.inspectit.shared.cs.indexing.impl.IndexingException; import rocks.inspectit.shared.cs.storage.recording.RecordingState; /** * Tests for the all cmr data processors we have. * * @author Ivan Senic * */ @SuppressWarnings({ "all", "unchecked" }) public class CmrDataProcessorsTest { @Mock Logger log; @Mock private IBuffer<MethodSensorData> buffer; @Mock private CacheIdGenerator cacheIdGenerator; @Mock private IBufferTreeComponent<DefaultData> indexingTree; @Mock private CmrStorageManager storageManager; @Mock private TimerDataAggregator timerDataAggregator; @Mock private SerializationManager serializationManager; @Mock private AbstractCmrDataProcessor chainedProcessor; @Mock private EntityManager entityManager; @BeforeMethod public void init() { MockitoAnnotations.initMocks(this); } /** * Tests the {@link BufferInserterCmrProcessor}. */ @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void bufferInserter() { BufferInserterCmrProcessor processor = new BufferInserterCmrProcessor(); processor.buffer = buffer; // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(buffer, entityManager); // we don't allow system sensor data processor.process(new CpuInformationData(), entityManager); verifyZeroInteractions(buffer, entityManager); // we only allow invocation that is a root InvocationSequenceData invocationSequenceData = mock(InvocationSequenceData.class, RETURNS_SMART_NULLS); processor.process(invocationSequenceData, entityManager); verifyZeroInteractions(buffer, entityManager); // we don't insert data that's part of invocation InvocationAwareData invocationAwareData = mock(InvocationAwareData.class); when(invocationAwareData.isOnlyFoundInInvocations()).thenReturn(false); when(invocationAwareData.isOnlyFoundOutsideInvocations()).thenReturn(false); processor.process(invocationAwareData, entityManager); verifyZeroInteractions(buffer, entityManager); // allow other data invocationAwareData = mock(InvocationAwareData.class); when(invocationAwareData.isOnlyFoundInInvocations()).thenReturn(false); when(invocationAwareData.isOnlyFoundOutsideInvocations()).thenReturn(true); processor.process(invocationAwareData, entityManager); ArgumentCaptor<IBufferElement> captor = ArgumentCaptor.forClass(IBufferElement.class); verify(buffer, times(1)).put(captor.capture()); verifyZeroInteractions(entityManager); assertThat(captor.getValue().getObject(), is(equalTo(((Object) invocationAwareData)))); } /** * Tests the {@link CacheIdGeneratorCmrProcessor}. */ @Test public void cacheIdProcessor() { CacheIdGeneratorCmrProcessor processor = new CacheIdGeneratorCmrProcessor(); processor.cacheIdGenerator = cacheIdGenerator; // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(cacheIdGenerator, entityManager); // assign Id otherwise DefaultData defaultData = mock(DefaultData.class); processor.process(defaultData, entityManager); verify(cacheIdGenerator, times(1)).assignObjectAnId(defaultData); verifyZeroInteractions(entityManager); } /** * Tests the {@link ExceptionMessageCmrProcessor}. */ @Test public void exceptionMessageProcessor() { ExceptionMessageCmrProcessor processor = new ExceptionMessageCmrProcessor(); // only exceptions assertThat(processor.canBeProcessed(new TimerData()), is(false)); // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(entityManager); ExceptionSensorData parent = new ExceptionSensorData(); parent.setErrorMessage("parentMsg"); ExceptionSensorData child = new ExceptionSensorData(); child.setErrorMessage("childMsg"); parent.setChild(child); // prove message changing processor.process(parent, entityManager); assertThat(parent.getErrorMessage(), is("parentMsg")); assertThat(child.getErrorMessage(), is("parentMsg")); } /** * Tests the {@link IndexerCmrProcessor}. */ @SuppressWarnings("unchecked") @Test public void indexerProcessor() throws IndexingException { IndexerCmrProcessor processor = new IndexerCmrProcessor(); processor.log = log; processor.indexingTree = indexingTree; // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(log, indexingTree, entityManager); // don't allow system sensor data processor.process(new CpuInformationData(), entityManager); verifyZeroInteractions(log, indexingTree, entityManager); // don't allow invocations processor.process(new InvocationSequenceData(), entityManager); verifyZeroInteractions(log, indexingTree, entityManager); // don't allow invocation aware data that is not part of invocation InvocationAwareData invocationAwareData = mock(InvocationAwareData.class); when(invocationAwareData.isOnlyFoundInInvocations()).thenReturn(false); when(invocationAwareData.isOnlyFoundOutsideInvocations()).thenReturn(false); processor.process(invocationAwareData, entityManager); when(invocationAwareData.isOnlyFoundOutsideInvocations()).thenReturn(true); processor.process(invocationAwareData, entityManager); verifyZeroInteractions(log, indexingTree, entityManager); // index other data invocationAwareData = mock(InvocationAwareData.class); when(invocationAwareData.isOnlyFoundInInvocations()).thenReturn(true); when(invocationAwareData.isOnlyFoundOutsideInvocations()).thenReturn(false); processor.process(invocationAwareData, entityManager); verify(indexingTree, times(1)).put(invocationAwareData); // survive indexing exception when(indexingTree.put(indexingTree.put(invocationAwareData))).thenThrow(IndexingException.class); processor.process(invocationAwareData, entityManager); verifyZeroInteractions(entityManager); } /** * Tests the {@link RecorderCmrProcessor}. */ @Test public void recordProcessor() { RecorderCmrProcessor processor = new RecorderCmrProcessor(); processor.storageManager = storageManager; // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(storageManager, entityManager); DefaultData defaultData = mock(DefaultData.class); // don't call record if it's not on when(storageManager.getRecordingState()).thenReturn(RecordingState.OFF); processor.process(defaultData, entityManager); verify(storageManager, times(0)).record(defaultData); // call record if it's on when(storageManager.getRecordingState()).thenReturn(RecordingState.ON); processor.process(defaultData, entityManager); verify(storageManager, times(1)).record(defaultData); verifyZeroInteractions(entityManager); } /** * Tests the {@link PersistingCmrProcessor}. */ @Test public void persistingCmrProcessor() { // only Timer Data PersistingCmrProcessor processor = new PersistingCmrProcessor(Collections.<Class<? extends DefaultData>> singletonList(TimerData.class)); // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(entityManager); // don't process wrong classes processor.process(new SqlStatementData(), entityManager); processor.process(new HttpTimerData(), entityManager); verifyZeroInteractions(entityManager); // yes for correct class TimerData timerData = new TimerData(); processor.process(timerData, entityManager); verify(entityManager, times(1)).persist(timerData); // no when influx is active processor.influxActive = true; processor.process(timerData, entityManager); verifyNoMoreInteractions(entityManager); } /** * Tests the {@link SystemIn}. */ @Test public void SystemInformationPersistingCmrProcessor() { // only Timer Data SystemInformationPersistingCmrProcessor processor = new SystemInformationPersistingCmrProcessor(); // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(entityManager); // don't process wrong classes processor.process(new SqlStatementData(), entityManager); processor.process(new HttpTimerData(), entityManager); verifyZeroInteractions(entityManager); // yes for correct class SystemInformationData systemInformationData = new SystemInformationData(); processor.process(systemInformationData, entityManager); verify(entityManager, times(1)).persist(systemInformationData); // also when influx is active processor.influxActive = true; processor.process(systemInformationData, entityManager); verify(entityManager, times(2)).persist(systemInformationData); } /** * Tests the {@link SqlExclusiveTimeCmrProcessor}. */ @Test public void sqlExclusiveTimeProcessor() { SqlExclusiveTimeCmrProcessor processor = new SqlExclusiveTimeCmrProcessor(); // don't fail on null processor.process((DefaultData) null, entityManager); // only sqls assertThat(processor.canBeProcessed(new TimerData()), is(false)); // make sure exclusive data is set SqlStatementData sqlStatementData = new SqlStatementData(); sqlStatementData.setDuration(5d); processor.process(sqlStatementData, entityManager); assertThat(sqlStatementData.getExclusiveCount(), is(1l)); assertThat(sqlStatementData.getExclusiveDuration(), is(5d)); assertThat(sqlStatementData.getExclusiveMin(), is(5d)); assertThat(sqlStatementData.getExclusiveMax(), is(5d)); verifyZeroInteractions(entityManager); } /** * Tests the {@link TimerDataChartingCmrProcessor}. */ @Test public void chartingProcessor() throws CloneNotSupportedException, SerializationException { TimerDataChartingCmrProcessor processor = new TimerDataChartingCmrProcessor(); processor.timerDataAggregator = timerDataAggregator; processor.serializationManager = serializationManager; // set up entity manager for quering CriteriaBuilder build = mock(CriteriaBuilder.class, RETURNS_SMART_NULLS); CriteriaQuery<HttpInfo> criteria = mock(CriteriaQuery.class, RETURNS_SMART_NULLS); Root<? extends HttpInfo> root = mock(Root.class, RETURNS_SMART_NULLS); TypedQuery<HttpInfo> query = mock(TypedQuery.class, RETURNS_SMART_NULLS); when(entityManager.getCriteriaBuilder()).thenReturn(build); when(build.createQuery(HttpInfo.class)).thenReturn(criteria); when(criteria.from(HttpInfo.class)).thenReturn((Root<HttpInfo>) root); when(entityManager.createQuery(criteria)).thenReturn(query); // don't fail on null processor.process((DefaultData) null, entityManager); verifyZeroInteractions(timerDataAggregator, entityManager); TimerData timerData = mock(TimerData.class); HttpInfo originalInfo = mock(HttpInfo.class, RETURNS_SMART_NULLS); HttpTimerData httpTimerData = mock(HttpTimerData.class); when(httpTimerData.getHttpInfo()).thenReturn(originalInfo); HttpTimerData clone = mock(HttpTimerData.class); when(serializationManager.copy(Matchers.<HttpTimerData> any())).thenReturn(clone); HttpInfo httpInfo = mock(HttpInfo.class); when(query.getResultList()).thenReturn(Collections.singletonList(httpInfo)); // first with no charting skip when(timerData.isCharting()).thenReturn(false); when(httpTimerData.isCharting()).thenReturn(false); processor.process(timerData, entityManager); processor.process(httpTimerData, entityManager); verifyZeroInteractions(timerDataAggregator, entityManager); // then with charting process when(timerData.isCharting()).thenReturn(true); when(httpTimerData.isCharting()).thenReturn(true); processor.process(timerData, entityManager); processor.process(httpTimerData, entityManager); // timer to aggregator verify(timerDataAggregator, times(1)).processTimerData(timerData); // http to entityManager verify(clone, times(1)).setHttpInfo(httpInfo); verify(entityManager, times(1)).persist(clone); verifyNoMoreInteractions(timerDataAggregator); // correct ID set on the clone verify(clone, times(1)).setId(0); verify(httpTimerData, times(0)).setId(0); } /** * Tests the {@link TimerDataChartingCmrProcessor} when influx is active. */ @Test public void chartingProcessorInfluxActive() throws CloneNotSupportedException, SerializationException { TimerDataChartingCmrProcessor processor = new TimerDataChartingCmrProcessor(); processor.timerDataAggregator = timerDataAggregator; processor.serializationManager = serializationManager; processor.influxActive = true; // don't write TimerData timerData = new TimerData(); timerData.setCharting(true); processor.process(timerData, entityManager); verifyZeroInteractions(timerDataAggregator, entityManager); } /** * Timer data processing with {@link InvocationModifierCmrProcessor}. */ @Test public void invocationProcessorTimerData() { InvocationModifierCmrProcessor processor = new InvocationModifierCmrProcessor(Collections.singletonList(chainedProcessor)); InvocationSequenceData parent = new InvocationSequenceData(); parent.setId(10L); TimerData parentTimer = new TimerData(); parentTimer.setCount(1L); parentTimer.setDuration(2L); parent.setTimerData(parentTimer); InvocationSequenceData child = new InvocationSequenceData(); child.setId(20L); TimerData childTimer = new TimerData(); childTimer.setCount(1L); childTimer.setDuration(1L); child.setTimerData(childTimer); child.setParentSequence(parent); parent.setNestedSequences(Collections.singletonList(child)); processor.process(parent, entityManager); // correctly passed to the chained verify(chainedProcessor, times(1)).process(parentTimer, entityManager); verify(chainedProcessor, times(1)).process(child, entityManager); verify(chainedProcessor, times(1)).process(childTimer, entityManager); verifyNoMoreInteractions(chainedProcessor); verifyZeroInteractions(entityManager); // exclusive times in timers are set assertThat(parentTimer.getExclusiveDuration(), is(1d)); assertThat(childTimer.getExclusiveDuration(), is(1d)); // invocation parent is set correctly assertThat(parentTimer.isOnlyFoundInInvocations(), is(true)); assertThat(parentTimer.getInvocationParentsIdSet(), hasSize(1)); assertThat(parentTimer.getInvocationParentsIdSet(), hasItem(10L)); // not that every timer must point to the root invocation assertThat(childTimer.isOnlyFoundInInvocations(), is(true)); assertThat(childTimer.getInvocationParentsIdSet(), hasSize(1)); assertThat(childTimer.getInvocationParentsIdSet(), hasItem(10L)); } /** * Sql data processing with {@link InvocationModifierCmrProcessor}. */ @Test public void invocationProcessorSqlData() { InvocationModifierCmrProcessor processor = new InvocationModifierCmrProcessor(Collections.singletonList(chainedProcessor)); InvocationSequenceData parent = new InvocationSequenceData(); parent.setId(10L); TimerData parentTimer = new TimerData(); parentTimer.setCount(1L); parentTimer.setDuration(2L); parent.setTimerData(parentTimer); InvocationSequenceData child = new InvocationSequenceData(); child.setId(20L); SqlStatementData sql = new SqlStatementData(); sql.setCount(1L); sql.setDuration(1L); child.setSqlStatementData(sql); child.setParentSequence(parent); parent.setNestedSequences(Collections.singletonList(child)); processor.process(parent, entityManager); // correctly passed to the chained verify(chainedProcessor, times(1)).process(parentTimer, entityManager); verify(chainedProcessor, times(1)).process(child, entityManager); verify(chainedProcessor, times(1)).process(sql, entityManager); verifyNoMoreInteractions(chainedProcessor); verifyZeroInteractions(entityManager); // root has info about sql assertThat(parent.isNestedSqlStatements(), is(true)); // exclusive times in parent timer is set assertThat(parentTimer.getExclusiveDuration(), is(1d)); // sql has correct invocation affiliation assertThat(sql.isOnlyFoundInInvocations(), is(true)); assertThat(sql.getInvocationParentsIdSet(), hasSize(1)); assertThat(sql.getInvocationParentsIdSet(), hasItem(10L)); } /** * Simple exception processing with {@link InvocationModifierCmrProcessor}. */ @Test public void invocationProcessorOneExceptionData() { InvocationModifierCmrProcessor processor = new InvocationModifierCmrProcessor(Collections.singletonList(chainedProcessor)); ExceptionMessageCmrProcessor exceptionMessageCmrProcessor = mock(ExceptionMessageCmrProcessor.class); processor.exceptionMessageCmrProcessor = exceptionMessageCmrProcessor; InvocationSequenceData parent = new InvocationSequenceData(); parent.setId(10L); InvocationSequenceData child = new InvocationSequenceData(); child.setId(20L); ExceptionSensorData exceptionSensorData = new ExceptionSensorData(); exceptionSensorData.setExceptionEvent(ExceptionEvent.CREATED); exceptionSensorData.setThrowableIdentityHashCode(1L); child.setExceptionSensorDataObjects(Collections.singletonList(exceptionSensorData)); child.setParentSequence(parent); parent.setNestedSequences(Collections.singletonList(child)); processor.process(parent, entityManager); // correctly passed to the chained verify(chainedProcessor, times(1)).process(child, entityManager); verify(chainedProcessor, times(1)).process(exceptionSensorData, entityManager); verify(exceptionMessageCmrProcessor, times(1)).process(exceptionSensorData, entityManager); verifyNoMoreInteractions(chainedProcessor, exceptionMessageCmrProcessor); verifyZeroInteractions(entityManager); // root has info about sql assertThat(parent.isNestedExceptions(), is(true)); // sql has correct invocation affiliation assertThat(exceptionSensorData.isOnlyFoundInInvocations(), is(true)); assertThat(exceptionSensorData.getInvocationParentsIdSet(), hasSize(1)); assertThat(exceptionSensorData.getInvocationParentsIdSet(), hasItem(10L)); } /** * When we have not created exception event in exception object. */ @Test public void invocationProcessorNotCreatedExceptionData() { InvocationModifierCmrProcessor processor = new InvocationModifierCmrProcessor(Collections.singletonList(chainedProcessor)); ExceptionMessageCmrProcessor exceptionMessageCmrProcessor = mock(ExceptionMessageCmrProcessor.class); processor.exceptionMessageCmrProcessor = exceptionMessageCmrProcessor; InvocationSequenceData parent = new InvocationSequenceData(); parent.setId(10L); InvocationSequenceData child = new InvocationSequenceData(); child.setId(20L); ExceptionSensorData exceptionSensorData = new ExceptionSensorData(); exceptionSensorData.setExceptionEvent(ExceptionEvent.PASSED); exceptionSensorData.setThrowableIdentityHashCode(1L); child.setExceptionSensorDataObjects(Collections.singletonList(exceptionSensorData)); child.setParentSequence(parent); parent.setNestedSequences(Collections.singletonList(child)); processor.process(parent, entityManager); // correctly passed to the chained verify(chainedProcessor, times(1)).process(child, entityManager); verify(chainedProcessor, times(0)).process(exceptionSensorData, entityManager); verify(exceptionMessageCmrProcessor, times(0)).process(exceptionSensorData, entityManager); verifyNoMoreInteractions(chainedProcessor, exceptionMessageCmrProcessor); verifyZeroInteractions(entityManager); // root has info about sql assertThat(parent.isNestedExceptions(), is(nullValue())); // sql has correct invocation affiliation assertThat(exceptionSensorData.isOnlyFoundInInvocations(), is(false)); assertThat(exceptionSensorData.getInvocationParentsIdSet(), is(empty())); } }