Added check for cyclic dependencies.

Changes intent test scenarios to use the key and added dependencies to make them more robust; h2h still fails intermittently due to a bug.

Increased parallelism of the smoke test scenario.

Change-Id: Ib7fed38d17d1b25c5dd854ef1cd4dc777002c5fc
diff --git a/tools/test/bin/onos-check-apps b/tools/test/bin/onos-check-apps
index 9bdf393..dfd6b4e 100755
--- a/tools/test/bin/onos-check-apps
+++ b/tools/test/bin/onos-check-apps
@@ -9,16 +9,26 @@
 aux=/tmp/stc-$$.log
 trap "rm -f $aux $aux.1 $aux.2 2>/dev/null" EXIT
 
-onos ${1:-$OCI} "onos:apps -s -a" | grep -v /bin/client > $aux
-cat $aux
+for attempt in {1..3}; do
+    onos ${1:-$OCI} "onos:apps -s -a" | grep -v /bin/client > $aux
+    cat $aux
 
-# Normalize the installed apps
-cut -c7- $aux | grep -v '/bin/client' | cut -d\  -f1 | sort > $aux.1
+    # Normalize the installed apps
+    cut -c7- $aux | grep -v '/bin/client' | cut -d\  -f1 | sort > $aux.1
 
-# Normalize the expected apps
-apps=${2:-$ONOS_APPS}
-apps=${apps:-drivers,openflow}
-(for app in ${apps//,/ }; do echo org.onosproject.$app; done) | sort > $aux.2
+    # Normalize the expected apps
+    apps=${2:-$ONOS_APPS}
+    apps=${apps:-drivers,openflow}
+    (for app in ${apps//,/ }; do echo org.onosproject.$app; done) | sort > $aux.2
 
-# Check for differences
-diff $aux.1 $aux.2
+    # Check for differences
+    case ${3:-equals} in
+    equals) diff $aux.1 $aux.2;;
+    includes) [ $(egrep -c -f $aux.2 $aux.1) -eq $(wc -l $aux.2 | sed "s|$aux.2||g") ];;
+    excludes) ! egrep -f $aux.2 $aux.1;;
+    esac
+
+    [ $? -eq 0 ] && exit 0 || sleep 1
+done
+
+exit 1;
diff --git a/tools/test/bin/onos-check-intent b/tools/test/bin/onos-check-intent
index 55410ad..e332dc5 100755
--- a/tools/test/bin/onos-check-intent
+++ b/tools/test/bin/onos-check-intent
@@ -13,9 +13,9 @@
 echo onos-check-intent: $*
 
 set -x
-for i in 1 2 3; do
-    onos $target "onos:intents" > $aux
-    ( cat $aux | grep "id=$2" | grep "state=$3" ) && cat $aux && exit 0
+for i in {1..10}; do
+    onos $target "onos:intents" | tee $aux
+    ( cat $aux | grep "key=$2" | grep "state=$3" ) && cat $aux && exit 0
     sleep 1
 done
 
diff --git a/tools/test/bin/onos-create-intent b/tools/test/bin/onos-create-intent
index 6482103..d1c0b93 100755
--- a/tools/test/bin/onos-create-intent
+++ b/tools/test/bin/onos-create-intent
@@ -18,15 +18,5 @@
 
 set -x
 
-onos $target "onos:add-${type}-intent" "${arg1}" "${arg2}" >> $aux
-result=$?
-cat $aux
-
-if [ $result -eq 0 ]; then
-    id=$(cat $aux | sed -e "1d" | sed -e "s/^[a-zA-Z]*{//" | sed -e "s/,.*$//" | sed -e "s/^...//")
-    echo @stc ${name}Id=${id}
-fi
-
-
-exit $result
+onos $target "onos:add-${type}-intent" --key $name "${arg1}" "${arg2}"
 
diff --git a/tools/test/scenarios/net-host-intent.xml b/tools/test/scenarios/net-host-intent.xml
index 88b79bf..fbf8c4a 100644
--- a/tools/test/scenarios/net-host-intent.xml
+++ b/tools/test/scenarios/net-host-intent.xml
@@ -16,36 +16,44 @@
 <scenario name="net-host-intent" description="Network host intent connectivity test">
     <!-- TODO: parametrize this via recipes -->
     <group name="Host-Intent-Connectivity">
-        <step name="Uninstall-Reactive-Forwarding"
+        <step name="Host-Intent.Uninstall-Reactive-Forwarding"
               exec="onos ${OC1} app deactivate org.onosproject.fwd org.onosproject.ifwd"/>
+        <step name="Host-Intent.Check-Apps" requires="^"
+              exec="onos-check-apps ${OC1} fwd,ifwd excludes"/>
 
-        <step name="Find-Host-1" requires="^"
-              exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect ."/>
-        <step name="Find-Host-2" requires="^"
-              exec="onos-mininet sendAndExpect h4 ping -c1 h1 --expect ."/>
+        <step name="Host-Intent.Find-Host-1" requires="^"
+              exec="onos-mininet sendAndExpect h1 ping -c1 -w1 h4 --expect ."/>
+        <step name="Host-Intent.Find-Host-2" requires="^"
+              exec="onos-mininet sendAndExpect h4 ping -c1 -w1 h1 --expect ."/>
 
-        <step name="Create-Intent" requires="^"
-              exec="onos-create-intent ${OC1} hostToHost host 00:00:00:00:00:01/-1 00:00:00:00:00:04/-1"/>
-        <step name="Validate-Intent-Installed" exec="onos-check-intent ${OC1} ${hostToHostId} INSTALLED"
-              requires="Create-Intent" />
+        <step name="Host-Intent.Create-Intent" requires="^"
+              exec="onos-create-intent ${OC1} h2h host 00:00:00:00:00:01/-1 00:00:00:00:00:04/-1"/>
+        <step name="Host-Intent.Validate-Intent-Installed" requires="Host-Intent.Create-Intent"
+              exec="onos-check-intent ${OC1} h2h INSTALLED"/>
 
         <import file="${ONOS_SCENARIOS}/net-link-down-up.xml" namespace="Host-Intent"/>
-        <dependency name="Host-Intent.Net-Link-Down-Up" requires="Validate-Intent-Installed"/>
+        <dependency name="Host-Intent.Net-Link-Down-Up"
+                    requires="Host-Intent.Validate-Intent-Installed"/>
 
-        <step name="Validate-Intent-Installed-Still" exec="onos-check-intent ${OC1} ${hostToHostId} INSTALLED"
-              requires="Host-Intent.Link-1-Down" />
+        <step name="Host-Intent.Validate-Intent-Installed-Still" requires="Host-Intent.Link-1-Down"
+              exec="onos-check-intent ${OC1} h2h INSTALLED"/>
 
-        <step name="Validate-Intent-Failed" exec="onos-check-intent ${OC1} ${hostToHostId} FAILED"
-              requires="Host-Intent.Link-2-Down" />
+        <dependency name="Host-Intent.Link-2-Down"
+                    requires="~Host-Intent.Validate-Intent-Installed-Still" />
 
-        <step name="Validate-Intent-Installed-Again" exec="onos-check-intent ${OC1} ${hostToHostId} INSTALLED"
-              requires="Host-Intent.Link-1-Up" />
-        <dependency name="Host-Intent.Ping-4" requires="Validate-Intent-Installed-Again" />
+        <step name="Host-Intent.Validate-Intent-Failed" requires="Host-Intent.Link-2-Down"
+              exec="onos-check-intent ${OC1} h2h FAILED"/>
 
-        <step name="Remove-Intent" requires="~Host-Intent.Net-Link-Down-Up"
-              exec="onos ${OC1} remove-intent -p"/>
-        
+        <dependency name="Host-Intent.Link-1-Up"
+                    requires="~Host-Intent.Validate-Intent-Failed" />
 
+        <step name="Host-Intent.Validate-Intent-Installed-Again" requires="Host-Intent.Link-1-Up"
+              exec="onos-check-intent ${OC1} h2h INSTALLED"/>
 
+        <dependency name="Host-Intent.Ping-4"
+                    requires="~Host-Intent.Validate-Intent-Installed-Again" />
+
+        <step name="Host-Intent.Remove-Intent" requires="~Host-Intent.Net-Link-Down-Up"
+              exec="onos ${OC1} remove-intent -p org.onosproject.cli h2h"/>
     </group>
 </scenario>
diff --git a/tools/test/scenarios/net-link-down-up.xml b/tools/test/scenarios/net-link-down-up.xml
index 70a8fee..8bcbfa7 100644
--- a/tools/test/scenarios/net-link-down-up.xml
+++ b/tools/test/scenarios/net-link-down-up.xml
@@ -18,21 +18,21 @@
     <group name="Net-Link-Down-Up">
         <step name="Ping-1"
               exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect \ 0% packet loss"/>
-        <step name="Link-1-Down" requires="Ping-1"
+        <step name="Link-1-Down" requires="~Ping-1"
               exec="onos-mininet sendAndExpect link s4 s7 down --expect ."/>
-        <step name="Ping-2" requires="Link-1-Down"
+        <step name="Ping-2" requires="~Link-1-Down"
               exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect \ 0% packet loss"/>
-        <step name="Link-2-Down" requires="Ping-2"
+        <step name="Link-2-Down" requires="~Ping-2"
               exec="onos-mininet sendAndExpect link s4 s5 down --expect ."/>
-        <step name="Ping-3" requires="Link-2-Down"
-              exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect 100% packet loss"/>
-        <step name="Link-1-Up" requires="Ping-3"
+        <step name="Ping-3" requires="~Link-2-Down"
+              exec="onos-mininet sendAndExpect h1 ping -c1 -w1 h4 --expect 100% packet loss"/>
+        <step name="Link-1-Up" requires="~Ping-3"
               exec="onos-mininet sendAndExpect link s4 s7 up --expect ."/>
-        <step name="Ping-4" requires="Link-1-Up"
+        <step name="Ping-4" requires="~Link-1-Up"
               exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect \ 0% packet loss"/>
-        <step name="Link-2-Up" requires="Ping-4"
+        <step name="Link-2-Up" requires="~Ping-4"
               exec="onos-mininet sendAndExpect link s4 s5 up --expect ."/>
-        <step name="Ping-5" requires="Link-2-Up"
+        <step name="Ping-5" requires="~Link-2-Up"
               exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect \ 0% packet loss"/>
     </group>
 </scenario>
\ No newline at end of file
diff --git a/tools/test/scenarios/net-pingall.xml b/tools/test/scenarios/net-pingall.xml
index df1ae1f..8968e0d 100644
--- a/tools/test/scenarios/net-pingall.xml
+++ b/tools/test/scenarios/net-pingall.xml
@@ -18,11 +18,14 @@
     <group name="Net-Pingall">
         <step name="Install-Apps"
               exec="onos ${OC1} app activate org.onosproject.openflow org.onosproject.proxyarp org.onosproject.fwd"/>
+        <step name="Check-Apps" requires="^"
+              exec="onos-check-apps ${OC1} drivers,openflow,proxyarp,fwd includes"/>
 
-        <step name="Check-Apps" requires="Install-Apps"
-              exec="onos-check-apps ${OC1} drivers,openflow,proxyarp,fwd"/>
+        <!-- TODO: take this out when initial pingall sweep is 100% -->
+        <step name="Initial-Ping-All" requires="Check-Apps"
+              exec="onos-mininet sendAndExpect py net.pingAll(1) --expect 600 received"/>
 
-        <step name="Ping-All-And-Verify" requires="Check-Apps"
+        <step name="Ping-All-And-Verify" requires="Check-Apps,Initial-Ping-All"
               exec="onos-mininet sendAndExpect py net.pingAll(1) --expect 600/600 received"/>
 
         <step name="Check-Summary-For-Hosts" requires="~Ping-All-And-Verify"
diff --git a/tools/test/scenarios/net-point-intent.xml b/tools/test/scenarios/net-point-intent.xml
index 125f491..acb8212 100644
--- a/tools/test/scenarios/net-point-intent.xml
+++ b/tools/test/scenarios/net-point-intent.xml
@@ -13,44 +13,65 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<scenario name="net-point-intent" description="Network point to point intent connectivity test">
+<scenario name="net-point-intent"
+          description="Network point to point intent connectivity test">
     <!-- TODO: parametrize this via recipes -->
-    <group name="Point-To-Point-Intent-Connectivity">
-        <step name="P2P-Uninstall-Reactive-Forwarding"
+    <group name="P2P-Intent-Connectivity">
+        <step name="P2P-Intent.Uninstall-Reactive-Forwarding"
               exec="onos ${OC1} app deactivate org.onosproject.fwd org.onosproject.ifwd"/>
+        <step name="P2P-Intent.Check-Apps" requires="^"
+              exec="onos-check-apps ${OC1} fwd,ifwd excludes"/>
 
-        <step name="P2P-Find-Host-1" requires="^"
-              exec="onos-mininet sendAndExpect h1 ping -c1 h4 --expect ."/>
-        <step name="P2PFind-Host-2" requires="^"
-              exec="onos-mininet sendAndExpect h4 ping -c1 h1 --expect ."/>
+        <step name="P2P-Intent.Find-Host-1" requires="^"
+              exec="onos-mininet sendAndExpect h1 ping -c1 -w1 h4 --expect ."/>
+        <step name="P2P-Intent.Find-Host-2" requires="^"
+              exec="onos-mininet sendAndExpect h4 ping -c1 -w1 h1 --expect ."/>
 
-        <step name="P2P-Create-Intent-1-To-4" requires="^"
-              exec="onos-create-intent ${OC1} oneToFour point of:0000000000000001/1 of:0000000000000004/1"/>
-        <step name="P2P-Create-Intent-4-To-1" requires="^"
-              exec="onos-create-intent ${OC1} fourToOne point of:0000000000000004/1 of:0000000000000001/1"/>
+        <step name="P2P-Intent.Create-Intent-XY" requires="^"
+              exec="onos-create-intent ${OC1} xy point of:0000000000000001/1 of:0000000000000004/1"/>
+        <step name="P2P-Intent.Create-Intent-YX" requires="^"
+              exec="onos-create-intent ${OC1} yx point of:0000000000000004/1 of:0000000000000001/1"/>
 
-        <step name="P2P-Validate-Point-Intents-Installed1" exec="onos-check-intent ${OC1} ${fourToOneId} INSTALLED"
-              requires="^" />
-        <step name="P2P-Validate-Point-Intents-Installed2" exec="onos-check-intent ${OC1} ${oneToFourId} INSTALLED"
-              requires="^" />
+        <step name="P2P-Intent.Validate-Intent-XY-Installed" requires="^"
+              exec="onos-check-intent ${OC1} yx INSTALLED"/>
+        <step name="P2P-Intent.Validate-Intent-YX-Installed" requires="^"
+              exec="onos-check-intent ${OC1} xy INSTALLED"/>
 
-        <import file="${ONOS_SCENARIOS}/net-link-down-up.xml" namespace="Point-To-Point-Intent"/>
-        <dependency name="Point-To-Point-Intent.Net-Link-Down-Up" requires="P2P-Validate-Point-Intents-Installed2"/>
-        <step name="P2P-Validate-Intent-Installed-Still" exec="onos-check-intent ${OC1} ${fourToOneId} INSTALLED"
-              requires="Point-To-Point-Intent.Link-1-Down" />
+        <import file="${ONOS_SCENARIOS}/net-link-down-up.xml" namespace="P2P-Intent"/>
+        <dependency name="P2P-Intent.Net-Link-Down-Up"
+                    requires="P2P-Intent.Validate-Intent-XY-Installed,
+                              P2P-Intent.Validate-Intent-YX-Installed"/>
 
-        <step name="P2P-Validate-Intent-Failed" exec="onos-check-intent ${OC1} ${fourToOneId} FAILED"
-              requires="Point-To-Point-Intent.Link-2-Down" />
-        <step name="P2P-Validate-Intent-Failed2" exec="onos-check-intent ${OC1} ${oneToFourId} FAILED"
-              requires="Point-To-Point-Intent.Link-2-Down" />
+        <step name="P2P-Intent.Validate-Intent-XY-Installed-Still" requires="P2P-Intent.Link-1-Down"
+              exec="onos-check-intent ${OC1} xy INSTALLED"/>
+        <step name="P2P-Intent.Validate-Intent-YX-Installed-Still" requires="P2P-Intent.Link-1-Down"
+              exec="onos-check-intent ${OC1} yx INSTALLED"/>
 
-        <step name="P2P-Validate-Intent-Installed-Again" exec="onos-check-intent ${OC1} ${fourToOneId} INSTALLED"
-              requires="Point-To-Point-Intent.Link-1-Up" />
-        <dependency name="Point-To-Point-Intent.Ping-4" requires="P2P-Validate-Intent-Installed-Again" />
+        <dependency name="P2P-Intent.Link-2-Down"
+                    requires="~P2P-Intent.Validate-Intent-XY-Installed-Still,
+                              ~P2P-Intent.Validate-Intent-YX-Installed-Still"/>
 
-        <step name="P2P-Remove-Intent-4-to-1" requires="~Point-To-Point-Intent.Net-Link-Down-Up"
-              exec="onos ${OC1} remove-intent -p org.onosproject.cli ${fourToOneId}"/>
-        <step name="P2P-Remove-Intent-1-to-4" requires="^"
-              exec="onos ${OC1} remove-intent -p org.onosproject.cli ${oneToFourId}"/>
+        <step name="P2P-Intent.Validate-Intent-XY-Failed" requires="P2P-Intent.Link-2-Down"
+              exec="onos-check-intent ${OC1} xy FAILED"/>
+        <step name="P2P-Intent.Validate-Intent-YX-Failed" requires="P2P-Intent.Link-2-Down"
+              exec="onos-check-intent ${OC1} yx FAILED"/>
+
+        <dependency name="P2P-Intent.Link-1-Up"
+                    requires="~P2P-Intent.Validate-Intent-XY-Failed,
+                              ~P2P-Intent.Validate-Intent-YX-Failed" />
+
+        <step name="P2P-Intent.Validate-Intent-XY-Installed-Again" requires="P2P-Intent.Link-1-Up"
+              exec="onos-check-intent ${OC1} xy INSTALLED"/>
+        <step name="P2P-Intent.Validate-Intent-YX-Installed-Again" requires="P2P-Intent.Link-1-Up"
+              exec="onos-check-intent ${OC1} yx INSTALLED"/>
+
+        <dependency name="P2P-Intent.Ping-4"
+                    requires="~P2P-Intent.Validate-Intent-XY-Installed-Again,
+                              ~P2P-Intent.Validate-Intent-YX-Installed-Again"/>
+
+        <step name="P2P-Intent.Remove-Intent-XY" requires="~P2P-Intent.Net-Link-Down-Up"
+              exec="onos ${OC1} remove-intent -p org.onosproject.cli xy"/>
+        <step name="P2P-Intent.Remove-Intent-YX" requires="~P2P-Intent.Net-Link-Down-Up"
+              exec="onos ${OC1} remove-intent -p org.onosproject.cli yx"/>
     </group>
 </scenario>
diff --git a/tools/test/scenarios/net-setup.xml b/tools/test/scenarios/net-setup.xml
index 5b71dfe..e179ec5 100644
--- a/tools/test/scenarios/net-setup.xml
+++ b/tools/test/scenarios/net-setup.xml
@@ -29,7 +29,7 @@
               exec="onos-mininet start topos/topo att-onos.py ${ONOS_INSTANCES}"/>
 
         <step name="Wait-For-Mininet" requires="Start-Mininet"
-              exec="onos-mininet wait 20"/>
+              exec="onos-mininet wait 10"/>
 
         <step name="Check-Summary" requires="Wait-For-Mininet"
               exec="onos-check-summary ${OC1} [0-9]* 25 140 0"/>
diff --git a/tools/test/scenarios/net-smoke.xml b/tools/test/scenarios/net-smoke.xml
index 15974c1..025dd26 100644
--- a/tools/test/scenarios/net-smoke.xml
+++ b/tools/test/scenarios/net-smoke.xml
@@ -27,9 +27,9 @@
         <dependency name="Host-Intent-Connectivity" requires="Net-Setup,~Reactive-Forwarding.Net-Link-Down-Up"/>
 
         <import file="${ONOS_SCENARIOS}/net-point-intent.xml"/>
-        <dependency name="Point-To-Point-Intent-Connectivity" requires="Net-Setup,~Reactive-Forwarding.Net-Link-Down-Up,Host-Intent-Connectivity"/>
+        <dependency name="P2P-Intent-Connectivity" requires="Net-Setup,~Reactive-Forwarding.Net-Link-Down-Up,Host-Intent-Connectivity"/>
 
         <import file="${ONOS_SCENARIOS}/net-teardown.xml"/>
-        <dependency name="Net-Teardown" requires="~Host-Intent-Connectivity,~Point-To-Point-Intent-Connectivity"/>
+        <dependency name="Net-Teardown" requires="~Host-Intent-Connectivity,~P2P-Intent-Connectivity"/>
     </group>
 </scenario>
diff --git a/tools/test/scenarios/smoke.xml b/tools/test/scenarios/smoke.xml
index 88608d5..ce8140a 100644
--- a/tools/test/scenarios/smoke.xml
+++ b/tools/test/scenarios/smoke.xml
@@ -23,7 +23,7 @@
     <dependency name="Net-Smoke" requires="Setup"/>
 
     <import file="${ONOS_SCENARIOS}/archetypes.xml"/>
-    <dependency name="Archetypes" requires="~Net-Smoke,Setup"/>
+    <dependency name="Archetypes" requires="Setup"/>
 
     <import file="${ONOS_SCENARIOS}/wrapup.xml"/>
     <dependency name="Wrapup" requires="~Archetypes,~Setup,~Net-Smoke"/>
diff --git a/utils/stc/src/main/java/org/onlab/stc/Compiler.java b/utils/stc/src/main/java/org/onlab/stc/Compiler.java
index 162e8df..c2a0c81 100644
--- a/utils/stc/src/main/java/org/onlab/stc/Compiler.java
+++ b/utils/stc/src/main/java/org/onlab/stc/Compiler.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onlab.graph.DepthFirstSearch;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -29,10 +30,10 @@
 import java.util.Map;
 import java.util.Set;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Preconditions.*;
 import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.graph.DepthFirstSearch.EdgeType.BACK_EDGE;
+import static org.onlab.graph.GraphPathSearch.ALL_PATHS;
 import static org.onlab.stc.Scenario.loadScenario;
 
 /**
@@ -109,6 +110,8 @@
         processFlow = new ProcessFlow(ImmutableSet.copyOf(steps.values()),
                                       ImmutableSet.copyOf(dependencies));
 
+        scanForCycles();
+
         // Extract the log directory if there was one specified
         String defaultPath = DEFAULT_LOG_DIR + scenario.name();
         String path = scenario.definition().getString(LOG_DIR, defaultPath);
@@ -449,6 +452,22 @@
     }
 
     /**
+     * Scans the process flow graph for cyclic dependencies.
+     */
+    private void scanForCycles() {
+        DepthFirstSearch<Step, Dependency> dfs = new DepthFirstSearch<>();
+        // Use a brute-force method of searching paths from all vertices.
+        processFlow().getVertexes().forEach(s -> {
+            DepthFirstSearch<Step, Dependency>.SpanningTreeResult r =
+                    dfs.search(processFlow, s, null, null, ALL_PATHS);
+            r.edges().forEach((e, et) -> checkArgument(et != BACK_EDGE,
+                                                       "Process flow has a cycle involving dependency from %s to %s",
+                                                       e.src().name, e.dst().name));
+        });
+    }
+
+
+    /**
      * Prints formatted output.
      *
      * @param format printf format string