Skip to content

Commit

Permalink
Support CommandGroups in CommandTester, closes strongback#111.
Browse files Browse the repository at this point in the history
  • Loading branch information
rothso committed Jun 14, 2017
1 parent aeb27dc commit 6b8ebf7
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class CommandTester {
private final CommandRunner runner;

public CommandTester(Command command) {
runner = new CommandRunner(command);
runner = CommandRunner.create(command);
}

/**
Expand Down
21 changes: 17 additions & 4 deletions strongback-tests/src/org/strongback/command/CommandRunnerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -68,15 +68,15 @@ 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);
}

@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);
Expand All @@ -93,14 +93,27 @@ 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
assertExecutedAtLeast(watched,2);
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();
Expand Down
27 changes: 27 additions & 0 deletions strongback-tests/src/org/strongback/command/CommandTesterTest.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
40 changes: 32 additions & 8 deletions strongback/src/org/strongback/command/CommandRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,25 +62,49 @@ 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;
this.timeoutInMillis = (long) (command.getTimeoutInSeconds() * 1000);
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;
Expand Down
28 changes: 1 addition & 27 deletions strongback/src/org/strongback/command/Scheduler.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
*
Expand Down

0 comments on commit 6b8ebf7

Please sign in to comment.