Thomas Vachuska

Enhancing STC

Change-Id: If9429cc6a30333bd27b579825fe6b1fac221cf60
...@@ -13,4 +13,8 @@ scenario=${1:-smoke} ...@@ -13,4 +13,8 @@ scenario=${1:-smoke}
13 [ ! -f $scenario ] && scenario=$scenario.xml 13 [ ! -f $scenario ] && scenario=$scenario.xml
14 [ ! -f $scenario ] && echo "Scenario $scenario file not found" && exit 1 14 [ ! -f $scenario ] && echo "Scenario $scenario file not found" && exit 1
15 15
16 -java -jar $JAR $scenario 16 +[ $# -ge 1 ] && shift
17 +
18 +[ -t 1 ] && stcColor=true || unset stcColor
19 +
20 +java -jar $JAR $scenario "$@"
......
...@@ -15,9 +15,11 @@ ...@@ -15,9 +15,11 @@
15 */ 15 */
16 package org.onlab.stc; 16 package org.onlab.stc;
17 17
18 +import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.Sets; 19 import com.google.common.collect.Sets;
19 20
20 import java.io.File; 21 import java.io.File;
22 +import java.util.List;
21 import java.util.Set; 23 import java.util.Set;
22 import java.util.concurrent.CountDownLatch; 24 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.ExecutorService; 25 import java.util.concurrent.ExecutorService;
...@@ -36,7 +38,6 @@ public class Coordinator { ...@@ -36,7 +38,6 @@ public class Coordinator {
36 38
37 private final ExecutorService executor = newFixedThreadPool(MAX_THREADS); 39 private final ExecutorService executor = newFixedThreadPool(MAX_THREADS);
38 40
39 - private final Scenario scenario;
40 private final ProcessFlow processFlow; 41 private final ProcessFlow processFlow;
41 42
42 private final StepProcessListener delegate; 43 private final StepProcessListener delegate;
...@@ -57,7 +58,7 @@ public class Coordinator { ...@@ -57,7 +58,7 @@ public class Coordinator {
57 * Represents processor state. 58 * Represents processor state.
58 */ 59 */
59 public enum Status { 60 public enum Status {
60 - WAITING, IN_PROGRESS, SUCCEEDED, FAILED 61 + WAITING, IN_PROGRESS, SUCCEEDED, FAILED, SKIPPED
61 } 62 }
62 63
63 /** 64 /**
...@@ -68,7 +69,6 @@ public class Coordinator { ...@@ -68,7 +69,6 @@ public class Coordinator {
68 * @param logDir scenario log directory 69 * @param logDir scenario log directory
69 */ 70 */
70 public Coordinator(Scenario scenario, ProcessFlow processFlow, File logDir) { 71 public Coordinator(Scenario scenario, ProcessFlow processFlow, File logDir) {
71 - this.scenario = scenario;
72 this.processFlow = processFlow; 72 this.processFlow = processFlow;
73 this.logDir = logDir; 73 this.logDir = logDir;
74 this.store = new ScenarioStore(processFlow, logDir, scenario.name()); 74 this.store = new ScenarioStore(processFlow, logDir, scenario.name());
...@@ -77,6 +77,46 @@ public class Coordinator { ...@@ -77,6 +77,46 @@ public class Coordinator {
77 } 77 }
78 78
79 /** 79 /**
80 + * Resets any previously accrued status and events.
81 + */
82 + public void reset() {
83 + store.reset();
84 + }
85 +
86 + /**
87 + * Resets all previously accrued status and events for steps that lie
88 + * in the range between the steps or groups whose names match the specified
89 + * patterns.
90 + *
91 + * @param runFromPatterns list of starting step patterns
92 + * @param runToPatterns list of ending step patterns
93 + */
94 + public void reset(List<String> runFromPatterns, List<String> runToPatterns) {
95 + List<Step> fromSteps = matchSteps(runFromPatterns);
96 + List<Step> toSteps = matchSteps(runToPatterns);
97 +
98 + // FIXME: implement this
99 + }
100 +
101 + /**
102 + * Returns a list of steps that match the specified list of patterns.
103 + *
104 + * @param runToPatterns list of patterns
105 + * @return list of steps with matching names
106 + */
107 + private List<Step> matchSteps(List<String> runToPatterns) {
108 + ImmutableList.Builder<Step> builder = ImmutableList.builder();
109 + store.getSteps().forEach(step -> {
110 + runToPatterns.forEach(p -> {
111 + if (step.name().matches(p)) {
112 + builder.add(step);
113 + }
114 + });
115 + });
116 + return builder.build();
117 + }
118 +
119 + /**
80 * Starts execution of the process flow graph. 120 * Starts execution of the process flow graph.
81 */ 121 */
82 public void start() { 122 public void start() {
...@@ -104,10 +144,19 @@ public class Coordinator { ...@@ -104,10 +144,19 @@ public class Coordinator {
104 } 144 }
105 145
106 /** 146 /**
107 - * Returns the status of the specified test step. 147 + * Returns a chronological list of step or group records.
148 + *
149 + * @return list of events
150 + */
151 + List<StepEvent> getRecords() {
152 + return store.getEvents();
153 + }
154 +
155 + /**
156 + * Returns the status record of the specified test step.
108 * 157 *
109 * @param step test step or group 158 * @param step test step or group
110 - * @return step status 159 + * @return step status record
111 */ 160 */
112 public Status getStatus(Step step) { 161 public Status getStatus(Step step) {
113 return store.getStatus(step); 162 return store.getStatus(step);
...@@ -138,6 +187,7 @@ public class Coordinator { ...@@ -138,6 +187,7 @@ public class Coordinator {
138 * @param group optional group 187 * @param group optional group
139 */ 188 */
140 private void executeRoots(Group group) { 189 private void executeRoots(Group group) {
190 + // FIXME: add ability to skip past completed steps
141 Set<Step> steps = 191 Set<Step> steps =
142 group != null ? group.children() : processFlow.getVertexes(); 192 group != null ? group.children() : processFlow.getVertexes();
143 steps.forEach(step -> { 193 steps.forEach(step -> {
...@@ -155,7 +205,7 @@ public class Coordinator { ...@@ -155,7 +205,7 @@ public class Coordinator {
155 private synchronized void execute(Step step) { 205 private synchronized void execute(Step step) {
156 Directive directive = nextAction(step); 206 Directive directive = nextAction(step);
157 if (directive == RUN || directive == SKIP) { 207 if (directive == RUN || directive == SKIP) {
158 - store.updateStatus(step, IN_PROGRESS); 208 + store.markStarted(step);
159 if (step instanceof Group) { 209 if (step instanceof Group) {
160 Group group = (Group) step; 210 Group group = (Group) step;
161 delegate.onStart(group); 211 delegate.onStart(group);
...@@ -247,7 +297,7 @@ public class Coordinator { ...@@ -247,7 +297,7 @@ public class Coordinator {
247 297
248 @Override 298 @Override
249 public void onCompletion(Step step, int exitCode) { 299 public void onCompletion(Step step, int exitCode) {
250 - store.updateStatus(step, exitCode == 0 ? SUCCEEDED : FAILED); 300 + store.markComplete(step, exitCode == 0 ? SUCCEEDED : FAILED);
251 listeners.forEach(listener -> listener.onCompletion(step, exitCode)); 301 listeners.forEach(listener -> listener.onCompletion(step, exitCode));
252 executeSucessors(step); 302 executeSucessors(step);
253 latch.countDown(); 303 latch.countDown();
......
...@@ -15,11 +15,18 @@ ...@@ -15,11 +15,18 @@
15 */ 15 */
16 package org.onlab.stc; 16 package org.onlab.stc;
17 17
18 +import com.google.common.collect.ImmutableList;
19 +import org.onlab.stc.Coordinator.Status;
20 +
18 import java.io.FileInputStream; 21 import java.io.FileInputStream;
19 import java.io.FileNotFoundException; 22 import java.io.FileNotFoundException;
20 import java.text.SimpleDateFormat; 23 import java.text.SimpleDateFormat;
21 import java.util.Date; 24 import java.util.Date;
25 +import java.util.List;
26 +import java.util.Objects;
22 27
28 +import static java.lang.System.currentTimeMillis;
29 +import static org.onlab.stc.Coordinator.Status.*;
23 import static org.onlab.stc.Coordinator.print; 30 import static org.onlab.stc.Coordinator.print;
24 31
25 /** 32 /**
...@@ -27,30 +34,53 @@ import static org.onlab.stc.Coordinator.print; ...@@ -27,30 +34,53 @@ import static org.onlab.stc.Coordinator.print;
27 */ 34 */
28 public final class Main { 35 public final class Main {
29 36
37 + private static final String NONE = "\u001B[0m";
38 + private static final String GRAY = "\u001B[30;1m";
39 + private static final String RED = "\u001B[31;1m";
40 + private static final String GREEN = "\u001B[32;1m";
41 + private static final String BLUE = "\u001B[36m";
42 +
30 private enum Command { 43 private enum Command {
31 - LIST, RUN, RUN_FROM, RUN_TO 44 + LIST, RUN, RUN_RANGE, HELP
32 } 45 }
33 46
34 - private final String[] args;
35 - private final Command command;
36 private final String scenarioFile; 47 private final String scenarioFile;
37 48
38 - private Scenario scenario; 49 + private Command command = Command.HELP;
50 + private String runFromPatterns = "";
51 + private String runToPatterns = "";
52 +
39 private Coordinator coordinator; 53 private Coordinator coordinator;
40 private Listener delegate = new Listener(); 54 private Listener delegate = new Listener();
41 55
56 + private static boolean useColor = Objects.equals("true", System.getenv("stcColor"));
57 +
58 + // usage: stc [<scenario-file>] [run]
59 + // usage: stc [<scenario-file>] run [from <from-patterns>] [to <to-patterns>]]
60 + // usage: stc [<scenario-file>] list
61 +
42 // Public construction forbidden 62 // Public construction forbidden
43 private Main(String[] args) { 63 private Main(String[] args) {
44 - this.args = args;
45 this.scenarioFile = args[0]; 64 this.scenarioFile = args[0];
46 - this.command = Command.valueOf("RUN"); 65 +
66 + if (args.length <= 1 || args.length == 2 && args[1].equals("run")) {
67 + command = Command.RUN;
68 + } else if (args.length == 2 && args[1].equals("list")) {
69 + command = Command.LIST;
70 + } else if (args.length >= 4 && args[1].equals("run")) {
71 + int i = 2;
72 + if (args[i].equals("from")) {
73 + command = Command.RUN_RANGE;
74 + runFromPatterns = args[i + 1];
75 + i += 2;
47 } 76 }
48 77
49 - // usage: stc [<command>] [<scenario-file>] 78 + if (args.length >= i + 2 && args[i].equals("to")) {
50 - // --list 79 + command = Command.RUN_RANGE;
51 - // [--run] 80 + runToPatterns = args[i + 1];
52 - // --run-from <step>,... 81 + }
53 - // --run-to <step>,... 82 + }
83 + }
54 84
55 /** 85 /**
56 * Main entry point for coordinating test scenario execution. 86 * Main entry point for coordinating test scenario execution.
...@@ -62,10 +92,11 @@ public final class Main { ...@@ -62,10 +92,11 @@ public final class Main {
62 main.run(); 92 main.run();
63 } 93 }
64 94
95 + // Runs the scenario processing
65 private void run() { 96 private void run() {
66 try { 97 try {
67 // Load scenario 98 // Load scenario
68 - scenario = Scenario.loadScenario(new FileInputStream(scenarioFile)); 99 + Scenario scenario = Scenario.loadScenario(new FileInputStream(scenarioFile));
69 100
70 // Elaborate scenario 101 // Elaborate scenario
71 Compiler compiler = new Compiler(scenario); 102 Compiler compiler = new Compiler(scenario);
...@@ -82,17 +113,27 @@ public final class Main { ...@@ -82,17 +113,27 @@ public final class Main {
82 } 113 }
83 } 114 }
84 115
116 + // Processes the appropriate command
85 private void processCommand() { 117 private void processCommand() {
86 switch (command) { 118 switch (command) {
87 case RUN: 119 case RUN:
88 processRun(); 120 processRun();
121 + break;
122 + case LIST:
123 + processList();
124 + break;
125 + case RUN_RANGE:
126 + processRunRange();
127 + break;
89 default: 128 default:
90 - print("Unsupported command"); 129 + print("Unsupported command %s", command);
91 } 130 }
92 } 131 }
93 132
133 + // Processes the scenario 'run' command.
94 private void processRun() { 134 private void processRun() {
95 try { 135 try {
136 + coordinator.reset();
96 coordinator.start(); 137 coordinator.start();
97 int exitCode = coordinator.waitFor(); 138 int exitCode = coordinator.waitFor();
98 pause(100); // allow stdout to flush 139 pause(100); // allow stdout to flush
...@@ -102,38 +143,85 @@ public final class Main { ...@@ -102,38 +143,85 @@ public final class Main {
102 } 143 }
103 } 144 }
104 145
105 - private void pause(int ms) { 146 + // Processes the scenario 'list' command.
147 + private void processList() {
148 + coordinator.getRecords()
149 + .forEach(event -> logStatus(event.time(), event.name(), event.status()));
150 + }
151 +
152 + // Processes the scenario 'run' command for range of steps.
153 + private void processRunRange() {
106 try { 154 try {
107 - Thread.sleep(ms); 155 + coordinator.reset(list(runFromPatterns), list(runToPatterns));
156 + coordinator.start();
157 + int exitCode = coordinator.waitFor();
158 + pause(100); // allow stdout to flush
159 + System.exit(exitCode);
108 } catch (InterruptedException e) { 160 } catch (InterruptedException e) {
109 - print("Interrupted!"); 161 + print("Unable to execute scenario %s", scenarioFile);
110 } 162 }
111 } 163 }
112 164
113 -
114 /** 165 /**
115 * Internal delegate to monitor the process execution. 166 * Internal delegate to monitor the process execution.
116 */ 167 */
117 - private class Listener implements StepProcessListener { 168 + private static class Listener implements StepProcessListener {
118 -
119 @Override 169 @Override
120 public void onStart(Step step) { 170 public void onStart(Step step) {
121 - print("%s %s started", now(), step.name()); 171 + logStatus(currentTimeMillis(), step.name(), IN_PROGRESS);
122 } 172 }
123 173
124 @Override 174 @Override
125 public void onCompletion(Step step, int exitCode) { 175 public void onCompletion(Step step, int exitCode) {
126 - print("%s %s %s", now(), step.name(), exitCode == 0 ? "completed" : "failed"); 176 + logStatus(currentTimeMillis(), step.name(), exitCode == 0 ? SUCCEEDED : FAILED);
127 } 177 }
128 178
129 @Override 179 @Override
130 public void onOutput(Step step, String line) { 180 public void onOutput(Step step, String line) {
131 } 181 }
182 + }
183 +
184 + // Logs the step status.
185 + private static void logStatus(long time, String name, Status status) {
186 + print("%s %s%s %s%s", time(time), color(status), name, action(status), color(null));
187 + }
132 188
189 + // Produces a description of event using the specified step status.
190 + private static String action(Status status) {
191 + return status == IN_PROGRESS ? "started" :
192 + (status == SUCCEEDED ? "completed" :
193 + (status == FAILED ? "failed" :
194 + (status == SKIPPED ? "skipped" : "waiting")));
133 } 195 }
134 196
135 - private String now() { 197 + // Produces an ANSI escape code for color using the specified step status.
136 - return new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date()); 198 + private static String color(Status status) {
199 + if (!useColor) {
200 + return "";
201 + }
202 + return status == null ? NONE :
203 + (status == IN_PROGRESS ? BLUE :
204 + (status == SUCCEEDED ? GREEN :
205 + (status == FAILED ? RED : GRAY)));
206 + }
207 +
208 + // Produces a list from the specified comma-separated string.
209 + private static List<String> list(String patterns) {
210 + return ImmutableList.copyOf(patterns.split(","));
211 + }
212 +
213 + // Produces a formatted time stamp.
214 + private static String time(long time) {
215 + return new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date(time));
216 + }
217 +
218 + // Pauses for the specified number of millis.
219 + private static void pause(int ms) {
220 + try {
221 + Thread.sleep(ms);
222 + } catch (InterruptedException e) {
223 + print("Interrupted!");
224 + }
137 } 225 }
138 226
139 } 227 }
......
...@@ -15,18 +15,20 @@ ...@@ -15,18 +15,20 @@
15 */ 15 */
16 package org.onlab.stc; 16 package org.onlab.stc;
17 17
18 +import com.google.common.collect.ImmutableList;
19 +import com.google.common.collect.Lists;
18 import com.google.common.collect.Maps; 20 import com.google.common.collect.Maps;
19 import org.apache.commons.configuration.ConfigurationException; 21 import org.apache.commons.configuration.ConfigurationException;
20 import org.apache.commons.configuration.PropertiesConfiguration; 22 import org.apache.commons.configuration.PropertiesConfiguration;
21 import org.onlab.stc.Coordinator.Status; 23 import org.onlab.stc.Coordinator.Status;
22 24
23 import java.io.File; 25 import java.io.File;
26 +import java.util.List;
24 import java.util.Map; 27 import java.util.Map;
25 import java.util.Set; 28 import java.util.Set;
26 29
27 import static com.google.common.base.Preconditions.checkNotNull; 30 import static com.google.common.base.Preconditions.checkNotNull;
28 -import static org.onlab.stc.Coordinator.Status.FAILED; 31 +import static org.onlab.stc.Coordinator.Status.*;
29 -import static org.onlab.stc.Coordinator.Status.WAITING;
30 import static org.onlab.stc.Coordinator.print; 32 import static org.onlab.stc.Coordinator.print;
31 33
32 /** 34 /**
...@@ -37,7 +39,8 @@ class ScenarioStore { ...@@ -37,7 +39,8 @@ class ScenarioStore {
37 private final ProcessFlow processFlow; 39 private final ProcessFlow processFlow;
38 private final File storeFile; 40 private final File storeFile;
39 41
40 - private final Map<Step, Status> stepStatus = Maps.newConcurrentMap(); 42 + private final List<StepEvent> events = Lists.newArrayList();
43 + private final Map<String, Status> statusMap = Maps.newConcurrentMap();
41 44
42 /** 45 /**
43 * Creates a new scenario store for the specified process flow. 46 * Creates a new scenario store for the specified process flow.
...@@ -49,9 +52,25 @@ class ScenarioStore { ...@@ -49,9 +52,25 @@ class ScenarioStore {
49 ScenarioStore(ProcessFlow processFlow, File logDir, String name) { 52 ScenarioStore(ProcessFlow processFlow, File logDir, String name) {
50 this.processFlow = processFlow; 53 this.processFlow = processFlow;
51 this.storeFile = new File(logDir, name + ".stc"); 54 this.storeFile = new File(logDir, name + ".stc");
52 - processFlow.getVertexes().forEach(step -> stepStatus.put(step, WAITING)); 55 + load();
53 } 56 }
54 57
58 + /**
59 + * Resets status of all steps to waiting and clears all events.
60 + */
61 + void reset() {
62 + events.clear();
63 + statusMap.clear();
64 + processFlow.getVertexes().forEach(step -> statusMap.put(step.name(), WAITING));
65 + try {
66 + PropertiesConfiguration cfg = new PropertiesConfiguration(storeFile);
67 + cfg.clear();
68 + cfg.save();
69 + } catch (ConfigurationException e) {
70 + print("Unable to store file %s", storeFile);
71 + }
72 +
73 + }
55 74
56 /** 75 /**
57 * Returns set of all test steps. 76 * Returns set of all test steps.
...@@ -63,23 +82,42 @@ class ScenarioStore { ...@@ -63,23 +82,42 @@ class ScenarioStore {
63 } 82 }
64 83
65 /** 84 /**
66 - * Returns the status of the specified test step. 85 + * Returns a chronological list of step or group records.
86 + *
87 + * @return list of events
88 + */
89 + synchronized List<StepEvent> getEvents() {
90 + return ImmutableList.copyOf(events);
91 + }
92 +
93 + /**
94 + * Returns the status record of the specified test step.
67 * 95 *
68 * @param step test step or group 96 * @param step test step or group
69 - * @return step status 97 + * @return step status record
70 */ 98 */
71 Status getStatus(Step step) { 99 Status getStatus(Step step) {
72 - return checkNotNull(stepStatus.get(step), "Step %s not found", step.name()); 100 + return checkNotNull(statusMap.get(step.name()), "Step %s not found", step.name());
73 } 101 }
74 102
75 /** 103 /**
76 - * Updates the status of the specified test step. 104 + * Marks the specified test step as being in progress.
105 + *
106 + * @param step test step or group
107 + */
108 + synchronized void markStarted(Step step) {
109 + add(new StepEvent(step.name(), IN_PROGRESS));
110 + save();
111 + }
112 +
113 + /**
114 + * Marks the specified test step as being complete.
77 * 115 *
78 * @param step test step or group 116 * @param step test step or group
79 * @param status new step status 117 * @param status new step status
80 */ 118 */
81 - void updateStatus(Step step, Status status) { 119 + synchronized void markComplete(Step step, Status status) {
82 - stepStatus.put(step, status); 120 + add(new StepEvent(step.name(), status));
83 save(); 121 save();
84 } 122 }
85 123
...@@ -89,7 +127,7 @@ class ScenarioStore { ...@@ -89,7 +127,7 @@ class ScenarioStore {
89 * @return true if there are failed steps 127 * @return true if there are failed steps
90 */ 128 */
91 boolean hasFailures() { 129 boolean hasFailures() {
92 - for (Status status : stepStatus.values()) { 130 + for (Status status : statusMap.values()) {
93 if (status == FAILED) { 131 if (status == FAILED) {
94 return true; 132 return true;
95 } 133 }
...@@ -98,10 +136,26 @@ class ScenarioStore { ...@@ -98,10 +136,26 @@ class ScenarioStore {
98 } 136 }
99 137
100 /** 138 /**
139 + * Registers a new step record.
140 + *
141 + * @param event step event
142 + */
143 + private synchronized void add(StepEvent event) {
144 + events.add(event);
145 + statusMap.put(event.name(), event.status());
146 + }
147 +
148 + /**
101 * Loads the states from disk. 149 * Loads the states from disk.
102 */ 150 */
103 private void load() { 151 private void load() {
104 - // FIXME: implement this 152 + try {
153 + PropertiesConfiguration cfg = new PropertiesConfiguration(storeFile);
154 + cfg.getKeys().forEachRemaining(prop -> add(StepEvent.fromString(cfg.getString(prop))));
155 + cfg.save();
156 + } catch (ConfigurationException e) {
157 + print("Unable to store file %s", storeFile);
158 + }
105 } 159 }
106 160
107 /** 161 /**
...@@ -110,7 +164,7 @@ class ScenarioStore { ...@@ -110,7 +164,7 @@ class ScenarioStore {
110 private void save() { 164 private void save() {
111 try { 165 try {
112 PropertiesConfiguration cfg = new PropertiesConfiguration(storeFile); 166 PropertiesConfiguration cfg = new PropertiesConfiguration(storeFile);
113 - stepStatus.forEach((step, status) -> cfg.setProperty(step.name(), status)); 167 + events.forEach(event -> cfg.setProperty("T" + event.time(), event.toString()));
114 cfg.save(); 168 cfg.save();
115 } catch (ConfigurationException e) { 169 } catch (ConfigurationException e) {
116 print("Unable to store file %s", storeFile); 170 print("Unable to store file %s", storeFile);
......
1 +/*
2 + * Copyright 2015 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +package org.onlab.stc;
17 +
18 +import org.onlab.stc.Coordinator.Status;
19 +
20 +import static java.lang.Long.parseLong;
21 +
22 +/**
23 + * Represents an event of execution of a scenario step or group.
24 + */
25 +public class StepEvent {
26 +
27 + private final String name;
28 + private final long time;
29 + private final Status status;
30 +
31 + /**
32 + * Creates a new step record.
33 + *
34 + * @param name test step or group name
35 + * @param time time in millis since start of epoch
36 + * @param status step completion status
37 + */
38 + public StepEvent(String name, long time, Status status) {
39 + this.name = name;
40 + this.time = time;
41 + this.status = status;
42 + }
43 +
44 + /**
45 + * Creates a new step record for non-running status.
46 + *
47 + * @param name test step or group name
48 + * @param status status
49 + */
50 + public StepEvent(String name, Status status) {
51 + this(name, System.currentTimeMillis(), status);
52 + }
53 +
54 + /**
55 + * Returns the test step or test group name.
56 + *
57 + * @return step or group name
58 + */
59 + public String name() {
60 + return name;
61 + }
62 +
63 + /**
64 + * Returns the step event time.
65 + *
66 + * @return time in millis since start of epoch
67 + */
68 + public long time() {
69 + return time;
70 + }
71 +
72 + /**
73 + * Returns the step completion status.
74 + *
75 + * @return completion status
76 + */
77 + public Status status() {
78 + return status;
79 + }
80 +
81 +
82 + @Override
83 + public String toString() {
84 + return name + ":" + time + ":" + status;
85 + }
86 +
87 + /**
88 + * Returns a record parsed from the specified string.
89 + *
90 + * @param string string encoding
91 + * @return step record
92 + */
93 + public static StepEvent fromString(String string) {
94 + String[] fields = string.split(":");
95 + return new StepEvent(fields[0], parseLong(fields[1]), Status.valueOf(fields[2]));
96 + }
97 +}
...@@ -55,6 +55,7 @@ public class CoordinatorTest { ...@@ -55,6 +55,7 @@ public class CoordinatorTest {
55 compiler.compile(); 55 compiler.compile();
56 coordinator = new Coordinator(scenario, compiler.processFlow(), compiler.logDir()); 56 coordinator = new Coordinator(scenario, compiler.processFlow(), compiler.logDir());
57 coordinator.addListener(listener); 57 coordinator.addListener(listener);
58 + coordinator.reset();
58 coordinator.start(); 59 coordinator.start();
59 coordinator.waitFor(); 60 coordinator.waitFor();
60 coordinator.removeListener(listener); 61 coordinator.removeListener(listener);
......