Adding support for SKIPPED state to STC.

Change-Id: I5a90bedb6272f0bb1d76f0bc5e19e67e8d05eec8
diff --git a/utils/stc/src/main/java/org/onlab/stc/Coordinator.java b/utils/stc/src/main/java/org/onlab/stc/Coordinator.java
index 209de84..123ef7d 100644
--- a/utils/stc/src/main/java/org/onlab/stc/Coordinator.java
+++ b/utils/stc/src/main/java/org/onlab/stc/Coordinator.java
@@ -204,19 +204,20 @@
      */
     private synchronized void execute(Step step) {
         Directive directive = nextAction(step);
-        if (directive == RUN || directive == SKIP) {
+        if (directive == RUN) {
             store.markStarted(step);
             if (step instanceof Group) {
                 Group group = (Group) step;
                 delegate.onStart(group);
-                if (directive == RUN) {
-                    executeRoots(group);
-                } else {
-                    group.children().forEach(child -> delegate.onCompletion(child, 1));
-                }
+                executeRoots(group);
             } else {
-                executor.execute(new StepProcessor(step, directive == SKIP,
-                                                   logDir, delegate));
+                executor.execute(new StepProcessor(step, logDir, delegate));
+            }
+        } else if (directive == SKIP) {
+            if (step instanceof Group) {
+                Group group = (Group) step;
+                group.children().forEach(child -> delegate.onCompletion(child, SKIPPED));
+                delegate.onCompletion(step, SKIPPED);
             }
         }
     }
@@ -237,7 +238,8 @@
             Status depStatus = store.getStatus(dependency.dst());
             if (depStatus == WAITING || depStatus == IN_PROGRESS) {
                 return NOOP;
-            } else if (depStatus == FAILED && !dependency.isSoft()) {
+            } else if ((depStatus == FAILED || depStatus == SKIPPED) &&
+                    !dependency.isSoft()) {
                 return SKIP;
             }
         }
@@ -270,7 +272,7 @@
                 failed = failed || status == FAILED;
             }
             if (done) {
-                delegate.onCompletion(group, failed ? 1 : 0);
+                delegate.onCompletion(group, failed ? FAILED : SUCCEEDED);
             }
         }
     }
@@ -296,9 +298,9 @@
         }
 
         @Override
-        public void onCompletion(Step step, int exitCode) {
-            store.markComplete(step, exitCode == 0 ? SUCCEEDED : FAILED);
-            listeners.forEach(listener -> listener.onCompletion(step, exitCode));
+        public void onCompletion(Step step, Status status) {
+            store.markComplete(step, status);
+            listeners.forEach(listener -> listener.onCompletion(step, status));
             executeSucessors(step);
             latch.countDown();
         }
diff --git a/utils/stc/src/main/java/org/onlab/stc/Main.java b/utils/stc/src/main/java/org/onlab/stc/Main.java
index 567aaa6..bcb259d 100644
--- a/utils/stc/src/main/java/org/onlab/stc/Main.java
+++ b/utils/stc/src/main/java/org/onlab/stc/Main.java
@@ -172,8 +172,8 @@
         }
 
         @Override
-        public void onCompletion(Step step, int exitCode) {
-            logStatus(currentTimeMillis(), step.name(), exitCode == 0 ? SUCCEEDED : FAILED);
+        public void onCompletion(Step step, Status status) {
+            logStatus(currentTimeMillis(), step.name(), status);
         }
 
         @Override
diff --git a/utils/stc/src/main/java/org/onlab/stc/StepProcessListener.java b/utils/stc/src/main/java/org/onlab/stc/StepProcessListener.java
index 2c751a1..421a606 100644
--- a/utils/stc/src/main/java/org/onlab/stc/StepProcessListener.java
+++ b/utils/stc/src/main/java/org/onlab/stc/StepProcessListener.java
@@ -31,10 +31,10 @@
     /**
      * Indicates that process step has completed.
      *
-     * @param step     subject step
-     * @param exitCode step process exit exitCode
+     * @param step   subject step
+     * @param status step completion status
      */
-    default void onCompletion(Step step, int exitCode) {
+    default void onCompletion(Step step, Coordinator.Status status) {
     }
 
     /**
diff --git a/utils/stc/src/main/java/org/onlab/stc/StepProcessor.java b/utils/stc/src/main/java/org/onlab/stc/StepProcessor.java
index 95d5c86..86315e9 100644
--- a/utils/stc/src/main/java/org/onlab/stc/StepProcessor.java
+++ b/utils/stc/src/main/java/org/onlab/stc/StepProcessor.java
@@ -15,6 +15,8 @@
  */
 package org.onlab.stc;
 
+import org.onlab.stc.Coordinator.Status;
+
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
@@ -23,6 +25,8 @@
 import java.io.PrintWriter;
 
 import static java.lang.String.format;
+import static org.onlab.stc.Coordinator.Status.FAILED;
+import static org.onlab.stc.Coordinator.Status.SUCCEEDED;
 import static org.onlab.stc.Coordinator.print;
 
 /**
@@ -30,12 +34,12 @@
  */
 class StepProcessor implements Runnable {
 
+    private static final String IGNORE_CODE = "~";
     private static final int FAIL = -1;
 
     static String launcher = "stc-launcher ";
 
     private final Step step;
-    private final boolean skip;
     private final File logDir;
 
     private Process process;
@@ -45,25 +49,22 @@
      * Creates a process monitor.
      *
      * @param step     step or group to be executed
-     * @param skip     indicates the process should not actually execute
      * @param logDir   directory where step process log should be stored
      * @param delegate process lifecycle listener
      */
-    StepProcessor(Step step, boolean skip, File logDir, StepProcessListener delegate) {
+    StepProcessor(Step step, File logDir, StepProcessListener delegate) {
         this.step = step;
-        this.skip = skip;
         this.logDir = logDir;
         this.delegate = delegate;
     }
 
     @Override
     public void run() {
-        int code = FAIL;
         delegate.onStart(step);
-        if (!skip) {
-            code = execute();
-        }
-        delegate.onCompletion(step, code);
+        int code = execute();
+        boolean ignoreCode = step.env() != null && step.env.equals(IGNORE_CODE);
+        Status status = ignoreCode || code == 0 ? SUCCEEDED : FAILED;
+        delegate.onCompletion(step, status);
     }
 
     /**