Add a unit test for rerouting.

The unit test checks that if the high level intent was marked
as stale when a topology changed event was received while
processing reroute then the stale high level intent is executed
once again after completion of its reroute.

Change-Id: I5fa9fffd037af4740faa9c478b0519e77e776df2
diff --git a/src/test/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModuleTest.java b/src/test/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModuleTest.java
index e9286ad..f341628 100644
--- a/src/test/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModuleTest.java
+++ b/src/test/java/net/onrc/onos/core/intent/runtime/PathCalcRuntimeModuleTest.java
@@ -12,7 +12,6 @@
 import java.util.List;
 
 import net.floodlightcontroller.core.module.FloodlightModuleContext;
-import net.floodlightcontroller.core.module.FloodlightModuleException;
 import net.onrc.onos.core.intent.Intent;
 import net.onrc.onos.core.intent.Intent.IntentState;
 import net.onrc.onos.core.intent.IntentMap;
@@ -182,7 +181,7 @@
      * high level intents contain a proper error entry for the bad path.
      */
     @Test
-    public void testInvalidSwitchName() throws FloodlightModuleException {
+    public void testInvalidSwitchName() {
         final String BAD_SWITCH_INTENT_NAME = "No Such Switch Intent";
 
         // create shortest path intents
@@ -242,8 +241,7 @@
      * entry for the deleted path.
      */
     @Test
-    public void testIntentRemoval() throws FloodlightModuleException {
-
+    public void testIntentRemoval() {
         // create shortest path intents
         final IntentOperationList opList = new IntentOperationList();
         opList.add(Operator.ADD,
@@ -339,15 +337,12 @@
      * entry for the deleted link.
      */
     @Test
-    public void testIntentReroute() throws FloodlightModuleException {
-
+    public void testIntentReroute() {
         // create shortest path intents
         final IntentOperationList opList = new IntentOperationList();
-        final ShortestPathIntent pathIntent1 =
+        opList.add(Operator.ADD,
                 new ShortestPathIntent("1", 1L, 12L, LOCAL_PORT, 2L, 21L,
-                        LOCAL_PORT);
-
-        opList.add(Operator.ADD, pathIntent1);
+                        LOCAL_PORT));
         opList.add(Operator.ADD,
                 new ShortestPathIntent("2", 1L, 14L, LOCAL_PORT, 4L, 41L,
                         LOCAL_PORT));
@@ -556,4 +551,271 @@
         assertThat(reroutedPathIntents,
                 hasIntentWithIdAndState("3___0", IntentState.INST_ACK));
     }
+
+    /**
+     * Test the result of executing a path calculation on an
+     * Intent Operation List and then forcing a reroute and
+     * an interrupt of a topology change event while the reroute
+     * is processed.
+     * <p/>
+     * A 3 path list is created, one of the links is removed, then
+     * the removed link is restored.
+     * The test checks that if the high level intent was marked
+     * as stale when a topology changed event was received while
+     * processing reroute then the stale high level intent is executed
+     * once again after completion of its reroute.
+     */
+    @Test
+    public void testIntentRerouteWithTopologyEventInterrupt() {
+
+        // create shortest path intents
+        final IntentOperationList opList = new IntentOperationList();
+        opList.add(Operator.ADD,
+                new ShortestPathIntent("1", 1L, 12L, LOCAL_PORT, 2L, 21L,
+                        LOCAL_PORT));
+        opList.add(Operator.ADD,
+                new ShortestPathIntent("2", 1L, 14L, LOCAL_PORT, 4L, 41L,
+                        LOCAL_PORT));
+        opList.add(Operator.ADD,
+                new ShortestPathIntent("3", 2L, 23L, LOCAL_PORT, 3L, 32L,
+                        LOCAL_PORT));
+
+        // compile high-level intent operations into low-level intent
+        // operations (calculate paths)
+        final IntentOperationList pathIntentOpList =
+                runtime.executeIntentOperations(opList);
+        assertThat(pathIntentOpList, notNullValue());
+
+        final IntentMap highLevelIntents = runtime.getHighLevelIntents();
+        assertThat(highLevelIntents, notNullValue());
+
+        final Collection<Intent> allIntents = highLevelIntents.getAllIntents();
+        assertThat(allIntents, notNullValue());
+
+        //  Should be one operation per path
+        assertThat(pathIntentOpList, hasSize(opList.size()));
+
+        //  Should be a high level intent for each operation
+        assertThat(allIntents, hasSize(opList.size()));
+
+        // Check that we got a high level intent for each operation
+        assertThat(allIntents, hasIntentWithId("3"));
+        assertThat(allIntents, hasIntentWithId("2"));
+        assertThat(allIntents, hasIntentWithId("1"));
+
+        //  Check that the high level intent 1 was correctly processed
+        assertThat(highLevelIntents,
+                hasIntentWithIdAndState("1", IntentState.INST_REQ));
+
+        //  Check that the high level intent 2 was correctly processed
+        assertThat(highLevelIntents,
+                hasIntentWithIdAndState("2", IntentState.INST_REQ));
+
+        //  Check that the high level intent 3 was correctly processed
+        assertThat(highLevelIntents,
+                hasIntentWithIdAndState("3", IntentState.INST_REQ));
+
+        final IntentMap pathIntents = runtime.getPathIntents();
+        assertThat(pathIntents, notNullValue());
+
+        final Collection<Intent> allPathIntents = pathIntents.getAllIntents();
+        assertThat(allPathIntents, notNullValue());
+
+        // Check that we got a low level intent for each operation
+        assertThat(allPathIntents, hasIntentWithId("3___0"));
+        assertThat(allPathIntents, hasIntentWithId("2___0"));
+        assertThat(allPathIntents, hasIntentWithId("1___0"));
+
+        //  Check that the low level intent 1 was correctly processed
+        assertThat(pathIntents,
+                hasIntentWithIdAndState("1___0", IntentState.INST_REQ));
+
+        //  Check that the low level intent 2 was correctly processed
+        assertThat(pathIntents,
+                hasIntentWithIdAndState("2___0", IntentState.INST_REQ));
+
+        //  Check that the low level intent 3 was correctly processed
+        assertThat(pathIntents,
+                hasIntentWithIdAndState("3___0", IntentState.INST_REQ));
+
+        // Receive notification from south-bound
+        IntentStateList isl = new IntentStateList();
+        isl.put("1___0", IntentState.INST_ACK);
+        isl.put("2___0", IntentState.INST_ACK);
+        isl.put("3___0", IntentState.INST_ACK);
+        isl.domainSwitchDpids.add(1L);
+        isl.domainSwitchDpids.add(2L);
+        isl.domainSwitchDpids.add(3L);
+        isl.domainSwitchDpids.add(4L);
+        runtime.entryUpdated(isl);
+
+        // Now check the results
+        final IntentMap processedHighLevelIntents = runtime.getHighLevelIntents();
+        assertThat(processedHighLevelIntents, notNullValue());
+
+        //  Check that the high level intent 1 was correctly processed
+        assertThat(processedHighLevelIntents,
+                hasIntentWithIdAndState("1", IntentState.INST_ACK));
+
+        //  Check that the high level intent 2 was correctly processed
+        assertThat(processedHighLevelIntents,
+                hasIntentWithIdAndState("2", IntentState.INST_ACK));
+
+        //  Check that the high level intent 3 was correctly processed
+        assertThat(processedHighLevelIntents,
+                hasIntentWithIdAndState("3", IntentState.INST_ACK));
+
+        final IntentMap processedPathIntents = runtime.getPathIntents();
+        assertThat(processedPathIntents, notNullValue());
+
+        //  Check that the low level intent 1 was correctly processed
+        assertThat(processedPathIntents,
+                hasIntentWithIdAndState("1___0", IntentState.INST_ACK));
+
+        //  Check that the low level intent 2 was correctly processed
+        assertThat(processedPathIntents,
+                hasIntentWithIdAndState("2___0", IntentState.INST_ACK));
+
+        //  Check that the low level intent 3 was correctly processed
+        assertThat(processedPathIntents,
+                hasIntentWithIdAndState("3___0", IntentState.INST_ACK));
+
+
+        //  Remove one of the links and check results
+        final List<SwitchEvent> emptySwitchEvents = new LinkedList<>();
+        final List<PortEvent> emptyPortEvents = new LinkedList<>();
+        final List<DeviceEvent> emptyDeviceEvents = new LinkedList<>();
+        final List<LinkEvent> addedLinkEvents = new LinkedList<>();
+        final List<LinkEvent> removedLinkEvents = new LinkedList<>();
+
+        final MockTopology topology = mocks.getTopology();
+        topology.removeLink(1L, 12L, 2L, 21L); // This link is used by the intent "1"
+        topology.removeLink(2L, 21L, 1L, 12L);
+        final LinkEvent linkEvent1 = new LinkEvent(1L, 12L, 2L, 21L);
+        final LinkEvent linkEvent2 = new LinkEvent(2L, 21L, 1L, 12L);
+        removedLinkEvents.add(linkEvent1);
+        removedLinkEvents.add(linkEvent2);
+        runtime.topologyEvents(
+                emptySwitchEvents,
+                emptySwitchEvents,
+                emptyPortEvents,
+                emptyPortEvents,
+                addedLinkEvents,
+                removedLinkEvents,
+                emptyDeviceEvents,
+                emptyDeviceEvents);
+
+        //  Check the high level intents.
+        final IntentMap highLevelIntentsAfterReroute = runtime.getHighLevelIntents();
+        assertThat(highLevelIntentsAfterReroute, notNullValue());
+
+        //  Check the states of the high level intents
+        //  Check that the high level intent 1 was correctly processed
+        assertThat(highLevelIntentsAfterReroute,
+                hasIntentWithIdAndState("1", IntentState.REROUTE_REQ));
+
+        //  Check that the high level intent 2 was not affected
+        assertThat(highLevelIntentsAfterReroute,
+                hasIntentWithIdAndState("2", IntentState.INST_ACK));
+
+        //  Check that the high level intent 3 was not affected
+        assertThat(highLevelIntentsAfterReroute,
+                hasIntentWithIdAndState("3", IntentState.INST_ACK));
+
+        final IntentMap pathIntentsAfterReroute = runtime.getPathIntents();
+        assertThat(pathIntentsAfterReroute, notNullValue());
+
+        //  Check that the low level intent 1 was correctly processed
+        assertThat(pathIntentsAfterReroute,
+                hasIntentWithIdAndState("1___0", IntentState.DEL_REQ));
+        assertThat(pathIntentsAfterReroute,
+                hasIntentWithIdAndState("1___1", IntentState.INST_REQ));
+
+        //  Check that the low level intent 2 was not affected
+        assertThat(pathIntentsAfterReroute,
+                hasIntentWithIdAndState("2___0", IntentState.INST_ACK));
+
+        //  Check that the low level intent 3 was not affected
+        assertThat(pathIntentsAfterReroute,
+                hasIntentWithIdAndState("3___0", IntentState.INST_ACK));
+
+        // Interrupt by topology changed event while the reroute
+        removedLinkEvents.clear();
+        addedLinkEvents.clear();
+        topology.addBidirectionalLinks(1L, 12L, 2L, 21L); // Restoration of the failure
+        addedLinkEvents.add(linkEvent1);
+        addedLinkEvents.add(linkEvent2);
+        runtime.topologyEvents(
+                emptySwitchEvents,
+                emptySwitchEvents,
+                emptyPortEvents,
+                emptyPortEvents,
+                addedLinkEvents,
+                removedLinkEvents,
+                emptyDeviceEvents,
+                emptyDeviceEvents);
+
+        //  Check the high level intents.
+        final IntentMap highLevelIntentsAfterInterrupt = runtime.getHighLevelIntents();
+        assertThat(highLevelIntentsAfterInterrupt, notNullValue());
+
+        //  Check the states of the high level intents
+        //  Check that the high level intent 1 was not affected
+        assertThat(highLevelIntentsAfterInterrupt,
+                hasIntentWithIdAndState("1", IntentState.REROUTE_REQ));
+
+        final IntentMap pathIntentsAfterInterrupt = runtime.getPathIntents();
+        assertThat(pathIntentsAfterInterrupt, notNullValue());
+
+        //  Check that the low level intent 1 was not affected
+        assertThat(pathIntentsAfterInterrupt,
+                hasIntentWithIdAndState("1___0", IntentState.DEL_REQ));
+        assertThat(pathIntentsAfterInterrupt,
+                hasIntentWithIdAndState("1___1", IntentState.INST_REQ));
+
+        // Receive notification from south-bound
+        isl = new IntentStateList();
+        isl.put("1___0", IntentState.DEL_ACK);
+        isl.put("1___1", IntentState.INST_ACK);
+        isl.domainSwitchDpids.add(1L);
+        isl.domainSwitchDpids.add(2L);
+        isl.domainSwitchDpids.add(4L);
+        runtime.entryUpdated(isl);
+
+        // Now check the results
+        final IntentMap reroutedHighLevelIntents = runtime.getHighLevelIntents();
+        assertThat(reroutedHighLevelIntents, notNullValue());
+
+        // Check that the high level intent 1 was correctly processed
+        // It should be rerouted once again
+        assertThat(reroutedHighLevelIntents,
+                hasIntentWithIdAndState("1", IntentState.REROUTE_REQ));
+
+        //  Check that the high level intent 2 was not affected
+        assertThat(reroutedHighLevelIntents,
+                hasIntentWithIdAndState("2", IntentState.INST_ACK));
+
+        //  Check that the high level intent 3 was not affected
+        assertThat(reroutedHighLevelIntents,
+                hasIntentWithIdAndState("3", IntentState.INST_ACK));
+
+        final IntentMap reroutedPathIntents = runtime.getPathIntents();
+        assertThat(processedPathIntents, notNullValue());
+
+        //  Check that the low level intent 1 was correctly processed
+        assertThat(reroutedPathIntents.getAllIntents(),
+                   not(hasIntentWithId("1___0")));
+        assertThat(reroutedPathIntents,
+                hasIntentWithIdAndState("1___1", IntentState.DEL_REQ));
+        assertThat(reroutedPathIntents,
+                hasIntentWithIdAndState("1___2", IntentState.INST_REQ));
+
+        //  Check that the low level intent 2 was not affected
+        assertThat(reroutedPathIntents,
+                hasIntentWithIdAndState("2___0", IntentState.INST_ACK));
+
+        //  Check that the low level intent 3 was not affected
+        assertThat(reroutedPathIntents,
+                hasIntentWithIdAndState("3___0", IntentState.INST_ACK));
+    }
 }