package javaflow.network.impl;
import javaflow.components.*;
import javaflow.components.api.Component;
import javaflow.components.api.OutputPort;
import javaflow.components.api.Packet;
import javaflow.components.metadata.AddMetadata;
import javaflow.components.metadata.MetadataExtractor;
import javaflow.network.api.*;
import javaflow.network.definer.NetworkDefiner;
import javaflow.network.utils.ComponentHandlingTimes;
import javaflow.network.utils.PacketCountInComponent;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class SimpleNetworkBuilderTest {
List out;
NetworkBuilder builder;
@BeforeMethod
public void setUp() {
out = new CopyOnWriteArrayList();
builder = new SimpleNetworkBuilder();
}
@Test
public void simpleGeneratorNetwork() {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("out", ToCollection.class);
from("generator").to("out");
initialize("generator.COUNT", 10);
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 10);
}
private void runNetworkDefinition(final NetworkDefinition definitionBuilder) {
Network network = builder.build(definitionBuilder);
network.runInCurrentThread();
}
@Test(timeOut = 1000)
public void packetListenerIsCalled() {
Network network = builder.build(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("out", ToCollection.class);
from("generator").to("out");
initialize("generator.COUNT", 10);
initialize("out.COLLECTION", out);
}
});
ComponentHandlingTimes times = new ComponentHandlingTimes(network.components().size());
network.addPacketListener(times);
network.runInCurrentThread();
Assert.assertEquals(times.getPacketCount(1), 11); // Initialization packet and the other 10
}
@Test(timeOut = 1000, description = "Tests that non-looping component is able to run multiple times")
public void nonLoopingElementInTheMiddle() {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("copy", Copy.class);
component("out", ToCollection.class);
from("generator").to("copy").to("out");
initialize("generator.COUNT", 10);
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 10);
}
@Test(timeOut = 10000, description = "Tests that a lots of components can be run when not all of them has to run at the same time")
public void lotsOfSequentialMooving() {
final int max = 1000;
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("out", ToCollection.class);
for (int i = 0; i < max; i++) {
component("copy" + i, Copy.class);
}
from("generator").to("copy0");
for (int i = 1; i < max; i++) {
from("copy" + (i - 1)).to("copy" + i);
}
from("copy" + (max - 1)).to("out");
initialize("generator.COUNT", 10);
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 10);
for (int i = 0; i < 10; i++) {
Assert.assertEquals(out.get(i), i);
}
}
@Test(timeOut = 10000, description = "Test that multiple senders work")
public void multipleSenders() {
runNetworkDefinition(new NetworkDefiner() {
{
component("copy", Copy.class);
component("out", ToCollection.class);
int max = 50;
for (int i = 0; i < max; i++) {
final String name = "generator" + i;
component(name, IntegerGenerator.class);
from(name).to("copy");
}
from("copy").to("out");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 500);
}
@Test(timeOut = 1000)
public void onlyOutComponentIsStarted() {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", StaticTestMessageSender.class);
component("out", ToCollection.class);
from("generator").to("out");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 1);
}
@Test(timeOut = 1000)
public void portsInSuperclassAreFilledAlso() {
runNetworkDefinition(new NetworkDefiner() {
{
component("t2", T2.class);
component("out", ToCollection.class);
from("t2").to("out");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 1);
}
private static abstract class T1 implements Component {
OutputPort out;
}
public static class T2 extends T1 {
@Override
public void execute() {
out.createPacket("OK").send();
}
}
@Test(timeOut = 1000)
public void testArrayPorts() {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("splitter", RoundRobinScheduler.class);
component("aggregator", Aggregator.class);
component("out", ToCollection.class);
from("generator").to("splitter");
from("splitter","OUT[0]").to("IN[0]", "aggregator", "OUT").to("out");
from("splitter","OUT[1]").to("IN[1]", "aggregator");
initialize("generator.COUNT", 10);
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 10);
Assert.assertEquals(out, Arrays.asList(0, 2, 4, 6, 8, 1, 3, 5, 7, 9));
}
@Test(timeOut = 1000)
public void testStartAndEndTypes() {
final Map<String, String> meta1 = new HashMap<>();
final Map<String, String> meta2 = new HashMap<>();
meta1.put("a", "1");
meta2.put("b", "2");
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("metaadder", AddMetadata.class);
component("extractor", MetadataExtractor.class);
component("collector", ToCollection.class);
from("generator").to("metaadder").to("extractor");
from("extractor", "METAOUT").to("collector");
initialize("generator.COUNT", 2);
initialize("collector.COLLECTION", out);
initialize("metaadder.METAIN", meta1);
initialize("metaadder.METAIN", meta2);
}
});
Assert.assertEquals(out.size(), 2);
Assert.assertEquals(((Map) out.get(0)).get("a"), "1");
Assert.assertNull(((Map) out.get(0)).get("b"));
Assert.assertEquals(((Map) out.get(1)).get("b"), "2");
Assert.assertNull(((Map) out.get(1)).get("a"));
}
@Test(timeOut = 1000)
public void testMetadataCanBeHandled() {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("marker", MarkSubstream.class);
component("gather", StringsFromSubstreams.class);
component("collector", ToCollection.class);
from("generator").to("marker").to("gather").to("collector");
initialize("generator.COUNT", 10);
initialize("collector.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 1);
Assert.assertEquals(out.get(0), "0123456789");
}
@Test(description = "Test that packet can be assembled to single tree structure and drop still works")
public void assemblingPacketsWorks() {
runNetworkDefinition(new NetworkDefiner() {
{
component("assembler", Assembler.class);
component("out", LogPacketsToCollection.class);
from("assembler").to("out");
initialize("assembler.IN", startPacket("numbers"));
initialize("assembler.IN", packet("1"));
initialize("assembler.IN", packet("2"));
initialize("assembler.IN", endPacket("numbers"));
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 1);
Packet<?> rootPacket = (Packet) out.get(0);
Assert.assertTrue(rootPacket.isTree());
Collection<String> strings = new ArrayList<>();
for (Packet subPacket : rootPacket) {
strings.add(subPacket.getContent().toString());
}
Assert.assertEquals(strings.size(), 2);
Assert.assertTrue(strings.contains("1"));
Assert.assertTrue(strings.contains("2"));
}
@Test(timeOut = 1000)
public void assemblingSubTreeWorks() {
runNetworkDefinition(new NetworkDefiner() {
{
component("assembler", Assembler.class);
component("out", LogPacketsToCollection.class);
from("assembler").to("out");
initialize("assembler.IN", startPacket("characters"));
initialize("assembler.IN", packet("a"));
initialize("assembler.IN", startPacket("numbers"));
initialize("assembler.IN", packet("1"));
initialize("assembler.IN", packet("2"));
initialize("assembler.IN", endPacket("numbers"));
initialize("assembler.IN", packet("b"));
initialize("assembler.IN", endPacket("characters"));
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 1);
Packet<?> rootPacket = (Packet) out.get(0);
Assert.assertTrue(rootPacket.isTree());
Collection<String> strings = new ArrayList<>();
for (Packet subPacket : rootPacket) {
strings.add(subPacket.getContent().toString());
}
Assert.assertEquals(strings.size(), 3);
Assert.assertTrue(strings.contains("a"));
Assert.assertTrue(strings.contains("numbers"));
Assert.assertTrue(strings.contains("b"));
}
@Test(timeOut = 1000)
public void disassemblingWorks() {
runNetworkDefinition(new NetworkDefiner() {
{
component("assembler", Assembler.class);
component("disassembler", Disassembler.class);
component("out", ToCollection.class);
from("assembler").to("disassembler").to("out");
initialize("assembler.IN", startPacket("characters"));
initialize("assembler.IN", packet("a"));
initialize("assembler.IN", startPacket("numbers"));
initialize("assembler.IN", packet("1"));
initialize("assembler.IN", packet("2"));
initialize("assembler.IN", endPacket("numbers"));
initialize("assembler.IN", packet("b"));
initialize("assembler.IN", endPacket("characters"));
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 8);
Assert.assertEquals(out.get(0), "characters");
Assert.assertEquals(out.get(1), "a");
Assert.assertEquals(out.get(2), "numbers");
Assert.assertEquals(out.get(3), "1");
Assert.assertEquals(out.get(4), "2");
Assert.assertEquals(out.get(5), "numbers");
Assert.assertEquals(out.get(6), "b");
Assert.assertEquals(out.get(7), "characters");
}
@Test( //timeOut = 1000,
expectedExceptions = Error.class,
description = "Tests that network detects deadlocks when no component is processing packages")
public void deadlock() {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator 1", IntegerGenerator.class);
component("generator 2", IntegerGenerator.class);
component("ss 1", MarkSubstream.class);
component("ss 2", MarkSubstream.class);
component("rr", RoundRobinScheduler.class);
component("aggregator 1", Aggregator.class);
component("aggregator 2", Aggregator.class);
from("generator 1").to("ss 1").to("aggregator 1.IN[0]").to("rr");
from("generator 2").to("ss 2").to("aggregator 1.IN[1]");
// This will cause dead lock if queue size is less than 22.
// Round robin (rr) will try to send 22 packets (20 numbers, 1 start and 1 end)
// to aggregators _second_ port. Because aggregators tries to empty its
// IN[0] port first the queue for the IN[1] will be filled and rr will block.
// Aggregator on the other hand is waiting the input on the IN[0] and thus
// the packets will never advance.
from("rr.OUT[1]").to("aggregator 2.IN[0]");
from("rr.OUT[0]").to("aggregator 2.IN[1]");
initialize("generator 1.COUNT", 20);
initialize("generator 2.COUNT", 20);
}
});
}
@Test(timeOut = 1000,
description = "Tests that lost packets that are received are detected")
public void lostPacketsAreDetected1() {
try {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("eater", JustEatPacketComponent.class);
from("generator").to("eater");
}
});
Assert.fail("Should have reported missing packets");
} catch (Throwable t) {
Assert.assertTrue(t.getMessage().contains("eater (NOT RUNNING): 10 in component"),
"Error message did not report missing packets correctly:\n" + t.getMessage());
}
}
@Test(timeOut = 1000,
description = "Tests that lost packets that are created inside component are detected")
public void lostPacketsAreDetected2() {
try {
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", CreatePacketsButDontSendThem.class);
}
});
Assert.fail("Should have reported missing packets");
} catch (Throwable t) {
Assert.assertTrue(t.getMessage().contains("generator (NOT RUNNING): 10 in component"),
"Error message did not report missing packets correctly:\n" + t.getMessage());
}
}
@Test(timeOut = 1000,
description = "Tests non correct handling of initialization packets are detected")
public void lostPacketsAreDetected3() {
try {
runNetworkDefinition(new NetworkDefiner() {
{
component("eater", JustEatPacketComponent.class);
initialize("eater.IN", "eat this");
}
});
Assert.fail("Should have reported missing packets");
} catch (Throwable t) {
Assert.assertTrue(t.getMessage().contains("eater (NOT RUNNING): 1 in component"),
"Error message did not report missing packets correctly:\n" + t.getMessage());
}
}
@Test(timeOut = 1000,
description = "Tests closing input port when there is input ")
public void lostPacketsAreDetected4() {
runNetworkDefinition(new NetworkDefiner() {
{
component("dropper", TestDropAndSendComponent.class);
component("out", ToCollection.class);
from("dropper").to("out");
initialize("dropper.IN", "drop this");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 1);
Assert.assertEquals(out.get(0), "testing");
}
@Test(timeOut = 1000,
description = "Tests chaining packets does not confuse the counters")
public void lostPacketsAreDetectedWithChains() {
Network network = (NetworkImpl) builder.build(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("substream", MarkSubstream.class);
component("assembler", Assembler.class);
component("disassembler", Disassembler.class);
component("dropper", Drop.class);
from("generator").to("substream").to("assembler").to("disassembler").to("dropper");
}
});
final PacketCountInComponent listener = new PacketCountInComponent(network.components().size());
network.addPacketListener(listener);
network.runInCurrentThread();
for (NetworkComponent component : network.components()) {
Assert.assertEquals(listener.getCount(component.componentId()), 0, component.componentName() + " has wrong message count.");
}
}
@Test(//timeOut = 1000,
description = "Sending to a closed output port is error")
public void sendingToClosedOuputPort() {
runNetworkDefinition(new NetworkDefiner() {
{
component("non-connected-out", TestSendingThrowsException.class);
component("out", ToCollection.class);
from("non-connected-out","RESULT").to("out");
initialize("non-connected-out.IN", "test message");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 2);
Assert.assertEquals(out.get(0), "testing");
Assert.assertEquals(out.get(1), "success");
}
@Test( timeOut=1000,
description = "Tests that isOpen works, also @MustRun annotation is tested because there is no kicker component used.")
public void isOpenTestAndMustRunWorks() {
runNetworkDefinition(new NetworkDefiner() {
{
component("test output closes", TestOutputPortClosingComponent.class);
component("close input, signal when ready", ReceiveOneSignalAndCloseInputComponent.class);
component("out", ToCollection.class);
from("test output closes").to("close input, signal when ready");
from("close input, signal when ready","SIGNAL").to("SIGNAL", "test output closes");
from("test output closes","RESULT").to("out");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 1, "Content was " + out);
Assert.assertEquals(out.get(0), "SUCCESS", "Content was " + out);
}
@Test
public void testComponentIsNotStartedIfNoInputInPorts() {
runNetworkDefinition(new NetworkDefiner() {
{
component("closer", SendOneMessageWaitAndCloseOutput.class);
component("receiver", ReceiveNonNullPackets.class);
component("out", ToCollection.class);
from("closer").to("receiver");
from("receiver","ERROR").to("out");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 0, "Component was run when it input port was empty!");
}
@Test(timeOut=1000)
public void nonlooperComponentsAllowNetworkToShutdown() {
runNetworkDefinition(new NetworkDefiner() {
{
component("ex", ExtractAndSubstituteSubchain.class);
component("copy", Copy.class);
from("ex","EXTRACTED").to("copy").to("SUBSTITUTE","ex","OUT")
.to(component("drop", Drop.class));
initialize("ex.IN", "a");
initialize("ex.CHAINPATH", "a");
}
});
for (Thread t : Thread.getAllStackTraces().keySet()) {
if ("ex".equals(t.getName())) {
Assert.fail("Extractor component is not shutdown.");
}
}
}
@Test
public void subnetTest() {
final SubnetDefinition everyOther = new NetworkDefiner(){
{
inputPort("IN");
outputPort("OUT");
component("roundRobin", RoundRobinScheduler.class);
component("drop", Drop.class);
from("IN").to("roundRobin");
from("roundRobin","OUT[0]").to("OUT");
from("roundRobin","OUT[1]").to("drop");
}
};
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", IntegerGenerator.class);
component("every other", everyOther);
component("out", ToCollection.class);
from("generator").to("every other").to("out");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 5, "Wrong size of output Output was " + out);
Assert.assertEquals(out.get(0).toString(),"0");
Assert.assertEquals(out.get(1).toString(),"2");
Assert.assertEquals(out.get(2).toString(),"4");
Assert.assertEquals(out.get(3).toString(),"6");
Assert.assertEquals(out.get(4).toString(),"8");
}
@Test
public void subnetRelaunchTest() {
final SubnetDefinition uniq = new NetworkDefiner(){
{
inputPort("IN", SUBSTREAM_SENSITIVE);
outputPort("OUT");
component("unique component", Unique.class);
from("IN").to("unique component").to("OUT");
}
};
runNetworkDefinition(new NetworkDefiner() {
{
component("uniq subnet", uniq);
component("out", ToCollection.class);
from("uniq subnet").to("out");
initialize("out.COLLECTION", out);
initialize("uniq subnet.IN", startPacket(null));
initialize("uniq subnet.IN", packet(1));
initialize("uniq subnet.IN", packet(1));
initialize("uniq subnet.IN", packet(2));
initialize("uniq subnet.IN", endPacket(null));
initialize("uniq subnet.IN", startPacket(null));
initialize("uniq subnet.IN", packet(1));
initialize("uniq subnet.IN", packet(2));
initialize("uniq subnet.IN", endPacket(null));
}
});
Assert.assertEquals(out.size(), 4, "Wrong size of output. Output was " + out);
Assert.assertEquals(out.get(0),1);
Assert.assertEquals(out.get(1),2);
Assert.assertEquals(out.get(2), 1);
Assert.assertEquals(out.get(3),2);
}
@Test
public void substreamSensitiveOutput() {
final SubnetDefinition subnet = new NetworkDefiner(){
{
inputPort("IN", SUBSTREAM_SENSITIVE);
outputPort("OUT", SUBSTREAM_SENSITIVE);
component("pass", PassThrough.class);
from("IN").to("pass").to("OUT");
}
};
runNetworkDefinition(new NetworkDefiner() {
{
component("subnet", subnet);
component("out", LogPacketsToCollection.class);
from("subnet").to("out");
initialize("out.COLLECTION", out);
initialize("subnet.IN", startPacket(null));
initialize("subnet.IN", packet(1));
initialize("subnet.IN", endPacket(null));
initialize("subnet.IN", startPacket(null));
initialize("subnet.IN", packet(2));
initialize("subnet.IN", endPacket(null));
}
});
Assert.assertEquals(out.size(), 6, "Wrong size of output. Output was " + out);
Assert.assertTrue(((Packet) out.get(0)).isStartPacket());
Assert.assertEquals(((Packet) out.get(1)).getContent(),1);
Assert.assertTrue(((Packet) out.get(2)).isEndPacket());
Assert.assertTrue(((Packet) out.get(3)).isStartPacket());
Assert.assertEquals(((Packet) out.get(4)).getContent(),2);
Assert.assertTrue(((Packet) out.get(5)).isEndPacket());
}
@Test(description = "There have been problem that actual network does not get started by packet from subnet")
public void subnetStartsNetwork() {
final SubnetDefinition subnet = new NetworkDefiner(){
{
outputPort("OUT");
from(c("generator", IntegerGenerator.class)).to("OUT");
}
};
runNetworkDefinition(new NetworkDefiner() {
{
component("generator", subnet);
component("out", ToCollection.class);
from("generator").to(c("passthrough",PassThrough.class)).to("out");
initialize("out.COLLECTION", out);
}
});
Assert.assertEquals(out.size(), 10, "Wrong size of output Output was " + out);
}
@Test(timeOut = 3000,
expectedExceptions = Error.class,
description = "Shutdown network when there is uncatch exception in component")
public void shutdownOnException() {
runNetworkDefinition(new NetworkDefiner() {
{
component("fail", FailComponent.class);
component("generator", ForeverGenerator.class);
component("drop", Drop.class);
from("generator","OUT").to("IN","drop");
}
});
}
static final class FailComponent implements Component{
@Override
public void execute() {
throw new Error("I just fail");
}
}
static final class ForeverGenerator implements Component{
OutputPort out;
@Override
public void execute() {
try {
Thread.sleep(2000);
} catch (InterruptedException ignored) {
}
while(true) {
out.createPacket("data").send();
try {
Thread.sleep(1);
} catch (InterruptedException ignored) {
}
}
}
}
}