diff --git a/strongback-testing/src/org/strongback/command/CommandTester.java b/strongback-testing/src/org/strongback/command/CommandTester.java index 4d9c29d..761b890 100644 --- a/strongback-testing/src/org/strongback/command/CommandTester.java +++ b/strongback-testing/src/org/strongback/command/CommandTester.java @@ -24,7 +24,7 @@ public class CommandTester { private final CommandRunner runner; public CommandTester(Command command) { - runner = new CommandRunner(command); + runner = CommandRunner.create(command); } /** diff --git a/strongback-tests/src/org/strongback/command/CommandRunnerTest.java b/strongback-tests/src/org/strongback/command/CommandRunnerTest.java index fc972eb..858a2bb 100644 --- a/strongback-tests/src/org/strongback/command/CommandRunnerTest.java +++ b/strongback-tests/src/org/strongback/command/CommandRunnerTest.java @@ -44,7 +44,7 @@ public Logger logger() { @Test public void shouldRunCommandWithTimeout() { WatchedCommand watched = WatchedCommand.watch(Command.pause(1000,TimeUnit.MILLISECONDS)); - CommandRunner runner = new CommandRunner(watched); + CommandRunner runner = CommandRunner.create(watched); assertThat(runner.step(INITIAL_TIME)).isFalse(); assertThat(runner.state()).isEqualTo(CommandState.RUNNING); assertIncomplete(watched); @@ -68,7 +68,7 @@ public boolean execute() { return false; } }); - CommandRunner runner = new CommandRunner(CONTEXT,watched); + CommandRunner runner = CommandRunner.create(CONTEXT,watched); assertThat(runner.step(INITIAL_TIME)).isTrue(); // completes because it is interrupted assertInterrupted(watched); } @@ -76,7 +76,7 @@ public boolean execute() { @Test public void shouldInterruptCommandThatThrowsExceptionDuringFirstExecute() { WatchedCommand watched = WatchedCommand.watch(Command.create((Runnable)()->{throw new IllegalStateException();})); - CommandRunner runner = new CommandRunner(CONTEXT,watched); + CommandRunner runner = CommandRunner.create(CONTEXT,watched); assertThat(runner.step(INITIAL_TIME)).isTrue(); // completes because it is interrupted assertExecutedAtLeast(watched,1); assertInterrupted(watched); @@ -93,7 +93,7 @@ public boolean execute() { return false; } }); - CommandRunner runner = new CommandRunner(CONTEXT,watched); + CommandRunner runner = CommandRunner.create(CONTEXT,watched); assertThat(runner.step(INITIAL_TIME)).isFalse(); // executed correctly the first time assertExecutedAtLeast(watched,1); assertThat(runner.step(INITIAL_TIME)).isTrue(); // completes because it is interrupted @@ -101,6 +101,19 @@ public boolean execute() { assertInterrupted(watched); } + @Test + public void shouldSupportCommandGroups() { + Command command = Command.pause(100, TimeUnit.MILLISECONDS); + WatchedCommand watched = WatchedCommand.watch(command); // CommandGroups don't call end(), so watch Command + CommandRunner tester = CommandRunner.create(CONTEXT, CommandGroup.runSequentially(watched)); + + tester.step(1); + assertIncomplete(watched); + + tester.step(101); + assertComplete(watched); + } + protected void assertIncomplete( WatchedCommand watched ) { assertThat(watched.isInitialized()).isTrue(); assertThat(watched.isExecuted()).isTrue(); diff --git a/strongback-tests/src/org/strongback/command/CommandTesterTest.java b/strongback-tests/src/org/strongback/command/CommandTesterTest.java new file mode 100644 index 0000000..a4b703f --- /dev/null +++ b/strongback-tests/src/org/strongback/command/CommandTesterTest.java @@ -0,0 +1,27 @@ +package org.strongback.command; + +import org.junit.Test; + +import java.util.concurrent.TimeUnit; + +import static org.fest.assertions.Assertions.assertThat; + +/** + * @author Rothanak So + */ +public class CommandTesterTest { + + @Test + public void shouldSupportCommandGroups() { + // Regression test for issue #111: https://github.com/strongback/strongback-java/issues/111 + Command command = Command.pause(100, TimeUnit.MILLISECONDS); + WatchedCommand watched = WatchedCommand.watch(command); // CommandGroups don't call end(), so watch Command + CommandTester tester = new CommandTester(CommandGroup.runSequentially(watched)); + + tester.step(1); + assertThat(watched.isEnded()).isFalse(); + + tester.step(101); + assertThat(watched.isEnded()).isTrue(); + } +} diff --git a/strongback/src/org/strongback/command/CommandRunner.java b/strongback/src/org/strongback/command/CommandRunner.java index b7bfceb..de430ee 100644 --- a/strongback/src/org/strongback/command/CommandRunner.java +++ b/strongback/src/org/strongback/command/CommandRunner.java @@ -62,17 +62,41 @@ public Logger logger() { private CommandState state = CommandState.UNINITIALIZED; private final Context context; - CommandRunner(Command command) { - // Just a command and no next is a leaf - this(DEFAULT_CONTEXT, command); + static CommandRunner create(Command command) { + return create(DEFAULT_CONTEXT, command); } - CommandRunner(Context context, Command command) { - // Just a command and no next is a leaf - this(context, null, command); + static CommandRunner create(Context context, Command command) { + return buildRunner(context, command, null); } - CommandRunner(Context context, CommandRunner next, Command command) { + private static CommandRunner buildRunner(Context context, Command command, CommandRunner last) { + if (command instanceof CommandGroup) { + CommandGroup cg = (CommandGroup) command; + Command[] commands = cg.getCommands(); + switch (cg.getType()) { + case SEQUENTIAL: + for (int i = commands.length - 1; i >= 0; i--) { + last = buildRunner(context, commands[i], last); + } + return last; + case PARRALLEL: + CommandRunner[] crs = new CommandRunner[commands.length]; + for (int i = 0; i < crs.length; i++) { + crs[i] = buildRunner(context, commands[i], null); + } + return new CommandRunner(context, last, crs); + case FORK: + assert commands.length == 1; + return new CommandRunner(context, last, new CommandRunner(context, buildRunner(context, commands[0], null))); + } + // This line should never happen, the switch will throw an exception first + throw new IllegalStateException("Unexpected command type: " + cg.getType()); + } + return new CommandRunner(context, last, command); + } + + private CommandRunner(Context context, CommandRunner next, Command command) { // A command and a next is a node this.command = command; this.next = next; @@ -80,7 +104,7 @@ public Logger logger() { this.context = context != null ? context : DEFAULT_CONTEXT; } - CommandRunner(Context context, CommandRunner next, CommandRunner... commands) { + private CommandRunner(Context context, CommandRunner next, CommandRunner... commands) { // A next and several children is a branch if (commands.length != 0) this.children = commands; this.next = next; diff --git a/strongback/src/org/strongback/command/Scheduler.java b/strongback/src/org/strongback/command/Scheduler.java index 97eb838..d33091f 100644 --- a/strongback/src/org/strongback/command/Scheduler.java +++ b/strongback/src/org/strongback/command/Scheduler.java @@ -65,37 +65,11 @@ public void killAll() { */ public void submit(Command command) { if (command != null) { - CommandRunner runner = buildRunner(command, null); + CommandRunner runner = CommandRunner.create(context, command); commands.add(runner); } } - private CommandRunner buildRunner(Command command, CommandRunner last) { - if (command instanceof CommandGroup) { - CommandGroup cg = (CommandGroup) command; - Command[] commands = cg.getCommands(); - switch (cg.getType()) { - case SEQUENTIAL: - for (int i = commands.length - 1; i >= 0; i--) { - last = buildRunner(commands[i], last); - } - return last; - case PARRALLEL: - CommandRunner[] crs = new CommandRunner[commands.length]; - for (int i = 0; i < crs.length; i++) { - crs[i] = buildRunner(commands[i], null); - } - return new CommandRunner(context, last, crs); - case FORK: - assert commands.length == 1; - return new CommandRunner(context, last, new CommandRunner(context, buildRunner(commands[0], null))); - } - // This line should never happen, the switch will throw an exception first - throw new IllegalStateException("Unexpected command type: " + cg.getType()); - } - return new CommandRunner(context, last, command); - } - /** * Steps once though all of the {@link Command}s in the {@link Scheduler}. *