Enhancing STC
Change-Id: If9429cc6a30333bd27b579825fe6b1fac221cf60
Showing
6 changed files
with
339 additions
and
45 deletions
... | @@ -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"); | ||
47 | - } | ||
48 | 65 | ||
49 | - // usage: stc [<command>] [<scenario-file>] | 66 | + if (args.length <= 1 || args.length == 2 && args[1].equals("run")) { |
50 | - // --list | 67 | + command = Command.RUN; |
51 | - // [--run] | 68 | + } else if (args.length == 2 && args[1].equals("list")) { |
52 | - // --run-from <step>,... | 69 | + command = Command.LIST; |
53 | - // --run-to <step>,... | 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; | ||
76 | + } | ||
77 | + | ||
78 | + if (args.length >= i + 2 && args[i].equals("to")) { | ||
79 | + command = Command.RUN_RANGE; | ||
80 | + runToPatterns = args[i + 1]; | ||
81 | + } | ||
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); | ... | ... |
-
Please register or login to post a comment