package rocks.inspectit.server.messaging; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; 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.reflect.Field; import java.util.Arrays; import java.util.Collections; import java.util.Map; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.slf4j.Logger; import org.springframework.context.ApplicationEvent; import org.testng.annotations.Test; import com.google.common.collect.ImmutableMap; import rocks.inspectit.server.ci.event.ClassInstrumentationChangedEvent; import rocks.inspectit.server.event.AgentDeletedEvent; import rocks.inspectit.server.event.AgentRegisteredEvent; import rocks.inspectit.server.util.AgentStatusDataProvider; import rocks.inspectit.shared.all.cmr.model.PlatformIdent; import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData; import rocks.inspectit.shared.all.communication.data.cmr.AgentStatusData.InstrumentationStatus; import rocks.inspectit.shared.all.communication.message.IAgentMessage; import rocks.inspectit.shared.all.communication.message.UpdatedInstrumentationMessage; import rocks.inspectit.shared.all.instrumentation.config.impl.InstrumentationDefinition; import rocks.inspectit.shared.all.testbase.TestBase; /** * Tests the {@link AgentInstrumentationMessageGate} class. * * @author Marius Oehler * */ @SuppressWarnings("PMD") public class AgentInstrumentationMessageGateTest extends TestBase { @InjectMocks AgentInstrumentationMessageGate messageGate; @Mock Logger log; @Mock AgentMessageProvider messageProvider; @Mock AgentStatusDataProvider agentStatusDataProvider; @SuppressWarnings("unchecked") protected Map<Long, Map<String, InstrumentationDefinition>> getDefinitionBuffer() throws Exception { Field field = AgentInstrumentationMessageGate.class.getDeclaredField("definitionBuffer"); field.setAccessible(true); return (Map<Long, Map<String, InstrumentationDefinition>>) field.get(messageGate); } /** * Tests the * {@link AgentInstrumentationMessageGate#onApplicationEvent(ClassInstrumentationChangedEvent)} * method. */ public static class OnApplicationEvent extends AgentInstrumentationMessageGateTest { @Mock InstrumentationDefinition definitionOne; @Mock InstrumentationDefinition definitionTwo; @Mock InstrumentationDefinition definitionThree; @Mock AgentStatusData statusData; @Test public void changeEventAddInstrumentationDefinitions() throws Exception { when(definitionOne.getClassName()).thenReturn("class.one"); when(definitionTwo.getClassName()).thenReturn("class.two"); when(definitionThree.getClassName()).thenReturn("class.three"); ClassInstrumentationChangedEvent eventOne = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definitionOne, definitionTwo)); ClassInstrumentationChangedEvent eventTwo = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definitionThree)); when(agentStatusDataProvider.getAgentStatusDataMap()).thenReturn(ImmutableMap.of(10L, statusData)); when(statusData.getInstrumentationStatus()).thenReturn(InstrumentationStatus.UP_TO_DATE, InstrumentationStatus.PENDING); long currentTime = System.currentTimeMillis(); messageGate.onApplicationEvent(eventOne); messageGate.onApplicationEvent(eventTwo); verify(definitionOne).getClassName(); verify(definitionTwo).getClassName(); verify(definitionThree).getClassName(); verify(agentStatusDataProvider, times(2)).getAgentStatusDataMap(); verify(statusData, times(2)).getInstrumentationStatus(); verify(statusData).setInstrumentationStatus(InstrumentationStatus.PENDING); ArgumentCaptor<Long> timeCaptor = ArgumentCaptor.forClass(Long.class); verify(statusData).setPendingSinceTime(timeCaptor.capture()); verifyNoMoreInteractions(definitionOne, definitionTwo, definitionThree, agentStatusDataProvider, statusData); verifyZeroInteractions(messageProvider); assertThat(timeCaptor.getValue(), greaterThanOrEqualTo(currentTime)); assertThat(getDefinitionBuffer().entrySet(), hasSize(1)); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(3)); assertThat(getDefinitionBuffer().get(10L), hasEntry("class.one", definitionOne)); assertThat(getDefinitionBuffer().get(10L), hasEntry("class.two", definitionTwo)); assertThat(getDefinitionBuffer().get(10L), hasEntry("class.three", definitionThree)); } @Test @SuppressWarnings("unchecked") public void changeEventAddInstrumentationDefinitionForDiffrentAgents() throws Exception { when(definitionOne.getClassName()).thenReturn("class.one"); when(definitionTwo.getClassName()).thenReturn("class.two"); when(definitionThree.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent eventOne = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definitionOne, definitionTwo)); ClassInstrumentationChangedEvent eventTwo = new ClassInstrumentationChangedEvent(this, 20L, Arrays.asList(definitionThree)); when(agentStatusDataProvider.getAgentStatusDataMap()).thenReturn(Collections.EMPTY_MAP); when(statusData.getInstrumentationStatus()).thenReturn(InstrumentationStatus.UP_TO_DATE, InstrumentationStatus.PENDING); messageGate.onApplicationEvent(eventOne); messageGate.onApplicationEvent(eventTwo); assertThat(getDefinitionBuffer().entrySet(), hasSize(2)); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(2)); assertThat(getDefinitionBuffer().get(10L), hasEntry("class.one", definitionOne)); assertThat(getDefinitionBuffer().get(10L), hasEntry("class.two", definitionTwo)); assertThat(getDefinitionBuffer().get(20L).entrySet(), hasSize(1)); assertThat(getDefinitionBuffer().get(20L), hasEntry("class.one", definitionThree)); } @Test @SuppressWarnings("unchecked") public void changeEventReplaceInstrumentationDefinition() throws Exception { when(definitionOne.getClassName()).thenReturn("class.one"); when(definitionTwo.getClassName()).thenReturn("class.two"); when(definitionThree.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent eventOne = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definitionOne, definitionTwo)); ClassInstrumentationChangedEvent eventTwo = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definitionThree)); when(agentStatusDataProvider.getAgentStatusDataMap()).thenReturn(Collections.EMPTY_MAP); when(statusData.getInstrumentationStatus()).thenReturn(InstrumentationStatus.UP_TO_DATE, InstrumentationStatus.PENDING); messageGate.onApplicationEvent(eventOne); messageGate.onApplicationEvent(eventTwo); assertThat(getDefinitionBuffer().entrySet(), hasSize(1)); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(2)); assertThat(getDefinitionBuffer().get(10L), hasEntry("class.one", definitionThree)); assertThat(getDefinitionBuffer().get(10L), hasEntry("class.two", definitionTwo)); } @Test @SuppressWarnings("unchecked") public void changeEventNoAgentStatusData() throws Exception { when(definitionOne.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent eventOne = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definitionOne)); when(agentStatusDataProvider.getAgentStatusDataMap()).thenReturn(Collections.EMPTY_MAP); messageGate.onApplicationEvent(eventOne); verify(agentStatusDataProvider).getAgentStatusDataMap(); verifyNoMoreInteractions(agentStatusDataProvider); verifyZeroInteractions(statusData, messageProvider); } @Test public void agentDeletedEvent() throws Exception { when(definitionOne.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent event = new ClassInstrumentationChangedEvent(this, 10L, Collections.singletonList(definitionOne)); messageGate.onApplicationEvent(event); AgentDeletedEvent deletedEvent = new AgentDeletedEvent(this, 10L); messageGate.onApplicationEvent(deletedEvent); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(0)); } @Test public void agentRegisteredEvent() throws Exception { when(definitionOne.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent event = new ClassInstrumentationChangedEvent(this, 10L, Collections.singletonList(definitionOne)); messageGate.onApplicationEvent(event); AgentRegisteredEvent registeredEvent = new AgentRegisteredEvent(this, 10L); messageGate.onApplicationEvent(registeredEvent); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(0)); } @Test public void unknownEvent() throws Exception { messageGate.onApplicationEvent(mock(ApplicationEvent.class)); verifyZeroInteractions(definitionOne, definitionTwo, definitionThree, agentStatusDataProvider, statusData, messageProvider); } @Test public void nullEvent() throws Exception { messageGate.onApplicationEvent(null); verifyZeroInteractions(definitionOne, definitionTwo, definitionThree, agentStatusDataProvider, statusData, messageProvider); } } /** * Tests the {@link AgentInstrumentationMessageGate#flush(PlatformIdent)} method. */ public static class Flush extends AgentInstrumentationMessageGateTest { @Mock InstrumentationDefinition definition; @Mock AgentStatusData statusData; @Test @SuppressWarnings("rawtypes") public void successful() throws Exception { when(definition.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent event = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definition)); messageGate.onApplicationEvent(event); when(agentStatusDataProvider.getAgentStatusDataMap()).thenReturn(ImmutableMap.of(10L, statusData)); when(statusData.getInstrumentationStatus()).thenReturn(InstrumentationStatus.PENDING); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(1)); messageGate.flush(10L); ArgumentCaptor<IAgentMessage> messageCaptor = ArgumentCaptor.forClass(IAgentMessage.class); verify(messageProvider).provideMessage(eq(10L), messageCaptor.capture()); verify(statusData).setInstrumentationStatus(InstrumentationStatus.UP_TO_DATE); verifyNoMoreInteractions(statusData, messageProvider); assertThat(((UpdatedInstrumentationMessage) messageCaptor.getValue()).getMessageContent(), contains(definition)); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(0)); } @Test @SuppressWarnings("unchecked") public void unknownPlatformId() throws Exception { when(definition.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent event = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definition)); messageGate.onApplicationEvent(event); when(agentStatusDataProvider.getAgentStatusDataMap()).thenReturn(Collections.EMPTY_MAP); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(1)); messageGate.flush(20L); verifyZeroInteractions(messageProvider); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(1)); } } /** * Tests the {@link AgentInstrumentationMessageGate#clear(PlatformIdent)} method. */ public static class Clear extends AgentInstrumentationMessageGateTest { @Mock InstrumentationDefinition definition; @Test public void successful() throws Exception { when(definition.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent event = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definition)); messageGate.onApplicationEvent(event); messageGate.clear(10L); verifyZeroInteractions(messageProvider); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(0)); } @Test public void unknownPlatformId() throws Exception { when(definition.getClassName()).thenReturn("class.one"); ClassInstrumentationChangedEvent event = new ClassInstrumentationChangedEvent(this, 10L, Arrays.asList(definition)); messageGate.onApplicationEvent(event); messageGate.clear(20L); verifyZeroInteractions(messageProvider); assertThat(getDefinitionBuffer().get(10L).entrySet(), hasSize(1)); } } }