| /******************************************************************************* |
| * Copyright (c) 2017 École Polytechnique de Montréal |
| * |
| * All rights reserved. This program and the accompanying materials are |
| * made available under the terms of the Eclipse Public License 2.0 which |
| * accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| *******************************************************************************/ |
| |
| package org.eclipse.tracecompass.tmf.core.tests.event.matching; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotNull; |
| |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.tracecompass.tmf.core.event.ITmfEvent; |
| import org.eclipse.tracecompass.tmf.core.event.TmfEvent; |
| import org.eclipse.tracecompass.tmf.core.event.TmfEventField; |
| import org.eclipse.tracecompass.tmf.core.event.matching.IEventMatchingKey; |
| import org.eclipse.tracecompass.tmf.core.event.matching.ITmfMatchEventDefinition; |
| import org.eclipse.tracecompass.tmf.core.event.matching.TmfEventDependency.DependencyEvent; |
| import org.eclipse.tracecompass.tmf.core.event.matching.TmfEventMatching; |
| import org.eclipse.tracecompass.tmf.core.event.matching.TmfEventMatching.Direction; |
| import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; |
| import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; |
| import org.eclipse.tracecompass.tmf.tests.stubs.event.TmfEventTypeStub; |
| import org.eclipse.tracecompass.tmf.tests.stubs.trace.TmfTraceStub; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import com.google.common.collect.Table; |
| |
| /** |
| * Test the {@link TmfEventMatching} class |
| * |
| * @author Geneviève Bastien |
| */ |
| public class TmfEventMatchingTest { |
| |
| private static final @NonNull IProgressMonitor PROGRESS_MONITOR = new NullProgressMonitor(); |
| private TmfTraceStub fT1; |
| private TmfTraceStub fT2; |
| private @NonNull Collection<@NonNull ITmfTrace> fTraces = Collections.emptyList(); |
| |
| private static class IntMatchingKey implements IEventMatchingKey { |
| |
| private final @NonNull Integer fId; |
| |
| public IntMatchingKey(@NonNull Integer id) { |
| fId = id; |
| } |
| |
| @Override |
| public int hashCode() { |
| return fId.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (o instanceof IntMatchingKey) { |
| IntMatchingKey key = (IntMatchingKey) o; |
| return key.fId.equals(fId); |
| } |
| return false; |
| } |
| } |
| |
| private static class StringMatchingKey implements IEventMatchingKey { |
| |
| private final @NonNull String fStr; |
| |
| public StringMatchingKey(@NonNull String id) { |
| fStr = id; |
| } |
| |
| @Override |
| public int hashCode() { |
| return fStr.hashCode(); |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object o) { |
| if (o instanceof StringMatchingKey) { |
| StringMatchingKey key = (StringMatchingKey) o; |
| return key.fStr.equals(fStr); |
| } |
| return false; |
| } |
| } |
| |
| private static class StubEventMatching implements ITmfMatchEventDefinition { |
| |
| @Override |
| public IEventMatchingKey getEventKey(ITmfEvent event) { |
| if (!(event instanceof MatchEventStub)) { |
| return null; |
| } |
| int id = ((MatchEventStub) event).fId; |
| // Use a String key if the id is negative, otherwise use an int key |
| if (id < 0) { |
| return new StringMatchingKey(String.valueOf(id)); |
| } |
| return new IntMatchingKey(id); |
| } |
| |
| @Override |
| public boolean canMatchTrace(ITmfTrace trace) { |
| return true; |
| } |
| |
| @Override |
| public Direction getDirection(ITmfEvent event) { |
| if (!(event instanceof MatchEventStub)) { |
| return null; |
| } |
| return ((MatchEventStub) event).fDirection; |
| } |
| |
| } |
| |
| private static class MatchEventStub extends TmfEvent { |
| |
| private final int fId; |
| private final Direction fDirection; |
| |
| public MatchEventStub(final ITmfTrace trace, final ITmfTimestamp timestamp, int id, Direction direction) { |
| super(trace, |
| ITmfContext.UNKNOWN_RANK, |
| timestamp, |
| new TmfEventTypeStub(), |
| new TmfEventField("stub", "stub", null)); |
| fId = id; |
| fDirection = direction; |
| } |
| |
| } |
| |
| private static class TmfEventMatchingStub extends TmfEventMatching { |
| |
| public TmfEventMatchingStub(Collection<@NonNull ITmfTrace> traces) { |
| super(traces); |
| } |
| |
| @Override |
| public Table<ITmfTrace, IEventMatchingKey, DependencyEvent> getUnmatchedIn() { |
| return super.getUnmatchedIn(); |
| } |
| |
| @Override |
| public Table<ITmfTrace, IEventMatchingKey, DependencyEvent> getUnmatchedOut() { |
| return super.getUnmatchedOut(); |
| } |
| |
| } |
| |
| /** |
| * Initializing the traces |
| */ |
| @Before |
| public void init() { |
| TmfTraceStub t1 = new TmfTraceStub(); |
| t1.init("t1"); |
| TmfTraceStub t2 = new TmfTraceStub(); |
| t2.init("t2"); |
| |
| Collection<@NonNull ITmfTrace> traces = new LinkedList<>(); |
| traces.add(t1); |
| traces.add(t2); |
| fT1 = t1; |
| fT2 = t2; |
| fTraces = traces; |
| TmfEventMatching.registerMatchObject(new StubEventMatching()); |
| } |
| |
| /** |
| * Clean up |
| */ |
| @After |
| public void cleanup() { |
| TmfTraceStub trace = fT1; |
| if (trace != null) { |
| trace.dispose(); |
| } |
| trace = fT2; |
| if (trace != null) { |
| trace.dispose(); |
| } |
| } |
| |
| /** |
| * Test event matching where event matches only from the same hosts, no cleanup |
| * is expected |
| */ |
| @Test |
| public void testHostToSelf() { |
| // Test-specific data initialization |
| int count = 1; |
| int matchedKey1 = count++; |
| int unmatchedKey = count++; |
| int matchedKey3 = count++; |
| |
| Collection<@NonNull ITmfTrace> traces = fTraces; |
| assertNotNull(traces); |
| TmfEventMatchingStub matching = new TmfEventMatchingStub(traces); |
| matching.initMatching(); |
| |
| TmfTraceStub trace = fT1; |
| assertNotNull(trace); |
| |
| // Add an unmatched cause |
| matching.matchEvent(new MatchEventStub(trace, TmfTimestamp.fromNanos(1L), matchedKey1, Direction.CAUSE), trace, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(trace).size()); |
| |
| // Add the matching effect, the events should be removed |
| matching.matchEvent(new MatchEventStub(trace, TmfTimestamp.fromNanos(3L), matchedKey1, Direction.EFFECT), trace, PROGRESS_MONITOR); |
| assertEquals(0, matching.getUnmatchedOut().row(trace).size()); |
| |
| // Add an unmatched effect |
| matching.matchEvent(new MatchEventStub(trace, TmfTimestamp.fromNanos(5L), unmatchedKey, Direction.EFFECT), trace, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedIn().row(trace).size()); |
| assertEquals(0, matching.getUnmatchedOut().row(trace).size()); |
| |
| // Add an unmatched cause |
| matching.matchEvent(new MatchEventStub(trace, TmfTimestamp.fromNanos(7L), matchedKey3, Direction.CAUSE), trace, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedIn().row(trace).size()); |
| assertEquals(1, matching.getUnmatchedOut().row(trace).size()); |
| |
| // Add a matched effect, the cause should be removed, but the unmatched one |
| // should still be there |
| matching.matchEvent(new MatchEventStub(trace, TmfTimestamp.fromNanos(7L), matchedKey3, Direction.EFFECT), trace, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedIn().row(trace).size()); |
| assertEquals(0, matching.getUnmatchedOut().row(trace).size()); |
| } |
| |
| /** |
| * Test event matching from different hosts, there should be cleanup of event |
| * data when matches occur |
| */ |
| @Test |
| public void testMultiHost() { |
| // Test-specific data initialization |
| int count = 1; |
| int matchedKey1 = count++; |
| int unmatchedKey1 = count++; |
| int unmatchedKey2 = count++; |
| int unmatchedKey3 = count++; |
| int unmatchedKey4 = count++; |
| int matchedKey2 = count++; |
| |
| Collection<@NonNull ITmfTrace> traces = fTraces; |
| assertNotNull(traces); |
| TmfEventMatchingStub matching = new TmfEventMatchingStub(traces); |
| matching.initMatching(); |
| |
| TmfTraceStub t1 = fT1; |
| assertNotNull(t1); |
| TmfTraceStub t2 = fT2; |
| assertNotNull(t2); |
| |
| /* |
| * Have t1 send and receive all its packets before any of t2, no match should be |
| * made at this point |
| */ |
| |
| // Add an unmatched cause |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(1L), unmatchedKey1, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| |
| // Add an unmatched effect |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(3L), unmatchedKey2, Direction.EFFECT), t1, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(1, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add another unmatched effect |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(5L), unmatchedKey3, Direction.EFFECT), t1, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(2, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add a cause that will be matched later |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(7L), matchedKey1, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(2, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(2, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add an effect that will be matched later |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(11L), matchedKey2, Direction.EFFECT), t1, PROGRESS_MONITOR); |
| assertEquals(2, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(3, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add an unmatched cause |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(13L), unmatchedKey4, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(3, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(3, matching.getUnmatchedIn().row(t1).size()); |
| |
| /* Let t2 receive and send the packets */ |
| |
| // Match the sent packet, it should cleanup the unmatched outgoing from t1, |
| // except the last one |
| matching.matchEvent(new MatchEventStub(t2, TmfTimestamp.fromNanos(20L), matchedKey1, Direction.EFFECT), t2, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(3, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Match the received packet, it should cleanup the unmatched incoming from t1 |
| matching.matchEvent(new MatchEventStub(t2, TmfTimestamp.fromNanos(21L), matchedKey2, Direction.CAUSE), t2, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(0, matching.getUnmatchedIn().row(t1).size()); |
| |
| } |
| |
| /** |
| * Test event matching from different hosts and with different key types, there |
| * should be cleanup of event data when matches occur, but only for the appropriate key |
| */ |
| @Test |
| public void testMultiHostMultiKeyType() { |
| // Test-specific data initialization |
| int count = 1; |
| int matchedKey1 = count++; |
| int unmatchedKey1 = count++; |
| int unmatchedKey2 = count++; |
| int unmatchedKey3 = count++; |
| int unmatchedKey4 = count++; |
| int unmatchedStringKey1 = -count++; |
| int matchedStringKey1 = -count++; |
| int matchedKey2 = count++; |
| |
| Collection<@NonNull ITmfTrace> traces = fTraces; |
| assertNotNull(traces); |
| TmfEventMatchingStub matching = new TmfEventMatchingStub(traces); |
| matching.initMatching(); |
| |
| TmfTraceStub t1 = fT1; |
| assertNotNull(t1); |
| TmfTraceStub t2 = fT2; |
| assertNotNull(t2); |
| |
| /* |
| * Have t1 send and receive all its packets before any of t2, no match should be |
| * made at this point |
| */ |
| |
| // Add an unmatched cause |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(1L), unmatchedKey1, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| |
| // Add an unmatched effect |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(3L), unmatchedKey2, Direction.EFFECT), t1, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(1, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add another unmatched effect |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(5L), unmatchedKey3, Direction.EFFECT), t1, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(2, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add a string key that will not be matched |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(6L), unmatchedStringKey1, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(2, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(2, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add a cause that will be matched later |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(7L), matchedKey1, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(3, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(2, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add an effect that will be matched later |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(11L), matchedKey2, Direction.EFFECT), t1, PROGRESS_MONITOR); |
| assertEquals(3, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(3, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add an unmatched cause |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(13L), unmatchedKey4, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(4, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(3, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Add a string key that will not be matched |
| matching.matchEvent(new MatchEventStub(t1, TmfTimestamp.fromNanos(15L), matchedStringKey1, Direction.CAUSE), t1, PROGRESS_MONITOR); |
| assertEquals(5, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(3, matching.getUnmatchedIn().row(t1).size()); |
| |
| /* Let t2 receive and send the packets */ |
| |
| // Match the sent packet, it should cleanup the unmatched outgoing from t1, |
| // except the last one and the strings |
| matching.matchEvent(new MatchEventStub(t2, TmfTimestamp.fromNanos(20L), matchedKey1, Direction.EFFECT), t2, PROGRESS_MONITOR); |
| assertEquals(3, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(3, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Match the received packet, it should cleanup the unmatched incoming from t1 |
| matching.matchEvent(new MatchEventStub(t2, TmfTimestamp.fromNanos(21L), matchedKey2, Direction.CAUSE), t2, PROGRESS_MONITOR); |
| assertEquals(3, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(0, matching.getUnmatchedIn().row(t1).size()); |
| |
| // Match the received string key, it should cleanup the unmatched string key from t1 |
| matching.matchEvent(new MatchEventStub(t2, TmfTimestamp.fromNanos(22L), matchedStringKey1, Direction.EFFECT), t2, PROGRESS_MONITOR); |
| assertEquals(1, matching.getUnmatchedOut().row(t1).size()); |
| assertEquals(0, matching.getUnmatchedIn().row(t1).size()); |
| |
| } |
| |
| } |