blob: af0e17045a57334c50ee07895e377c85ede4c7c1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2017 Ericsson and others
*
* 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
*
* Contributors:
* Bernd Hufmann - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.tmf.core.tests.signal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.tmf.core.component.TmfComponent;
import org.eclipse.tracecompass.tmf.core.signal.TmfEndSynchSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignal;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalHandler;
import org.eclipse.tracecompass.tmf.core.signal.TmfSignalManager;
import org.eclipse.tracecompass.tmf.core.signal.TmfStartSynchSignal;
import org.junit.After;
import org.junit.Test;
/**
* Test suite for {@link TmfSignalManager}
*
* @author Bernd Hufmann
*/
public class TmfSignalManagerTest {
private final @NonNull TestSignalSender signalSender = new TestSignalSender();
/**
* After-test cleanup
*/
@After
public void tearDown() {
signalSender.dispose();
}
// ------------------------------------------------------------------------
// Test cases
// ------------------------------------------------------------------------
/**
* Test send and receive including synch signals.
*/
@Test
public void testSendReceive() {
final int NB_HANDLERS = 10;
TestSignalHandler[] signalReceivers = new TestSignalHandler[NB_HANDLERS];
for (int i = 0; i < NB_HANDLERS; i++) {
signalReceivers[i] = new TestSignalHandler();
}
final TestSignal1 firstSignal = new TestSignal1(signalSender);
final TestSignal2 secondSignal = new TestSignal2(signalSender);
final Class<?>[] expectedOrder = new Class[] {
TmfStartSynchSignal.class, TestSignal1.class, TmfEndSynchSignal.class,
TmfStartSynchSignal.class, TestSignal2.class, TmfEndSynchSignal.class };
try {
signalSender.sendSignal(firstSignal);
signalSender.sendSignal(secondSignal);
for (int i = 0; i < NB_HANDLERS; i++) {
assertEquals(expectedOrder.length, signalReceivers[i].receivedSignals.size());
for (int k = 0; k < expectedOrder.length; k++) {
assertEquals(signalReceivers[i].receivedSignals.get(k).getClass(), expectedOrder[k]);
}
for (int k = 0; k < expectedOrder.length; k += 3) {
// Verify signal IDs
int startSyncId = signalReceivers[i].receivedSignals.get(k).getReference();
int signalId = signalReceivers[i].receivedSignals.get(k + 1).getReference();
int endSyncId = signalReceivers[i].receivedSignals.get(k + 2).getReference();
assertEquals(startSyncId, signalId);
assertEquals(startSyncId, endSyncId);
}
}
} finally {
// Make sure that handlers are disposed in any case (success or not success)
for (int i = 0; i < NB_HANDLERS; i++) {
signalReceivers[i].dispose();
}
}
}
/**
* Test inbound blacklisting
*/
@Test
public void testInboundBlacklisting() {
testInboundBlacklisting(Arrays.asList(),
Arrays.asList(
TmfStartSynchSignal.class, TestSignal1.class, TmfEndSynchSignal.class,
TmfStartSynchSignal.class, TestSignal2.class, TmfEndSynchSignal.class));
testInboundBlacklisting(Arrays.asList(TestSignal1.class),
Arrays.asList(
TmfStartSynchSignal.class, TmfEndSynchSignal.class,
TmfStartSynchSignal.class, TestSignal2.class, TmfEndSynchSignal.class));
testInboundBlacklisting(Arrays.asList(TestSignal2.class),
Arrays.asList(
TmfStartSynchSignal.class, TestSignal1.class, TmfEndSynchSignal.class,
TmfStartSynchSignal.class, TmfEndSynchSignal.class));
testInboundBlacklisting(Arrays.asList(TestSignal1.class, TestSignal2.class),
Arrays.asList(
TmfStartSynchSignal.class, TmfEndSynchSignal.class,
TmfStartSynchSignal.class, TmfEndSynchSignal.class));
testInboundBlacklisting(Arrays.asList(TmfSignal.class),
Arrays.asList());
}
private void testInboundBlacklisting(List<@NonNull Class<? extends TmfSignal>> ignoredSignals, List<Class<? extends TmfSignal>> expectedSignals) {
TestSignalHandler receiver = new TestSignalHandler();
/* Set the inbound blacklisting */
for (@NonNull Class<? extends TmfSignal> ignoredSignal : ignoredSignals) {
TmfSignalManager.addIgnoredInboundSignal(receiver, ignoredSignal);
}
/* Send signals */
signalSender.sendSignal(new TestSignal1(signalSender));
signalSender.sendSignal(new TestSignal2(signalSender));
/* Check received signals */
assertEquals(expectedSignals.size(), receiver.receivedSignals.size());
for (int i = 0; i < expectedSignals.size(); i++) {
assertEquals(expectedSignals.get(i), receiver.receivedSignals.get(i).getClass());
}
receiver.dispose();
}
/**
* Test outbound blacklisting
*/
@Test
public void testOutboundBlacklisting() {
testOutboundBlacklisting(Arrays.asList(),
Arrays.asList(
TmfStartSynchSignal.class, TestSignal1.class, TmfEndSynchSignal.class,
TmfStartSynchSignal.class, TestSignal2.class, TmfEndSynchSignal.class));
testOutboundBlacklisting(Arrays.asList(TestSignal1.class),
Arrays.asList(
TmfStartSynchSignal.class, TestSignal2.class, TmfEndSynchSignal.class));
testOutboundBlacklisting(Arrays.asList(TestSignal2.class),
Arrays.asList(
TmfStartSynchSignal.class, TestSignal1.class, TmfEndSynchSignal.class));
testOutboundBlacklisting(Arrays.asList(TestSignal1.class, TestSignal2.class),
Arrays.asList());
testOutboundBlacklisting(Arrays.asList(TmfSignal.class),
Arrays.asList());
}
private void testOutboundBlacklisting(List<@NonNull Class<? extends TmfSignal>> blockedSignals, List<Class<? extends TmfSignal>> expectedSignals) {
TestSignalHandler receiver = new TestSignalHandler();
/* Set the outbound blacklisting */
for (@NonNull Class<? extends TmfSignal> blockedSignal : blockedSignals) {
TmfSignalManager.addIgnoredOutboundSignal(signalSender, blockedSignal);
}
/* Send signals */
signalSender.sendSignal(new TestSignal1(signalSender));
signalSender.sendSignal(new TestSignal2(signalSender));
/* Check received signals */
assertEquals(expectedSignals.size(), receiver.receivedSignals.size());
for (int i = 0; i < expectedSignals.size(); i++) {
assertEquals(expectedSignals.get(i), receiver.receivedSignals.get(i).getClass());
}
receiver.dispose();
TmfSignalManager.clearIgnoredOutboundSignalList(signalSender);
}
/**
* Test nesting signals. Verify that they are handled in the same thread.
*/
@Test
public void testNestedSignals() {
TestSignalHandlerNested signalResender = new TestSignalHandlerNested();
TestSignalHandler signalReceiver = new TestSignalHandler();
final TestSignal1 mainSignal = new TestSignal1(signalSender);
final Class<?>[] expectedOrder = new Class[] {
TmfStartSynchSignal.class,
TmfStartSynchSignal.class,
TestSignal2.class,
TmfEndSynchSignal.class,
TmfStartSynchSignal.class,
TmfStartSynchSignal.class,
TestSignal4.class,
TmfEndSynchSignal.class,
TestSignal3.class,
TmfEndSynchSignal.class,
TestSignal1.class,
TmfEndSynchSignal.class
};
/*
* Index of signals in array signalReceiver.receivedSignals which have
* to have the same signal ID.
*/
final int[] sameSigNoIndex = new int[] {
0, 10, 11, 1, 2, 3, 4, 8, 9, 5, 6, 7
};
try {
signalSender.sendSignal(mainSignal);
assertEquals(expectedOrder.length, signalReceiver.receivedSignals.size());
for (int i = 0; i < expectedOrder.length; i++) {
assertEquals(signalReceiver.receivedSignals.get(i).getClass(), expectedOrder[i]);
}
for (int i = 0; i < sameSigNoIndex.length; i+=3) {
// Verify signal IDs
int startSyncId = signalReceiver.receivedSignals.get(sameSigNoIndex[i]).getReference();
int signalId = signalReceiver.receivedSignals.get(sameSigNoIndex[i + 1]).getReference();
int endSyncId = signalReceiver.receivedSignals.get(sameSigNoIndex[i + 2]).getReference();
assertEquals(startSyncId, signalId);
assertEquals(startSyncId, endSyncId);
}
} finally {
// Make sure that handlers are disposed in any case (success or not success)
signalResender.dispose();
signalReceiver.dispose();
}
}
/**
* Test concurrent signals. Verify that they are handled one after each
* other.
*/
@Test
public void testConcurrentSignals() {
TestSignalHandlerNested signalResender = new TestSignalHandlerNested();
TestSignalHandler signalReceiver = new TestSignalHandler(false, null);
/*
* Test of synchronization of signal manager.
*
* The order of received signals is either set of signals triggered by
* thread1 before set of signals triggered by thread2 or the other way
* around.
*
* If both received sets were interleaved then the synchronization of
* the signal manager would be not working.
*/
final Class<?>[] expectedOrder1 = new Class[] {
TestSignal2.class, TestSignal4.class, TestSignal3.class, TestSignal1.class, // signals triggered by thread 1
TestSignal4.class // signal triggered by thread2
};
final Class<?>[] expectedOrder2 = new Class[] {
TestSignal4.class, // signal triggered by thread2
TestSignal2.class, TestSignal4.class, TestSignal3.class, TestSignal1.class // signals triggered by thread 1
};
/*
* Run it multiple times so that both expected order are triggered
*/
try {
for (int k = 0; k < 10; k++) {
// Latch to ensure that both threads are started
final CountDownLatch startLatch = new CountDownLatch(2);
// Latch to ensure that signals are send roughly at the same time
final CountDownLatch sendLatch = new CountDownLatch(1);
// Latch to ensure that both treads are finished
final CountDownLatch endLatch = new CountDownLatch(2);
signalReceiver.receivedSignals.clear();
Thread senderThread1 = new Thread() {
@Override
public void run() {
startLatch.countDown();
try {
sendLatch.await();
} catch (InterruptedException e) {
}
signalSender.sendSignal(new TestSignal1(signalSender));
endLatch.countDown();
}
};
Thread senderThread2 = new Thread() {
@Override
public void run() {
startLatch.countDown();
try {
sendLatch.await();
} catch (InterruptedException e) {
}
signalSender.sendSignal(new TestSignal4(signalSender));
endLatch.countDown();
}
};
senderThread1.start();
senderThread2.start();
try {
startLatch.await();
} catch (InterruptedException e) {
}
sendLatch.countDown();
try {
endLatch.await();
} catch (InterruptedException e) {
}
assertEquals(expectedOrder1.length, signalReceiver.receivedSignals.size());
boolean pass = true;
for (int i = 0; i < expectedOrder1.length; i++) {
if (!signalReceiver.receivedSignals.get(i).getClass().equals(expectedOrder1[i])) {
pass = false;
break;
}
}
if (!pass) {
for (int i = 0; i < expectedOrder2.length; i++) {
if (!signalReceiver.receivedSignals.get(i).getClass().equals(expectedOrder2[i])) {
fail("Concurrent signal test failure!");
}
}
}
}
} finally {
// Make sure that handlers are disposed in any case (success or not success)
signalResender.dispose();
signalReceiver.dispose();
}
}
/**
* Test broadcastAsync()
*/
@Test
public void testBroadcastAsync() {
TestSignalHandlerNested signalResender = new TestSignalHandlerNested(false);
final int NB_HANDLERS = 10;
final CountDownLatch latch = new CountDownLatch(NB_HANDLERS);
TestSignalHandler[] signalReceivers = new TestSignalHandler[NB_HANDLERS];
for (int i = 0; i < NB_HANDLERS; i++) {
signalReceivers[i] = new TestSignalHandler(false, latch);
}
final Class<?>[] expectedOrder = new Class[] {
TestSignal1.class, TestSignal2.class, TestSignal3.class, TestSignal4.class
};
try {
final TestSignal1 mainSignal = new TestSignal1(signalSender);
signalSender.sendSignal(mainSignal);
try {
latch.await();
} catch (InterruptedException e) {
}
for (int i = 0; i < NB_HANDLERS; i++) {
assertEquals(expectedOrder.length, signalReceivers[i].receivedSignals.size());
for (int k = 0; k < expectedOrder.length; k++) {
assertEquals(signalReceivers[i].receivedSignals.get(k).getClass(), expectedOrder[k]);
}
}
} finally {
// Make sure that handlers are disposed in any case (success or not success)
for (int i = 0; i < NB_HANDLERS; i++) {
signalReceivers[i].dispose();
}
signalResender.dispose();
}
}
// ------------------------------------------------------------------------
// Helper classes
// ------------------------------------------------------------------------
/**
* Signal sender
*/
private class TestSignalSender extends TmfComponent {
TestSignalSender() {
super("TestSignalSender");
}
/**
* Send a signal
*
* @param signal
* main signal to send
*/
public void sendSignal(TmfSignal signal) {
broadcast(signal);
}
}
/**
* Signal handler implementation for testing nested signals.
* Needs to be public so TmfSignalManager can see it.
*/
public class TestSignalHandlerNested extends AbstractBaseSignalHandler {
private boolean sync;
/**
* Constructor
*/
private TestSignalHandlerNested() {
this(true);
}
/**
* Constructor
*
* @param sync
* log sync signals
*
*/
private TestSignalHandlerNested(boolean sync) {
super("TestSignalHandlerNested", false);
this.sync = sync;
TmfSignalManager.deregister(this);
TmfSignalManager.registerVIP(this);
}
/**
* Receive a signal of type TestSignal1.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveSignal1(final TestSignal1 signal) {
if (sync) {
broadcast(new TestSignal2(signal.getSource()));
broadcast(new TestSignal3(signal.getSource()));
} else {
broadcastAsync(new TestSignal2(signal.getSource()));
broadcastAsync(new TestSignal3(signal.getSource()));
}
}
/**
* Receive a signal of type TestSignal3.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveSignal3(final TestSignal3 signal) {
if (sync) {
broadcast(new TestSignal4(signal.getSource()));
} else {
broadcastAsync(new TestSignal4(signal.getSource()));
}
}
}
/**
* Signal handler implementation for testing of sending and receiving
* signals.
*/
public class TestSignalHandler extends AbstractBaseSignalHandler {
private CountDownLatch latch;
/**
* Constructor
*
*/
private TestSignalHandler() {
this(true, null);
}
/**
* Constructor
*
* @param logSyncSigs
* log sync signals
* @param latch
* latch to count down when receiving last signal
* (TmfSingal4)
*/
private TestSignalHandler(boolean logSyncSigs, CountDownLatch latch) {
super("TestSignalHandler", logSyncSigs);
this.latch = latch;
}
/**
* Receive a signal of type TestSignal1.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveSignal1(final TestSignal1 signal) {
receivedSignals.add(signal);
}
/**
* Receive a signal of type TestSignal2.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveSignal2(final TestSignal2 signal) {
receivedSignals.add(signal);
}
/**
* Receive a signal of type TestSignal3.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveSignal3(final TestSignal3 signal) {
receivedSignals.add(signal);
}
/**
* Receive a signal of type TestSignal4.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveSignal4(final TestSignal4 signal) {
receivedSignals.add(signal);
if (latch != null) {
latch.countDown();
}
}
}
/**
* Base signal handler for start and end sync signals.
*/
public abstract class AbstractBaseSignalHandler extends TmfComponent {
List<TmfSignal> receivedSignals = new ArrayList<>();
private boolean logSyncSigs;
private AbstractBaseSignalHandler(String name, boolean logSyncSignal) {
super(name);
this.logSyncSigs = logSyncSignal;
}
/**
* Receive a signal of type TmfStartSynchSignal.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveStartSynch(final TmfStartSynchSignal signal) {
if (logSyncSigs) {
receivedSignals.add(signal);
}
}
/**
* Receive a signal of type TmfEndSynchSignal.
*
* @param signal
* Signal received
*/
@TmfSignalHandler
public void receiveEndSynch(final TmfEndSynchSignal signal) {
if (logSyncSigs) {
receivedSignals.add(signal);
}
}
}
/**
* Test Signal object
*/
private class TestSignal1 extends TmfSignal {
public TestSignal1(Object source) {
super(source);
}
}
/**
* Test Signal object
*/
private class TestSignal2 extends TmfSignal {
public TestSignal2(Object source) {
super(source);
}
}
/**
* Test Signal object
*/
private class TestSignal3 extends TmfSignal {
public TestSignal3(Object source) {
super(source);
}
}
/**
* Test Signal object
*/
private class TestSignal4 extends TmfSignal {
public TestSignal4(Object source) {
super(source);
}
}
}