Enhancing STC and scenarios.

Change-Id: I57a4d25b7fb726a1242073055474ff5c7c3c1087
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 6ee03c5..167570c 100644
--- a/utils/stc/src/main/java/org/onlab/stc/Compiler.java
+++ b/utils/stc/src/main/java/org/onlab/stc/Compiler.java
@@ -50,6 +50,8 @@
     private static final String LOG_DIR = "[@logDir]";
     private static final String NAME = "[@name]";
     private static final String COMMAND = "[@exec]";
+    private static final String ENV = "[@env]";
+    private static final String CWD = "[@cwd]";
     private static final String REQUIRES = "[@requires]";
     private static final String IF = "[@if]";
     private static final String UNLESS = "[@unless]";
@@ -200,11 +202,12 @@
     private void processStep(HierarchicalConfiguration cfg,
                              String namespace, Group parentGroup) {
         String name = expand(prefix(cfg.getString(NAME), namespace));
-        String defaultValue = parentGroup != null ? parentGroup.command() : null;
-        String command = expand(cfg.getString(COMMAND, defaultValue));
+        String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null));
+        String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null));
+        String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null));
 
-        print("step name=%s command=%s", name, command);
-        Step step = new Step(name, command, parentGroup);
+        print("step name=%s command=%s env=%s cwd=%s", name, command, env, cwd);
+        Step step = new Step(name, command, env, cwd, parentGroup);
         registerStep(step, cfg, namespace, parentGroup);
     }
 
@@ -218,11 +221,12 @@
     private void processGroup(HierarchicalConfiguration cfg,
                               String namespace, Group parentGroup) {
         String name = expand(prefix(cfg.getString(NAME), namespace));
-        String defaultValue = parentGroup != null ? parentGroup.command() : null;
-        String command = expand(cfg.getString(COMMAND, defaultValue));
+        String command = expand(cfg.getString(COMMAND, parentGroup != null ? parentGroup.command() : null));
+        String env = expand(cfg.getString(ENV, parentGroup != null ? parentGroup.env() : null));
+        String cwd = expand(cfg.getString(CWD, parentGroup != null ? parentGroup.cwd() : null));
 
-        print("group name=%s command=%s", name, command);
-        Group group = new Group(name, command, parentGroup);
+        print("group name=%s command=%s env=%s cwd=%s", name, command, env, cwd);
+        Group group = new Group(name, command, env, cwd, parentGroup);
         if (registerStep(group, cfg, namespace, parentGroup)) {
             compile(cfg, namespace, group);
         }
diff --git a/utils/stc/src/main/java/org/onlab/stc/Group.java b/utils/stc/src/main/java/org/onlab/stc/Group.java
index a094828..0281c36 100644
--- a/utils/stc/src/main/java/org/onlab/stc/Group.java
+++ b/utils/stc/src/main/java/org/onlab/stc/Group.java
@@ -31,11 +31,13 @@
      * Creates a new test step.
      *
      * @param name    group name
-     * @param command group default command
+     * @param command default command
+     * @param env     default path to file to be sourced into the environment
+     * @param cwd     default path to current working directory for the step
      * @param group   optional group to which this step belongs
      */
-    public Group(String name, String command, Group group) {
-        super(name, command, group);
+    public Group(String name, String command, String env, String cwd, Group group) {
+        super(name, command, env, cwd, group);
     }
 
     /**
diff --git a/utils/stc/src/main/java/org/onlab/stc/Step.java b/utils/stc/src/main/java/org/onlab/stc/Step.java
index 834e0ec..3d8ea98 100644
--- a/utils/stc/src/main/java/org/onlab/stc/Step.java
+++ b/utils/stc/src/main/java/org/onlab/stc/Step.java
@@ -29,22 +29,28 @@
 
     protected final String name;
     protected final String command;
+    protected final String env;
+    protected final String cwd;
     protected final Group group;
 
     /**
      * Creates a new test step.
      *
-     * @param name     step name
-     * @param command  step command to execute
-     * @param group    optional group to which this step belongs
+     * @param name    step name
+     * @param command step command to execute
+     * @param env     path to file to be sourced into the environment
+     * @param cwd     path to current working directory for the step
+     * @param group   optional group to which this step belongs
      */
-    public Step(String name, String command, Group group) {
+    public Step(String name, String command, String env, String cwd, Group group) {
         this.name = checkNotNull(name, "Name cannot be null");
         this.group = group;
 
-        // Set the command; if one is not given default to the enclosing group
-        this.command = command != null ? command :
-                group != null && group.command != null ? group.command : null;
+        // Set the command, environment and cwd
+        // If one is not given use the value from the enclosing group
+        this.command = command != null ? command : group != null && group.command != null ? group.command : null;
+        this.env = env != null ? env : group != null && group.env != null ? group.env : null;
+        this.cwd = cwd != null ? cwd : group != null && group.cwd != null ? group.cwd : null;
     }
 
     /**
@@ -66,6 +72,24 @@
     }
 
     /**
+     * Returns the step environment script path.
+     *
+     * @return env script path
+     */
+    public String env() {
+        return env;
+    }
+
+    /**
+     * Returns the step current working directory path.
+     *
+     * @return current working dir path
+     */
+    public String cwd() {
+        return cwd;
+    }
+
+    /**
      * Returns the enclosing group; null if none.
      *
      * @return enclosing group or null
@@ -97,6 +121,8 @@
         return MoreObjects.toStringHelper(this)
                 .add("name", name)
                 .add("command", command)
+                .add("env", env)
+                .add("cwd", cwd)
                 .add("group", group)
                 .toString();
     }
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 b2d7635..95d5c86 100644
--- a/utils/stc/src/main/java/org/onlab/stc/StepProcessor.java
+++ b/utils/stc/src/main/java/org/onlab/stc/StepProcessor.java
@@ -22,6 +22,7 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 
+import static java.lang.String.format;
 import static org.onlab.stc.Coordinator.print;
 
 /**
@@ -71,8 +72,8 @@
      * @return exit code
      */
     private int execute() {
-        try (PrintWriter pw = new PrintWriter(logFile(step))) {
-            process = Runtime.getRuntime().exec(launcher + step.command());
+        try (PrintWriter pw = new PrintWriter(logFile())) {
+            process = Runtime.getRuntime().exec(command());
             processOutput(pw);
 
             // Wait for the process to complete and get its exit code.
@@ -90,6 +91,18 @@
     }
 
     /**
+     * Returns ready-to-run command for the step.
+     *
+     * @return command to execute
+     */
+    private String command() {
+        return format("%s %s %s %s", launcher,
+                      step.env() != null ? step.env() : "-",
+                      step.cwd() != null ? step.cwd() : "-",
+                      step.command());
+    }
+
+    /**
      * Captures output of the step process.
      *
      * @param pw print writer to send output to
@@ -108,12 +121,11 @@
     }
 
     /**
-     * Returns the log file for the specified step.
+     * Returns the log file for the step output.
      *
-     * @param step test step
      * @return log file
      */
-    private File logFile(Step step) {
+    private File logFile() {
         return new File(logDir, step.name() + ".log");
     }