content: /*
   * Copyright 2010 Aalto University, ComNet
   * Released under GPLv3. See LICENSE.txt for details.
   */
  package test;
  
  import routing.EpidemicRouter;
  import routing.MessageRouter;
  import core.DTNHost;
  import core.Message;
  
  /**
   * Tests for EpidemicRouter and, due the simple nature of Epidemic router,
   * also ActiveRouter in general.
   */
  public class EpidemicRouterTest extends AbstractRouterTest {
  
  	private static int TTL = 300;
  
  	@Override
  	public void setUp() throws Exception {
  		ts.putSetting(MessageRouter.MSG_TTL_S, ""+TTL);
  		ts.putSetting(MessageRouter.B_SIZE_S, ""+BUFFER_SIZE);
  		setRouterProto(new EpidemicRouter(ts));
  		super.setUp();
  	}
  
  	/**
  	 * Tests routing messages between three hosts
  	 */
  	public void testRouter() {
  		// nothing should have happened so far
  		assertEquals(mc.TYPE_NONE, mc.getLastType());
  
  		Message m1 = new Message(h1, h3, msgId1, 1);
  		h1.createNewMessage(m1);
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_CREATE, mc.getLastType());
  		assertEquals(mc.getLastFrom(), h1);
  		assertEquals(mc.getLastTo(), h3);
  
  		// connect h1-h2-h3
  		h1.connect(h2);
  		h2.connect(h3);
  
  		updateAllNodes();
  		clock.advance(2);
  		updateAllNodes();
  
  		// should cause relay h1 -> h2
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertFalse(mc.getLastFirstDelivery());
  		assertEquals(mc.getLastFrom(), h1);
  		assertEquals(mc.getLastTo(), h2);
  
  		// should cause relay h2 -> h3
  		clock.advance(1);
  		updateAllNodes();
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertTrue(mc.getLastFirstDelivery());
  		assertEquals(mc.getLastFrom(), h2);
  		assertEquals(mc.getLastTo(), h3);
  		// message delivered to recipient
  		assertFalse(mc.next());
  
  
  		// disconnect all nodes and try to other direction
  		disconnect(h2);
  		// create message while not connected
  		Message m2 = new Message(h3,h1, msgId2, 1);
  		h3.createNewMessage(m2);
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_CREATE, mc.getLastType());
  
  		h1.connect(h2);	// reconnect h1-h2
  		clock.advance(10);
  		updateAllNodes();
  		assertFalse(mc.next()); // nothing should have happened to messages
  
  		h2.connect(h3);
  		updateAllNodes(); // now h3 should start forwarding msg to h2
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertEquals(h2, mc.getLastTo());
  		assertEquals(h3, mc.getLastFrom());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  		assertFalse(mc.next());
  
  		clock.advance(10);
  		updateAllNodes();	// forwarding msg2 h3->h2
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h2, mc.getLastTo());
  		assertEquals(h3, mc.getLastFrom());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  		assertFalse(mc.next());
  
  		updateAllNodes();	// forwarding msg2 h2->h1
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertFalse(mc.next());
  
  		clock.advance(10);
  		updateAllNodes();	// forwarding msg2 h2->h1
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h1, mc.getLastTo());
  		assertEquals(h2, mc.getLastFrom());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  		assertTrue(mc.getLastFirstDelivery()); // message delivered to recipient
  		assertFalse(mc.next());
  	}
  
  	/**
  	 * Checks that delivering many messages in a row works
  	 */
  	public void testManyMessages() {
  
  		Message m1 = new Message(h1,h2, msgId1, 1);
  		h1.createNewMessage(m1);
  		Message m2 = new Message(h1,h2, msgId2, 1);
  		h1.createNewMessage(m2);
  		mc.reset();
  
  		h1.connect(h2);
  		updateAllNodes(); // h1 should start transfer
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertFalse(mc.next());
  
  		clock.advance(10);
  		updateAllNodes();
  
  		/* h1 should have delivered the msg & start next transfer */
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h2, mc.getLastTo());
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertFalse(mc.next());	// nothing more happened so far
  
  		clock.advance(10);
  		updateAllNodes(); // h1 should finish relaying the msg
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h2, mc.getLastTo());
  
  		assertFalse(mc.next());
  	}
  
  	/**
  	 * Tests that messages that can be delivered right a way are delivered first
  	 */
  	public void testDeliverableMessageExchange() {
  		Message m1 = new Message(h1,h3, "Dummy1", 1);
  		h1.createNewMessage(m1);
  		Message m2 = new Message(h1,h3, "A_Dummy2", 1);
  		h1.createNewMessage(m2);
  		Message m3 = new Message(h1,h2, msgId1, 1);
  		h1.createNewMessage(m3);
  
  		Message m4 = new Message(h2,h3, "Dummy3", 1);
  		h2.createNewMessage(m4);
  		Message m5 = new Message(h2,h1, msgId2, 1);
  		h2.createNewMessage(m5);
  		Message m6 = new Message(h2,h3, "Dummy4", 1);
  		h2.createNewMessage(m6);
  
  		checkCreates(6);
  
  		h1.connect(h2);
  		updateAllNodes();
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType()); // starts h1->h2 msgId1
  		assertEquals(h2, mc.getLastTo());
  		assertEquals(msgId1, mc.getLastMsg().getId());
  
  		clock.advance(10);
  		updateAllNodes();
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType()); // finished delivery
  
  		// should also start delivery of msgId2 from h2 to h1
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  		assertEquals(h1, mc.getLastTo());
  
  	}
  
  	/**
  	 * Tests aborting transfer when connections is disconnected during the
  	 * transfer
  	 */
  	public void testMessageRelayAbort() {
  		Message m1 = new Message(h1,h2, msgId1, BUFFER_SIZE);
  		h1.createNewMessage(m1);
  		checkCreates(1);
  
  		h1.connect(h2);
  		updateAllNodes();
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertFalse(mc.next());
  		clock.advance(1);
  		updateAllNodes();
  		assertFalse(mc.next());
  
  		h2.setLocation(farAway);
  		updateAllNodes(); // disconnect, still transferring
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_ABORT, mc.getLastType());
  		assertEquals(h1, mc.getLastFrom());
  		assertFalse(mc.next());
  	}
  
  	/**
  	 * try disconnecting on the same update interval when a transfer should
  	 * be finished -> should not cause abort (anymore)
  	 */
  	public void testAbortWhenReady() {
  		Message m1 = new Message(h2, h1, msgId2, 1);
  		h2.createNewMessage(m1);
  		checkCreates(1);
  
  		h2.connect(h1);
  		updateAllNodes(); // should start transfer
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertFalse(mc.next());
  
  		clock.advance(10);
  		h2.setLocation(farAway);
  		// transfer should have been finished even if nodes disconnected
  		updateAllNodes();
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h2, mc.getLastFrom());
  		assertFalse(mc.next());
  	}
  
  	/**
  	 * Test unexpected ordering of finalizations and message transfers.
  	 */
  	public void testDifferentOrdering() {
  		h1.connect(h2);
  		Message m1 = new Message(h1,h2, msgId1, 1);
  		h1.createNewMessage(m1);
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_CREATE, mc.getLastType());
  		updateAllNodes();
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertEquals(mc.getLastFrom(),h1);
  		assertEquals(mc.getLastTo(),h2);
  
  		clock.advance(10);
  		// h1 has transferred msg but not finalized transfer when h2 starts
  		Message m2 = new Message(h2,h1, msgId2, 1);
  		h2.createNewMessage(m2);
  		h2.update(true); // h2 and h1 are connected but this shouldn't start relay
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_CREATE, mc.getLastType());
  		assertFalse(mc.next()); // shouldn't start transfer (prev not finalized)
  
  		h1.update(true); // finalize the transfer of MSG2
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h2, mc.getLastTo());
  
  		// last transfer is finalized -> update should start transfer of msgId2
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertEquals(h1, mc.getLastTo());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  
  		assertFalse(mc.next());
  	}
  
  	/**
  	 * Tests if rejecting already delivered message(s) work
  	 */
  	public void testDoubleDelivery() {
  		Message m1 = new Message(h1,h2, msgId1, 1);
  		h1.createNewMessage(m1);
  
  		h1.connect(h2);
  		updateAllNodes(); // starts transfer h1 -> h2
  		clock.advance(10);
  		mc.reset();	// discard create & start
  		updateAllNodes(); // msg delivered h1 -> h2
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h2, mc.getLastTo());
  
  		h1.connect(h3);
  		updateAllNodes();	// start transfer h1 -> h3
  		assertTrue(mc.next());
  		clock.advance(10);
  		updateAllNodes(); // msg transferred h1 -> h3
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h3, mc.getLastTo());
  
  		h3.connect(h2);
  		updateAllNodes(); // should not start a new transfer (h1 -> h2)
  		clock.advance(10);
  		updateAllNodes(); // still shouldn't do anything
  
  		assertFalse(mc.next());
  	}
  
  	/**
  	 * Tests if the FIFO queue management works
  	 */
  	public void testQueueManagement() {
  		Message m1 = new Message(h1,h3, "dummy", BUFFER_SIZE-1);
  		h1.createNewMessage(m1);
  		assertEquals(1, h1.getNrofMessages());
  		Message m2 = new Message(h1,h3, msgId1, BUFFER_SIZE/3);
  		h1.createNewMessage(m2);
  		assertEquals(1, h1.getNrofMessages()); // message should replace dummy
  		assertEquals(msgId1, h1.getMessageCollection().iterator().next().getId());
  
  		mc.reset();
  
  		clock.advance(10);
  		Message m3 = new Message(h1,h3, msgId2, BUFFER_SIZE/3);
  		h1.createNewMessage(m3);
  		clock.advance(10);
  		Message m4 = new Message(h1,h3, "newestMsg", BUFFER_SIZE/3);
  		h1.createNewMessage(m4);
  
  		clock.advance(10);
  		Message m5 = new Message(h2,h3, "MSG_from_h2", BUFFER_SIZE/2);
  		h2.createNewMessage(m5);
  
  		checkCreates(3); // remove 3 creates from mc
  
  		h2.connect(h1);	// h2 starts transfer of message -> h1 makes room
  		h2.update(true);
  		// h1 should drop first msgId1 and then msgId2 (older first)
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_DELETE, mc.getLastType());
  		assertEquals(h1, mc.getLastFrom());
  		assertEquals(msgId1, mc.getLastMsg().getId());
  		assertTrue(mc.getLastDropped());
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_DELETE, mc.getLastType());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  
  		assertEquals(1, h1.getNrofMessages());
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType()); // h2 should start
  		assertEquals(h2, mc.getLastFrom());
  		assertFalse(mc.next());
  
  		clock.advance(10);
  		updateAllNodes(); // h2 should finish transfer
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(h1, mc.getLastTo());
  		assertFalse(mc.next());
  	}
  
  	/**
  	 * Tests creating a new message when the message buffer is full and the
  	 * message that should be removed is the message being sent
  	 */
  	public void testNewMessageToFullBufferWhileTransferring() {
  		int m3Size = BUFFER_SIZE-1;
  		int m1Size = BUFFER_SIZE/2;
  
  		Message m1 = new Message(h1,h3, msgId1, m1Size);
  		h1.createNewMessage(m1);
  		Message m2 = new Message(h1,h4, msgId2, BUFFER_SIZE/2);
  		h1.createNewMessage(m2);
  		checkCreates(2);
  
  		h3.connect(h1);
  		updateAllNodes(); // transfer of msgId1 should start
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		clock.advance(1);
  		updateAllNodes(); // should still be transferring
  		assertFalse(mc.next());
  
  		// creating a new message should cause dropping the msgId2 but
  		// not msgId1 (which is being transferred) -> buffer should become
  		// "over full"
  		Message m3 = new Message(h1,h4, msgId3, m3Size);
  		h1.createNewMessage(m3);
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_DELETE, mc.getLastType());
  		assertTrue(mc.getLastDropped());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_CREATE, mc.getLastType());
  		assertEquals(msgId3, mc.getLastMsg().getId());
  		assertTrue(h1.getBufferOccupancy() > 100); // buffer occupancy > 100%
  
  		assertFalse(mc.next());
  
  		clock.advance((m1Size/TRANSMIT_SPEED) + 1);
  		updateAllNodes(); // now transmission should be done
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  		assertEquals(msgId1, mc.getLastMsg().getId());
  
  		assertTrue(mc.next()); // now should drop the transferred message
  		assertEquals(mc.TYPE_DELETE, mc.getLastType());
  		assertTrue(mc.getLastDropped());
  		assertEquals(msgId1, mc.getLastMsg().getId());
  
  		 // buffer occupancy should drop back under 100 %
  		assertTrue(h1.getBufferOccupancy() < 100);
  
  		// should start transferring msgId3
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertEquals(msgId3, mc.getLastMsg().getId());
  
  		assertFalse(mc.next());
  	}
  
  	public void testTtlExpiry() {
  		final int TIME_STEP = 10;
  		Message m1 = new Message(h1,h3, msgId1, 1);
  		h1.createNewMessage(m1);
  		checkCreates(1);
  
  		clock.advance(TIME_STEP);
  		updateAllNodes();
  		assertFalse(mc.next());
  
  		// relay msg1 from h1 to h2
  		h1.connect(h2);
  		updateAllNodes();
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		clock.advance(TIME_STEP);
  		updateAllNodes();
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  
  		assertFalse(mc.next());
  
  		clock.advance((TTL-1)*60 - TIME_STEP*2);
  		updateAllNodes();
  		assertFalse(mc.next());
  		Message m2 = new Message(h4,h3, msgId2, 1);
  		h4.createNewMessage(m2);
  		checkCreates(1);
  
  		clock.advance(61);
  		updateAllNodes();
  
  		// h1 and h2 should delete the expired message
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_DELETE, mc.getLastType());
  		assertEquals(h1, mc.getLastFrom());
  		assertEquals(msgId1, mc.getLastMsg().getId());
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_DELETE, mc.getLastType());
  		assertEquals(h2, mc.getLastFrom());
  		assertEquals(msgId1, mc.getLastMsg().getId());
  
  		assertFalse(mc.next()); // h4 shouldn't remove the msg just yet
  
  		clock.advance((TTL-1)*60 -61);
  		updateAllNodes();
  		assertFalse(mc.next()); // not yet either
  		clock.advance(61);
  
  		updateAllNodes(); // but now it's time for h4 to remove the msg
  
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_DELETE, mc.getLastType());
  		assertEquals(h4, mc.getLastFrom());
  		assertEquals(msgId2, mc.getLastMsg().getId());
  
  		assertFalse(mc.next());
  	}
  
  	public void testResponse() {
  		Message m1 = new Message(h1,h3, msgId1, 1);
  		m1.setResponseSize(1);
  		h1.createNewMessage(m1);
  		h1.connect(h2);
  		updateAllNodes();
  		clock.advance(10);
  		updateAllNodes();
  		h2.connect(h3);
  		updateAllNodes();
  
  		// started h2->h3 relay
  		mc.reset();
  
  		clock.advance(10);
  		updateAllNodes();
  
  		// finished relay
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_RELAY, mc.getLastType());
  
  		// h3 should reply by creating a reply message..
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_CREATE, mc.getLastType());
  		assertEquals(routing.ActiveRouter.RESPONSE_PREFIX + msgId1,
  				mc.getLastMsg().getId());
  		assertEquals(h3, mc.getLastFrom());
  		assertEquals(h1, mc.getLastTo());
  
  		// .. and starting its transfer
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  		assertEquals(routing.ActiveRouter.RESPONSE_PREFIX + msgId1,
  				mc.getLastMsg().getId());
  		assertEquals(h3, mc.getLastFrom());
  		assertEquals(h2, mc.getLastTo());
  
  		assertFalse(mc.next());
  	}
  
  	private void newMessage(String id, DTNHost from, DTNHost to) {
  		Message m = new Message(from, to, id, 1);
  		from.createNewMessage(m);
  	}
  
  	/**
  	 * Runs a message exchange between node h1 and another node
  	 * @param withDestination If true, the other node is the final destination
  	 *        of the messages, if false, the other node is not the final dst
  	 * @return The order of the messages in a space delimited string
  	 */
  	private String runMessageExchange(boolean withDestination) {
  		String msgIds = "";
  		int nrof = 5;
  		DTNHost dst = h4;
  		DTNHost other = h2;
  
  		clock.setTime(0.0);
  		newMessage("1", h1, dst);
  		clock.advance(2.5);
  		newMessage("2", h1, dst);
  		clock.advance(3.5);
  		newMessage("3", h1, dst);
  		clock.advance(1.5);
  		newMessage("3", h1, dst);
  		clock.advance(2.0);
  		newMessage("4", h1, dst);
  		clock.advance(2.5);;
  		newMessage("5", h1, dst);
  
  		if (withDestination) {
  			h1.connect(dst);
  		}
  		else {
  			h1.connect(other);
  		}
  
  		mc.reset();
  		updateAllNodes();
  		assertTrue(mc.next());
  		assertEquals(mc.TYPE_START, mc.getLastType());
  
  		for (int i=0; i < nrof; i++) {
  			msgIds += mc.getLastMsg().getId()+ " ";
  			clock.advance(10);
  			updateAllNodes();
  			assertTrue("index " + i, mc.next());
  			assertEquals(mc.TYPE_RELAY, mc.getLastType());
  			if (i < nrof - 1) {
  				assertTrue("index " + i, mc.next());
  				assertEquals(mc.TYPE_START, mc.getLastType());
  			}
  		}
  
  		assertFalse(mc.next());
  
  		return msgIds;
  	}
  
  	public void testFifoSendingQ() throws Exception {
  		ts.putSetting(MessageRouter.SEND_QUEUE_MODE_S,
  				""+MessageRouter.Q_MODE_FIFO);
  		this.setUp();
  
  		String expectedIds = "1 2 3 4 5 ";
  
  		assertEquals(expectedIds, runMessageExchange(true));
  		assertEquals(expectedIds, runMessageExchange(false));
  	}
  
  	public void testRandomSendingQ() throws Exception {
  		ts.putSetting(MessageRouter.SEND_QUEUE_MODE_S,
  				""+MessageRouter.Q_MODE_RANDOM);
  		this.setUp();
  
  		String orderedIds = "1 2 3 4 5 ";
  
  		assertNotSame(orderedIds, runMessageExchange(true));
  		assertNotSame(orderedIds, runMessageExchange(false));
  	}
  }
  