diff --git a/configurations/evaluation/settingsProphet.txt b/configurations/evaluation/settingsProphet.txt new file mode 100644 index 0000000000000000000000000000000000000000..0a2d1365bd247123ca3decf6cc3da1cca7cd500f --- /dev/null +++ b/configurations/evaluation/settingsProphet.txt @@ -0,0 +1,4 @@ +# Add this file as argument if you want to use ProphetRouter + +Group.router = ProphetRouter +ProphetRouter.secondsInTimeUnit = 30 \ No newline at end of file diff --git a/src/routing/ProphetRouter.java b/src/routing/ProphetRouter.java index 40738cdf632e9b0dbca0c6cf4ff3f340f8fb0461..11ce70e23adcfac74a98379daec708a489d109ff 100644 --- a/src/routing/ProphetRouter.java +++ b/src/routing/ProphetRouter.java @@ -12,6 +12,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import core.DataMessage; +import core.MulticastMessage; +import routing.util.DatabaseApplicationUtil; import routing.util.RoutingInfo; import util.Tuple; @@ -55,7 +58,7 @@ public class ProphetRouter extends ActiveRouter { private double beta; /** delivery predictabilities */ - private Map<DTNHost, Double> preds; + private Map<Integer, Double> preds; /** last delivery predictability update (sim)time */ private double lastAgeUpdate; @@ -93,7 +96,7 @@ public class ProphetRouter extends ActiveRouter { * Initializes predictability hash */ private void initPreds() { - this.preds = new HashMap<DTNHost, Double>(); + this.preds = new HashMap<>(); } @Override @@ -115,9 +118,45 @@ public class ProphetRouter extends ActiveRouter { private void updateDeliveryPredFor(DTNHost host) { double oldValue = getPredFor(host); double newValue = oldValue + (1 - oldValue) * P_INIT; - preds.put(host, newValue); + preds.put(host.getAddress(), newValue); } + /** + * Returns the current prediction (P) value for a message or 0 if no entry for any recipient exists. + * @param message Message to find P for. Either a one-to-one message or a multicast. + * @return The current P value. In case of multicast, the maximum P value of remaining recipients. + */ + private double getPredFor(Message message) { + switch (message.getType()) { + case ONE_TO_ONE: + return this.getPredFor(message.getTo()); + case MULTICAST: + MulticastMessage multicast = (MulticastMessage)message; + return this.getMaxPredFor(multicast.getRemainingRecipients()); + default: + throw new IllegalArgumentException( + "No delivery predictability for messages of type " + message.getType() + " defined!"); + } + } + + /** + * Returns the maximum prediction (P) value for all hosts matching the provided addresses. If no such P values + * exist, returns 0. + * @param addresses The addresses to check. + * @return The maximum P value. + */ + private double getMaxPredFor(Collection<Integer> addresses) { + // Make sure preds are updated once before getting. + this.ageDeliveryPreds(); + + double maxPred = 0; + for (int address : addresses) { + double predForAddress = this.preds.getOrDefault(address, 0D); + maxPred = Math.max(maxPred, predForAddress); + } + return maxPred; + } + /** * Returns the current prediction (P) value for a host or 0 if entry for * the host doesn't exist. @@ -125,15 +164,21 @@ public class ProphetRouter extends ActiveRouter { * @return the current P value */ public double getPredFor(DTNHost host) { - ageDeliveryPreds(); // make sure preds are updated before getting - if (preds.containsKey(host)) { - return preds.get(host); - } - else { - return 0; - } + return this.getPredFor(host.getAddress()); } + /** + * Returns the current prediction (P) value for a host address or 0 if entry for + * the host doesn't exist. + * @param address The host address to look the P for + * @return the current P value + */ + private double getPredFor(Integer address) { + // make sure preds are updated before getting + this.ageDeliveryPreds(); + return preds.getOrDefault(address, 0D); + } + /** * Updates transitive (A->B->C) delivery predictions. * <CODE>P(a,c) = P(a,c)_old + (1 - P(a,c)_old) * P(a,b) * P(b,c) * BETA @@ -146,11 +191,11 @@ public class ProphetRouter extends ActiveRouter { " with other routers of same type"; double pForHost = getPredFor(host); // P(a,b) - Map<DTNHost, Double> othersPreds = + Map<Integer, Double> othersPreds = ((ProphetRouter)otherRouter).getDeliveryPreds(); - for (Map.Entry<DTNHost, Double> e : othersPreds.entrySet()) { - if (e.getKey() == getHost()) { + for (Map.Entry<Integer, Double> e : othersPreds.entrySet()) { + if (e.getKey().equals(getHost().getAddress())) { continue; // don't add yourself } @@ -175,7 +220,7 @@ public class ProphetRouter extends ActiveRouter { } double mult = Math.pow(GAMMA, timeDiff); - for (Map.Entry<DTNHost, Double> e : preds.entrySet()) { + for (Map.Entry<Integer, Double> e : preds.entrySet()) { e.setValue(e.getValue()*mult); } @@ -186,7 +231,7 @@ public class ProphetRouter extends ActiveRouter { * Returns a map of this router's delivery predictions * @return a map of this router's delivery predictions */ - private Map<DTNHost, Double> getDeliveryPreds() { + private Map<Integer, Double> getDeliveryPreds() { ageDeliveryPreds(); // make sure the aging is done return this.preds; } @@ -206,6 +251,16 @@ public class ProphetRouter extends ActiveRouter { tryOtherMessages(); } + /** + * Checks whether this router has anything to send out. + * + * @return Whether or not the router has anything to send out. + */ + @Override + protected boolean hasNothingToSend() { + return DatabaseApplicationUtil.hasNoMessagesToSend(this); + } + /** * Tries to send all other messages to all connected hosts ordered by * their delivery probability @@ -219,6 +274,7 @@ public class ProphetRouter extends ActiveRouter { /* for all connected hosts collect all messages that have a higher probability of delivery by the other host */ + List<Connection> availableConnections = new ArrayList<>(); for (Connection con : getConnections()) { DTNHost other = con.getOtherNode(getHost()); ProphetRouter othRouter = (ProphetRouter)other.getRouter(); @@ -226,18 +282,23 @@ public class ProphetRouter extends ActiveRouter { if (othRouter.isTransferring()) { continue; // skip hosts that are transferring } + availableConnections.add(con); for (Message m : msgCollection) { if (othRouter.hasMessage(m.getId())) { continue; // skip messages that the other one has } - if (othRouter.getPredFor(m.getTo()) > getPredFor(m.getTo())) { + if (othRouter.getPredFor(m) > getPredFor(m)) { // the other node has higher probability of delivery messages.add(new Tuple<Message, Connection>(m,con)); } } } + /* For all available connections, add useful data messages. */ + messages.addAll(DatabaseApplicationUtil.wrapUsefulDataIntoMessages( + this, this.getHost(), availableConnections)); + if (messages.size() == 0) { return null; } @@ -248,37 +309,44 @@ public class ProphetRouter extends ActiveRouter { } /** - * Comparator for Message-Connection-Tuples that orders the tuples by - * their delivery probability by the host on the other side of the - * connection (GRTRMax) + * Comparator for Message-Connection-Tuples that orders the tuples by the utility computed by + * {@link #computeUtility(Tuple)}, higher utilities first. */ private class TupleComparator implements Comparator <Tuple<Message, Connection>> { public int compare(Tuple<Message, Connection> tuple1, Tuple<Message, Connection> tuple2) { - // delivery probability of tuple1's message with tuple1's connection - double p1 = ((ProphetRouter)tuple1.getValue(). - getOtherNode(getHost()).getRouter()).getPredFor( - tuple1.getKey().getTo()); - // -"- tuple2... - double p2 = ((ProphetRouter)tuple2.getValue(). - getOtherNode(getHost()).getRouter()).getPredFor( - tuple2.getKey().getTo()); - - // bigger probability should come first - if (p2-p1 == 0) { - /* equal probabilities -> let queue mode decide */ + double utility1 = this.computeUtility(tuple1); + double utility2 = this.computeUtility(tuple2); + + // bigger utility should come first + int utilityComparison = (-1) * Double.compare(utility1, utility2); + if (utilityComparison == 0) { + /* equal utilities -> let queue mode decide */ return compareByQueueMode(tuple1.getKey(), tuple2.getKey()); } - else if (p2-p1 < 0) { - return -1; - } - else { - return 1; - } + return utilityComparison; } - } + + /** + * Computes a utility value for a Message-Connection tuple. This is + * - either the delivery probability by the host on the other side of the connection (GRTRMax) if the message is + * not a data message, or + * - the data message's utility. + * @param tuple Tuple to compute utility for. + * @return The tuple's utility. + */ + private double computeUtility(Tuple<Message, Connection> tuple) { + Message message = tuple.getKey(); + if (message instanceof DataMessage) { + return ((DataMessage) message).getUtility(); + } + + DTNHost neighbor = tuple.getValue().getOtherNode(getHost()); + return ((ProphetRouter)neighbor.getRouter()).getPredFor(message); + } + } @Override public RoutingInfo getRoutingInfo() { @@ -287,12 +355,12 @@ public class ProphetRouter extends ActiveRouter { RoutingInfo ri = new RoutingInfo(preds.size() + " delivery prediction(s)"); - for (Map.Entry<DTNHost, Double> e : preds.entrySet()) { - DTNHost host = e.getKey(); + for (Map.Entry<Integer, Double> e : preds.entrySet()) { + Integer hostAddress = e.getKey(); Double value = e.getValue(); ri.addMoreInfo(new RoutingInfo(String.format("%s : %.6f", - host, value))); + hostAddress, value))); } top.addMoreInfo(ri); diff --git a/src/test/AbstractReportTest.java b/src/test/AbstractReportTest.java index 971f4b5a4c1d68ef7ba32b8c149dbc5af1abe997..174e3e97c5bed5237b6026c48f4ff2bedad67e58 100644 --- a/src/test/AbstractReportTest.java +++ b/src/test/AbstractReportTest.java @@ -1,5 +1,6 @@ package test; +import core.Group; import core.SimClock; import org.junit.After; import org.junit.Before; @@ -53,6 +54,16 @@ public abstract class AbstractReportTest { this.outputFile.delete(); } + @After + public void resetGroups() { + Group.clearGroups(); + } + + @After + public void resetSimClock() { + SimClock.reset(); + } + /** * Checks that the report correctly handles the warm up time as set by the {@link Report#WARMUP_S} setting. */ diff --git a/src/test/AbstractRouterTest.java b/src/test/AbstractRouterTest.java index 433d749dc6e8b3d5d80ab2ee0259e337a6a1980a..b40f47f260e537861f8244c9ad51b21940c9e73d 100644 --- a/src/test/AbstractRouterTest.java +++ b/src/test/AbstractRouterTest.java @@ -84,6 +84,7 @@ public abstract class AbstractRouterTest extends TestCase { protected void tearDown() throws Exception { super.tearDown(); Settings.init(null); + SimClock.reset(); } protected void setRouterProto(MessageRouter r) { diff --git a/src/test/ActivenessHandlerTest.java b/src/test/ActivenessHandlerTest.java index 49af953f70008dbb1e4ec9a687ba9d8234be35b9..9ce05176e02839e819ed295ff7a3dfe6be8a9773 100644 --- a/src/test/ActivenessHandlerTest.java +++ b/src/test/ActivenessHandlerTest.java @@ -24,6 +24,11 @@ public class ActivenessHandlerTest extends TestCase { moreTimes); ah = new ActivenessHandler(ts); clock = SimClock.getInstance(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); SimClock.reset(); } diff --git a/src/test/ConnectionTest.java b/src/test/ConnectionTest.java index dfdc68a53f9fb633da8bb490af40e1cc33c6d35e..8faeb692be84db9e0b5864a54bb1faa69de1edb9 100644 --- a/src/test/ConnectionTest.java +++ b/src/test/ConnectionTest.java @@ -32,7 +32,6 @@ public class ConnectionTest extends TestCase { protected void setUp() throws Exception { super.setUp(); - SimClock.reset(); clock.setTime(START_TIME); TestSettings testSettings = new TestSettings(); testSettings.setNameSpace(TestUtils.IFACE_NS); @@ -65,6 +64,12 @@ public class ConnectionTest extends TestCase { conCount = 3; } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + SimClock.reset(); + } + private void con(DTNHost from, DTNHost to) { c[index] = new CBRConnection(from, from.getInterfaces().get(0), to, to.getInterfaces().get(0), speed[index]); index++; diff --git a/src/test/ContactTimesReportTest.java b/src/test/ContactTimesReportTest.java index 2e708b8766a861e6418c4dd3671075f498de5115..a757c65e91a51360f951b32e2b1c49a72a3c069e 100644 --- a/src/test/ContactTimesReportTest.java +++ b/src/test/ContactTimesReportTest.java @@ -72,6 +72,12 @@ public class ContactTimesReportTest extends TestCase { } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + SimClock.reset(); + } + private void generateConnections(TestUtils utils) { Coord c1 = new Coord(0,0); Coord c2 = new Coord(1,0); diff --git a/src/test/DistanceDelayReportTest.java b/src/test/DistanceDelayReportTest.java index 614add0e9311b8ed889a75d1e781c28161e94e4f..432b124d6366311a02404c145b9f0a9e9d88dc09 100644 --- a/src/test/DistanceDelayReportTest.java +++ b/src/test/DistanceDelayReportTest.java @@ -44,6 +44,12 @@ public class DistanceDelayReportTest extends TestCase { this.utils = new TestUtils(null, ml, ts); } + @Override + protected void tearDown() throws Exception{ + super.tearDown(); + SimClock.reset(); + } + @Test public void testMessageTransferred() throws IOException { DTNHost h1 = utils.createHost(new Coord(0,0)); diff --git a/src/test/ExternalMovementTest.java b/src/test/ExternalMovementTest.java index 1cdc4509da2f8e76677abd11be341dc0f67f7f7d..7e8f83c4b1aac6d1a8148a98c672571daf854db8 100644 --- a/src/test/ExternalMovementTest.java +++ b/src/test/ExternalMovementTest.java @@ -79,6 +79,12 @@ public class ExternalMovementTest extends TestCase { clock.setTime(0); } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + SimClock.reset(); + } + public void testMovement() throws Exception { setUpUsing(INPUT); diff --git a/src/test/GroupTest.java b/src/test/GroupTest.java index 8069545a493d46fff074f08bb557e30418320ae5..bae1212c0dc92b6eed1472e7a029f58d707ac310 100644 --- a/src/test/GroupTest.java +++ b/src/test/GroupTest.java @@ -5,7 +5,7 @@ import core.DTNHost; import core.Group; import core.MessageListener; import junit.framework.TestCase; -import org.junit.Before; +import org.junit.After; import org.junit.Test; import java.util.ArrayList; @@ -21,8 +21,8 @@ public class GroupTest { private TestUtils utils = new TestUtils(new ArrayList<ConnectionListener>(), new ArrayList<MessageListener>(), new TestSettings()); - @Before - public void setUp(){ + @After + public void tearDown(){ Group.clearGroups(); DTNHost.reset(); } diff --git a/src/test/ImmediateMessageDelayReportTest.java b/src/test/ImmediateMessageDelayReportTest.java index b1c19e50c5ad1da9a0ee8347c2b42506fb6d1bdb..54a1f466ae9535469d031c256f1d1d9e73b32c42 100644 --- a/src/test/ImmediateMessageDelayReportTest.java +++ b/src/test/ImmediateMessageDelayReportTest.java @@ -86,11 +86,12 @@ public class ImmediateMessageDelayReportTest extends AbstractReportTest { } /** - * Resets the clock to 0. + * Resets the clock to 0 and clears all groups. */ @After - public void resetClock() { + public void tearDown() { this.clock.setTime(0); + Group.clearGroups(); } /*** diff --git a/src/test/MessageRouterTest.java b/src/test/MessageRouterTest.java index 02fc9080dc7553f48eb3e0c88f88286e8639b83d..dc4119ad48ebe60ccd84cdb5ab162504b814846d 100644 --- a/src/test/MessageRouterTest.java +++ b/src/test/MessageRouterTest.java @@ -8,6 +8,7 @@ import core.Group; import core.Message; import core.MessageListener; import core.MulticastMessage; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -47,7 +48,6 @@ public class MessageRouterTest { @Before public void setUp() { - Group.clearGroups(); TestSettings ts = new TestSettings(); ts.putSetting(MessageRouter.B_SIZE_S, ""+BUFFER_SIZE); List<MessageListener> mListener = new ArrayList<>(1); @@ -69,6 +69,11 @@ public class MessageRouterTest { this.multicast = new MulticastMessage(sender, g,"G",DEFAULT_MESSAGE_SIZE); } + @After + public void tearDown() { + Group.clearGroups(); + } + @Test public void testMessageTransferredForNonRecipientPutsMessageIntoBufferButNotIntoDelivered() { DTNHost nonRecipient = this.utils.createHost(); diff --git a/src/test/MessageStatsReportTest.java b/src/test/MessageStatsReportTest.java index aeee40aab359355cbf3b302186e2c340e652ef8e..967ae4a62851f80e36a41df4b3995b7581d415c7 100644 --- a/src/test/MessageStatsReportTest.java +++ b/src/test/MessageStatsReportTest.java @@ -41,7 +41,6 @@ public class MessageStatsReportTest { public void init() throws IOException{ SimScenario.reset(); DTNHost.reset(); - Group.clearGroups(); Settings.init(null); java.util.Locale.setDefault(java.util.Locale.US); @@ -68,6 +67,7 @@ public class MessageStatsReportTest { // Cleanup to make further tests with other settings possible. SimScenario.reset(); DTNHost.reset(); + Group.clearGroups(); } private void playScenario() { diff --git a/src/test/MessageTest.java b/src/test/MessageTest.java index d3a6c003b4b71875c056675ed436c41593eadac3..eb3294ee727ce6069c2805be29b49a964150f7c2 100644 --- a/src/test/MessageTest.java +++ b/src/test/MessageTest.java @@ -48,6 +48,12 @@ public class MessageTest extends TestCase { msg.setTtl(10); } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + SimClock.reset(); + } + @Test public void testGetTtl() { assertEquals(10, msg.getTtl()); diff --git a/src/test/MessageTransferAcceptPolicyTest.java b/src/test/MessageTransferAcceptPolicyTest.java index 27b8f6ebcf5fc20b8f071b1adb2c0d613f10e963..530004b103f75b4d4104f1fb7637ede36da69582 100644 --- a/src/test/MessageTransferAcceptPolicyTest.java +++ b/src/test/MessageTransferAcceptPolicyTest.java @@ -8,6 +8,7 @@ import core.DisasterData; import core.Group; import core.Message; import core.MulticastMessage; +import org.junit.After; import org.junit.Before; import org.junit.Test; import routing.util.MessageTransferAcceptPolicy; @@ -53,7 +54,6 @@ public class MessageTransferAcceptPolicyTest { @Before public void init() { - Group.clearGroups(); DTNHost.reset(); this.settings = new TestSettings(); TestUtils utils = new TestUtils( @@ -83,6 +83,11 @@ public class MessageTransferAcceptPolicyTest { this.setAcceptableRecipientAddress(this.recipient.getAddress()); } + @After + public void tearDown() { + Group.clearGroups(); + } + @Test public void testAcceptSendingReturnsTrueForMessageAdheringToSimplePolicies() { MessageTransferAcceptPolicy policy = new MessageTransferAcceptPolicy(this.settings); diff --git a/src/test/MulticastCreateEventTest.java b/src/test/MulticastCreateEventTest.java index 8bd37f1f4484b591791cb083f14ccc6651ebda72..05ced8a3f95ba52745e1fe2a5f43d50997a40a7d 100644 --- a/src/test/MulticastCreateEventTest.java +++ b/src/test/MulticastCreateEventTest.java @@ -6,6 +6,7 @@ import core.Message; import core.MulticastMessage; import input.ExternalEvent; import input.MulticastCreateEvent; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -20,11 +21,15 @@ public class MulticastCreateEventTest extends AbstractMessageCreateEventTest { @Before public void createGroup(){ - Group.clearGroups(); Group g = Group.createGroup(0); g.addHost(creator); } + @After + public void tearDown() { + Group.clearGroups(); + } + @Test public void testProcessEventCreatesMulticastMessageWithCorrectGroupAddress() { int groupAddress = 0; diff --git a/src/test/MulticastDeliveryReportTest.java b/src/test/MulticastDeliveryReportTest.java index d886350751e88ee2b895721217ae7970ddbc637c..79770af5ca239072092460e3cc6bc865b509402f 100644 --- a/src/test/MulticastDeliveryReportTest.java +++ b/src/test/MulticastDeliveryReportTest.java @@ -68,8 +68,6 @@ public class MulticastDeliveryReportTest extends AbstractReportTest { // Let base do the basic report setup. super.setUp(); - Group.clearGroups(); - // Set clock to 0. this.clock.setTime(0); @@ -84,8 +82,9 @@ public class MulticastDeliveryReportTest extends AbstractReportTest { } @After - public void resetClock(){ + public void tearDown(){ clock.setTime(0); + Group.clearGroups(); } @Test diff --git a/src/test/MulticastEventGeneratorTest.java b/src/test/MulticastEventGeneratorTest.java index 9b4998f1d028745c5ef89b751a0ae5f536c29e51..b8180577eb7bd2b2e8822cdd85c50a2e1243035b 100644 --- a/src/test/MulticastEventGeneratorTest.java +++ b/src/test/MulticastEventGeneratorTest.java @@ -8,6 +8,7 @@ import core.SimScenario; import input.AbstractMessageEventGenerator; import input.MulticastCreateEvent; import input.MulticastEventGenerator; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -34,7 +35,6 @@ public class MulticastEventGeneratorTest extends AbstractMessageEventGeneratorTe public void init(){ super.init(); SimScenario.reset(); - Group.clearGroups(); DTNHost.reset(); TestSettings.addSettingsToEnableSimScenario(this.settings); @@ -52,6 +52,11 @@ public class MulticastEventGeneratorTest extends AbstractMessageEventGeneratorTe settings.putSetting(MulticastEventGenerator.GROUP_SIZE_RANGE_S,MIN_GROUP_SIZE+", "+MAX_GROUP_SIZE); } + @After + public void tearDown() { + Group.clearGroups(); + } + @Test public void testNextEventCreatesMulticastMessages() { AbstractMessageEventGenerator generator = new MulticastEventGenerator(this.settings); diff --git a/src/test/MulticastMessageTest.java b/src/test/MulticastMessageTest.java index 097a7f668d293f6df5ed9a432ce6118ca5c5cf95..9fc108c399d32fbe0254c608552d2f3335a8e809 100644 --- a/src/test/MulticastMessageTest.java +++ b/src/test/MulticastMessageTest.java @@ -7,6 +7,7 @@ import core.Message; import core.MessageListener; import core.MulticastMessage; import core.SimError; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -48,13 +49,17 @@ public class MulticastMessageTest { @Before public void setUp() { - Group.clearGroups(); this.group1 = Group.createGroup(GROUP_ADDRESS_1); this.group1.addHost(from); msg = new MulticastMessage(from, this.group1, "M", MESSAGE_SIZE); msgPrio = new MulticastMessage(from, this.group1, "N", MESSAGE_SIZE, PRIORITY); } + @After + public void tearDown() { + Group.clearGroups(); + } + @Test(expected = java.lang.UnsupportedOperationException.class) public void testGetToThrowsException() { this.msg.getTo(); diff --git a/src/test/ProphetRouterTest.java b/src/test/ProphetRouterTest.java index bc251e5c52da4d619f533560b4cb6c95802d2a68..66699483a4f0e4d0a6e8cf688217cd68510333d9 100644 --- a/src/test/ProphetRouterTest.java +++ b/src/test/ProphetRouterTest.java @@ -4,11 +4,23 @@ */ package test; +import applications.DatabaseApplication; +import core.BroadcastMessage; +import core.Coord; +import core.DTNHost; +import core.DisasterData; +import core.Group; +import core.MulticastMessage; +import core.SimClock; +import org.junit.Assert; import org.junit.Test; import routing.MessageRouter; import routing.ProphetRouter; import core.Message; +import java.util.Arrays; +import java.util.HashSet; + /** * Tests for PRoPHET router */ @@ -16,16 +28,33 @@ public class ProphetRouterTest extends AbstractRouterTest { private static int SECONDS_IN_TIME_UNIT = 60; + private static final double ONE_HOUR = 60 * 60D; + private static final double ONE_DAY = 24 * ONE_HOUR; + private static final double ONE_WEEK = 7 * ONE_DAY; + + private static final Coord FAR_AWAY_LOCATION = new Coord(30_000, 40_000); + @Override public void setUp() throws Exception { ts.putSetting(MessageRouter.B_SIZE_S, ""+BUFFER_SIZE); ts.putSetting(ProphetRouter.PROPHET_NS + "." + ProphetRouter.SECONDS_IN_UNIT_S , SECONDS_IN_TIME_UNIT+""); setRouterProto(new ProphetRouter(ts)); + DatabaseApplicationTest.addDatabaseApplicationSettings(ts); super.setUp(); } - /** + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + */ + @Override + protected void tearDown() throws Exception { + super.tearDown(); + Group.clearGroups(); + } + + /** * Tests normal routing */ @Test @@ -122,6 +151,173 @@ public class ProphetRouterTest extends AbstractRouterTest { } + /** + * Checks that {@link ProphetRouter} correctly sorts direct messages, broadcasts, non-direct one-to-one messages, + * direct and non-direct multicasts, and data messages. + */ + public void testPrioritization() { + // Create groups for multicasts. + Group directGroup = ProphetRouterTest.createGroup(0, this.h1, this.h2); + Group indirectGroup = ProphetRouterTest.createGroup(1, this.h1, this.h3, this.h4); + + // Create all kinds of messages. + Message broadcast = new BroadcastMessage(this.h1, "B1", 0); + Message directMessage = new Message(this.h1, this.h2, "M1", 0); + Message nonDirectMessage = new Message(this.h1, this.h3, "M2", 0); + Message nonDirectMessage2 = new Message(this.h1, this.h5, "M3", 0); + Message directMulticast = new MulticastMessage(this.h1, directGroup, "m1", 0); + Message indirectMulticast = new MulticastMessage(this.h1, indirectGroup, "m2", 0); + this.h1.createNewMessage(broadcast); + this.h1.createNewMessage(directMessage); + this.h1.createNewMessage(nonDirectMessage); + this.h1.createNewMessage(nonDirectMessage2); + this.h1.createNewMessage(directMulticast); + this.h1.createNewMessage(indirectMulticast); + + // Add data for data message. + this.clock.advance(ONE_DAY); + DisasterData data = + new DisasterData(DisasterData.DataType.MARKER, 0, 0, this.h1.getLocation()); + DatabaseApplication app = this.createDatabaseApplicationFor(this.h1); + app.disasterDataCreated(h1, data); + + // Modify utilities for testing: + // First, increase delivery predictability H2 --> H5, then advance clock to lower it again. + this.h2.connect(this.h5); + this.clock.advance(ONE_HOUR); + // Then, do the same for delivery predictability H2 --> H4. + this.h2.connect(this.h4); + this.clock.advance(ONE_HOUR); + // Finally, make sure the delivery predictability H2 --> H3 is high + this.h2.connect(this.h3); + disconnect(this.h2); + // And ensure that the multicast message was already delivered to H3. + ProphetRouterTest.addNodeOnPath(this.h1, indirectMulticast.getId(), this.h3); + + // Connect h1 to h2. + this.h1.connect(this.h2); + + // Check order of messages: First direct messages, then sorted by predictability / data utility + // Direct messages are pseudo-randomly sorted, so you might have to change the order if changing the clock. + String dataMessageId = "D" + Arrays.asList(data).hashCode(); + String[] idsInExpectedOrder = { + broadcast.getId(), directMessage.getId(), directMulticast.getId(), + nonDirectMessage.getId(), dataMessageId, indirectMulticast.getId(), nonDirectMessage2.getId() + }; + this.checkMessagesAreSentInOrder(idsInExpectedOrder); + } + + /** + * Checks that {@link ProphetRouter} sends {@link core.DataMessage}s wrapping useful data, but data with too low + * utility is not sent. + */ + public void testUsefulDataGetsExchanged() { + // Add data. + DatabaseApplication app = this.createDatabaseApplicationFor(this.h1); + this.clock.advance(ONE_WEEK); + this.h1.setLocation(FAR_AWAY_LOCATION); + DisasterData uselessData = new DisasterData(DisasterData.DataType.MARKER, 0, 0, new Coord(0, 0)); + DisasterData usefulData = + new DisasterData(DisasterData.DataType.MARKER, 0, SimClock.getTime(), this.h1.getLocation()); + app.disasterDataCreated(this.h1, uselessData); + app.disasterDataCreated(this.h1, usefulData); + + // Check that H1 only sends the useful data item to H2. + this.h1.connect(this.h2); + this.mc.reset(); + h1.update(false); + do { + this.mc.next(); + } while (!this.mc.TYPE_START.equals(this.mc.getLastType())); + Assert.assertEquals( + "Expected the useful data item to be sent.", + "D" + Arrays.asList(usefulData).hashCode(), mc.getLastMsg().getId()); + Assert.assertFalse("Did not expect any additional message.", this.mc.next()); + } + + /** + * Checks that both one-to-one messages and multicast messages are only transferred to a neighbor if those messages + * have higher delivery predictability at the neighbor than at the current host. + */ + public void testMessagesAreOnlyTransferredToHostsWithHigherPredictability() { + // Make sure H1 has higher delivery predictability for H3, while H2 has higher delivery predictability for H4. + // First, increase delivery predictability H1 --> H4, then advance clock to lower it again. + this.h1.connect(this.h4); + this.clock.advance(ONE_HOUR); + // Then set high delivery predictabilities for H1 --> H3 and H2 --> H4. + this.h1.connect(this.h3); + this.h2.connect(this.h4); + disconnect(this.h1); + disconnect(this.h2); + + // Create group including both H3 and H4. + Group group = ProphetRouterTest.createGroup(0, this.h1, this.h3, this.h4); + + // Create multicasts and one to one messages to H3 and H4. + Message messageToH3 = new Message(this.h1, this.h3, "M1", 0); + Message messageToH4 = new Message(this.h1, this.h4, "M2", 0); + Message receivedMulticast = new MulticastMessage(this.h1, group, "m1", 0); + Message newMulticast = new MulticastMessage(this.h1, group, "m2", 0); + this.h1.createNewMessage(messageToH3); + this.h1.createNewMessage(messageToH4); + this.h1.createNewMessage(receivedMulticast); + this.h1.createNewMessage(newMulticast); + + // Ensure that one of the multicast messages was already delivered to H4. + ProphetRouterTest.addNodeOnPath(this.h1, receivedMulticast.getId(), this.h4); + + // Connect h1 to h2. + this.h1.connect(this.h2); + + // Check that only half of the messages are transferred + HashSet<String> sentIds = new HashSet<>(); + this.mc.reset(); + this.h1.update(true); + while (this.mc.next()) { + sentIds.add(this.mc.getLastMsg().getId()); + this.h1.update(true); + } + HashSet<String> expectedIds = new HashSet<>(2); + expectedIds.add(messageToH4.getId()); + expectedIds.add(newMulticast.getId()); + assertEquals("Expected different message set to be sent.", sentIds, expectedIds); + } + + /** + * Checks that a host does not send out new messages to hosts which are already transferring. + */ + public void testNoMessagesAreReceivedWhenAlreadyTransferring() { + // Let h2 be transferring. + this.h2.connect(this.h3); + Message m1 = new Message(this.h2, this.h3, "M1", 1); + this.h2.createNewMessage(m1); + this.updateAllNodes(); + + // Check the transfer started. + this.mc.next(); + this.checkTransferStart(this.h2, this.h3, m1.getId()); + + // Let h1 try to send a message to h2 now. + Message m2 = new Message(this.h1, this.h2, "M2", 0); + this.h1.createNewMessage(m2); + this.h1.connect(this.h2); + this.updateAllNodes(); + + // Check that the new message was not sent. + while(this.mc.next()) { + Assert.assertNotEquals("Did not expect another transfer.", this.mc.TYPE_START, this.mc.getLastType()); + } + + // Finally, check that the original message will still be transferred. + this.clock.advance(1); + this.updateAllNodes(); + this.mc.next(); + Assert.assertEquals( + "Original message should have been processed next.", m1.getId(), this.mc.getLastMsg().getId()); + Assert.assertEquals( + "Original message should have been transferred.", this.mc.TYPE_RELAY, this.mc.getLastType()); + } + private void doRelay() { clock.advance(10); updateAllNodes(); @@ -153,4 +349,60 @@ public class ProphetRouterTest extends AbstractRouterTest { assertEquals(newPred, r5.getPredFor(h4)); } + /** + * Observes which messages are sent by {@link #h1} and checks they match the provided IDs. + * @param idsInExpectedOrder The expected message IDs in expected order. + */ + private void checkMessagesAreSentInOrder(String[] idsInExpectedOrder) { + this.mc.reset(); + for (String expectedId : idsInExpectedOrder) { + this.h1.update(true); + do { + this.mc.next(); + } while (!this.mc.TYPE_START.equals(this.mc.getLastType())); + Assert.assertEquals("Expected different message.", expectedId, mc.getLastMsg().getId()); + } + } + + /** + * Creates a group using the provided number as address. + * All provided hosts will be part of the group. + * @param address The new group's address. + * @param hosts The hosts in the new group. + * @return The created group. + */ + private static Group createGroup(int address, DTNHost... hosts) { + Group group = Group.createGroup(address); + for (DTNHost host : hosts) { + group.addHost(host); + } + return group; + } + + /** + * Creates and initializes a {@link DatabaseApplication} for the provided {@link DTNHost}. + * @param host Host to create the application for. + * @return The created application. + */ + private DatabaseApplication createDatabaseApplicationFor(DTNHost host) { + DatabaseApplication app = new DatabaseApplication(this.ts); + host.getRouter().addApplication(app); + app.update(host); + return app; + } + + /** + * Changes the meta data of the message with provided ID in the buffer of the provided host by adding the provided + * node to path. + * @param bufferToChange The host in which buffer the message should be changed. + * @param messageId The message's ID. + * @param node The host to add to path. + */ + private static void addNodeOnPath(DTNHost bufferToChange, String messageId, DTNHost node) { + for (Message m : bufferToChange.getMessageCollection()) { + if (m.getId().equals(messageId)) { + m.addNodeOnPath(node); + } + } + } } diff --git a/src/test/ScheduledUpdatesQueueTest.java b/src/test/ScheduledUpdatesQueueTest.java index 43c11e609b236e5f9054d80d65c0989f44320b2a..08be166f122268a372f5dd1af6a7c8eec8260138 100644 --- a/src/test/ScheduledUpdatesQueueTest.java +++ b/src/test/ScheduledUpdatesQueueTest.java @@ -18,10 +18,15 @@ public class ScheduledUpdatesQueueTest extends TestCase { protected void setUp() throws Exception { super.setUp(); - SimClock.reset(); suq = new ScheduledUpdatesQueue(); } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + SimClock.reset(); + } + public void testUpdates() { assertEquals(MAX, suq.nextEventsTime()); suq.addUpdate(1); diff --git a/src/test/TotalContactTimeReportTest.java b/src/test/TotalContactTimeReportTest.java index 9cfe62952ce8c6621e2385eb604f7041493d7d54..eeb4287eb9d74affb2864ba364898c8859508b36 100644 --- a/src/test/TotalContactTimeReportTest.java +++ b/src/test/TotalContactTimeReportTest.java @@ -36,7 +36,6 @@ public class TotalContactTimeReportTest extends TestCase { protected void setUp() throws Exception { java.util.Locale.setDefault(java.util.Locale.US); super.setUp(); - SimClock.reset(); outFile = File.createTempFile("cttest", ".tmp"); outFile.deleteOnExit(); @@ -61,6 +60,12 @@ public class TotalContactTimeReportTest extends TestCase { h3 = utils.createHost(c3); } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + SimClock.reset(); + } + private void done() throws Exception { ctr.done(); ctReader = new BufferedReader(new FileReader(outFile)); diff --git a/src/test/VhmBehaviorTest.java b/src/test/VhmBehaviorTest.java index 63993b3fcc95d2ccae3d7f577707dd04068006b5..85072b38128d6c951ba41f2d92b2f5db856dfe86 100644 --- a/src/test/VhmBehaviorTest.java +++ b/src/test/VhmBehaviorTest.java @@ -9,6 +9,7 @@ import movement.CarMovement; import movement.PanicMovement; import movement.ShortestPathMapBasedMovement; import movement.VoluntaryHelperMovement; +import org.junit.After; import org.junit.Test; import java.util.ArrayList; @@ -43,6 +44,11 @@ public class VhmBehaviorTest { vhm = VhmTestHelper.createMinimalVhm(testSettings,host); } + @After + public void tearDown() { + SimClock.reset(); + } + @Test public void testSetHostSetsHost(){ assertEquals("Host was not set as expected",host,vhm.getHost()); diff --git a/src/test/WorldTest.java b/src/test/WorldTest.java index a70de35d9cc922d3e9d95e732f7e9a28209eb0a3..1eab16bd202c8b33475fed95df026b9d3da56da1 100644 --- a/src/test/WorldTest.java +++ b/src/test/WorldTest.java @@ -35,7 +35,6 @@ public class WorldTest extends TestCase { protected void setUp() throws Exception { super.setUp(); - SimClock.reset(); TestSettings testSettings = new TestSettings(); testSettings.setNameSpace(TestUtils.IFACE_NS); testSettings.putSetting(NetworkInterface.TRANSMIT_RANGE_S, "1.0"); @@ -59,6 +58,12 @@ public class WorldTest extends TestCase { ts.getExternalEvents() ); } + @Override + protected void tearDown() throws Exception { + super.tearDown(); + SimClock.reset(); + } + public void testUpdate() { double endTime = 1000; int nrofRounds = (int)(endTime/upInterval);