From 84f0f216b4643601e4b8760d190b087bbce98bd4 Mon Sep 17 00:00:00 2001 From: Carlo Zancanaro Date: Sat, 6 Jun 2015 16:40:04 +1000 Subject: Lots of work on the stateful checking stuff: it's a fair bit nicer now --- .../java/au/id/zancanaro/javacheck/Generators.java | 3 +- .../au/id/zancanaro/javacheck/state/Command.java | 18 +++++--- .../id/zancanaro/javacheck/state/CommandList.java | 51 +++++++++++----------- .../javacheck/state/CommandListGenerator.java | 22 +++++----- .../zancanaro/javacheck/state/CommandResult.java | 12 ++--- .../id/zancanaro/javacheck/state/CommandValue.java | 22 +++++++++- .../javacheck/state/GeneratedCommand.java | 12 ++--- 7 files changed, 83 insertions(+), 57 deletions(-) (limited to 'src/main/java/au/id') diff --git a/src/main/java/au/id/zancanaro/javacheck/Generators.java b/src/main/java/au/id/zancanaro/javacheck/Generators.java index 619fe2d..f4ac025 100644 --- a/src/main/java/au/id/zancanaro/javacheck/Generators.java +++ b/src/main/java/au/id/zancanaro/javacheck/Generators.java @@ -40,7 +40,8 @@ public final class Generators { return integer(0, gens.length).flatMap(index -> gens[index].map(x -> (T) x)); } - public static Generator elements(T[] elements) { + @SafeVarargs + public static Generator elements(T... elements) { return elements(Arrays.asList(elements)); } diff --git a/src/main/java/au/id/zancanaro/javacheck/state/Command.java b/src/main/java/au/id/zancanaro/javacheck/state/Command.java index afa3957..78df8f1 100644 --- a/src/main/java/au/id/zancanaro/javacheck/state/Command.java +++ b/src/main/java/au/id/zancanaro/javacheck/state/Command.java @@ -2,22 +2,26 @@ package au.id.zancanaro.javacheck.state; import au.id.zancanaro.javacheck.Generator; -public abstract class Command { - public Generator argsGenerator(State state) { +public abstract class Command { + public Generator argsGenerator(S state) { return Generator.pure(null); } - public boolean preCondition(State state, Args args) { + public boolean preCondition(S state, A args) { return true; } - public abstract Result runCommand(Args args); + public abstract R runCommand(S state, A args) throws Throwable; - public State nextState(State state, Args args, CommandValue result) { + public S nextState(S state, A args, CommandValue result) { return state; } - public boolean postCondition(State oldState, State newState, Args args, Result result) { - return true; + public void postCondition(S oldState, S newState, A args, R result) throws Throwable { + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); } } diff --git a/src/main/java/au/id/zancanaro/javacheck/state/CommandList.java b/src/main/java/au/id/zancanaro/javacheck/state/CommandList.java index 0426fd3..05000f2 100644 --- a/src/main/java/au/id/zancanaro/javacheck/state/CommandList.java +++ b/src/main/java/au/id/zancanaro/javacheck/state/CommandList.java @@ -2,17 +2,17 @@ package au.id.zancanaro.javacheck.state; import java.util.*; -public class CommandList { - private final List> commands; +public class CommandList { + private final List> commands; - public CommandList(List> commands) { + public CommandList(List> commands) { this.commands = new ArrayList<>(commands); } - public CommandResult run(State initialState) { + public CommandResult run(S initialState) { Map values = new HashMap<>(); - CommandResult result = CommandResult.success(initialState); - for (GeneratedCommand generated : commands) { + CommandResult result = CommandResult.success(initialState); + for (GeneratedCommand generated : commands) { result = runRealCommand(generated, result.getState(), values); if (result.isFailed()) { break; @@ -21,26 +21,27 @@ public class CommandList { return result; } - private static CommandResult runRealCommand( - GeneratedCommand generated, - State state, + private static CommandResult runRealCommand( + GeneratedCommand generated, + S state, Map values) { int id = generated.getId(); - Command command = generated.getCommand(); - Args args = generated.getArgs(); + Command command = generated.getCommand(); + A args = generated.getArgs(); try { if (!command.preCondition(state, args)) { return CommandResult.fail(state, new Error("Precondition failed")); } - Result result = CommandValue.withValues(values, () -> - command.runCommand(args)); + final S oldState = state; + R result = CommandValue.withValues(values, () -> command.runCommand(oldState, args)); values.put(id, result); - final State oldState = state; - final State newState = CommandValue.withValues(values, () -> + final S newState = CommandValue.withValues(values, () -> command.nextState(oldState, args, new CommandValue<>(id))); state = newState; - if (!CommandValue.withValues(values, () -> command.postCondition(oldState, newState, args, result))) { - return CommandResult.fail(state, new Error("Postcondition failed")); + try { + CommandValue.withValues(values, () -> command.postCondition(oldState, newState, args, result)); + } catch (Throwable ex) { + return CommandResult.fail(state, ex); } return CommandResult.success(state); } catch (Throwable ex) { @@ -48,12 +49,12 @@ public class CommandList { } } - private static CommandResult runAbstractCommand( - GeneratedCommand generated, - State state) { + private static CommandResult runAbstractCommand( + GeneratedCommand generated, + S state) { int id = generated.getId(); - Command command = generated.getCommand(); - Args args = generated.getArgs(); + Command command = generated.getCommand(); + A args = generated.getArgs(); try { if (!command.preCondition(state, args)) { return CommandResult.fail(state, new Error("Precondition failed")); @@ -66,8 +67,8 @@ public class CommandList { } public boolean isValid() { - CommandResult result = CommandResult.success(null); - for (GeneratedCommand generated : commands) { + CommandResult result = CommandResult.success(null); + for (GeneratedCommand generated : commands) { result = runAbstractCommand(generated, result.getState()); if (result.isFailed()) { break; @@ -79,7 +80,7 @@ public class CommandList { @Override public String toString() { StringBuilder builder = new StringBuilder(); - Iterator> iterator = commands.iterator(); + Iterator> iterator = commands.iterator(); while (iterator.hasNext()) { builder.append(iterator.next()); if (iterator.hasNext()) { diff --git a/src/main/java/au/id/zancanaro/javacheck/state/CommandListGenerator.java b/src/main/java/au/id/zancanaro/javacheck/state/CommandListGenerator.java index 4c2c47f..1600f07 100644 --- a/src/main/java/au/id/zancanaro/javacheck/state/CommandListGenerator.java +++ b/src/main/java/au/id/zancanaro/javacheck/state/CommandListGenerator.java @@ -9,38 +9,40 @@ import java.util.function.Function; import static au.id.zancanaro.javacheck.Generator.pure; import static au.id.zancanaro.javacheck.Generators.noShrink; -public class CommandListGenerator implements Generator> { - private final Function>> generateCommand; +public class CommandListGenerator implements Generator> { + private final S initialState; + private final Function>> generateCommand; - public CommandListGenerator(Function>> generateCommand) { + public CommandListGenerator(S initialState, Function>> generateCommand) { + this.initialState = initialState; this.generateCommand = generateCommand; } - public Generator> commandGenerator(int id, State state) { + public Generator> commandGenerator(int id, S state) { return noShrink(generateCommand.apply(state)) .flatMap(command -> generateSingleCommand(id, command, state)); } - public Generator> generateSingleCommand(int id, Command command, State state) { + public Generator> generateSingleCommand(int id, Command command, S state) { return command.argsGenerator(state).flatMap(generatedArgs -> command.preCondition(state, generatedArgs) ? pure(new GeneratedCommand<>(id, command, generatedArgs)) : commandGenerator(id, state)); } - public State nextState(int id, GeneratedCommand generatedCommand, State state) { + public S nextState(int id, GeneratedCommand generatedCommand, S state) { return generatedCommand.getCommand().nextState(state, generatedCommand.getArgs(), new CommandValue<>(id)); } @Override - public ShrinkTree> generate(Random random, int size) { + public ShrinkTree> generate(Random random, int size) { int count = random.nextInt(size); @SuppressWarnings("unchecked") - ShrinkTree>[] commandTrees = (ShrinkTree>[]) new ShrinkTree[count]; - State state = null; + ShrinkTree>[] commandTrees = (ShrinkTree>[]) new ShrinkTree[count]; + S state = initialState; for (int i = 0; i < count; ++i) { commandTrees[i] = commandGenerator(i, state).generate(random, size); - GeneratedCommand generatedCommand = commandTrees[i].getValue(); + GeneratedCommand generatedCommand = commandTrees[i].getValue(); state = nextState(i, generatedCommand, state); } return ShrinkTree.combine(commandTrees, ShrinkTree::removeAndPromoteChildren) diff --git a/src/main/java/au/id/zancanaro/javacheck/state/CommandResult.java b/src/main/java/au/id/zancanaro/javacheck/state/CommandResult.java index 12f650d..cc2f5ba 100644 --- a/src/main/java/au/id/zancanaro/javacheck/state/CommandResult.java +++ b/src/main/java/au/id/zancanaro/javacheck/state/CommandResult.java @@ -1,15 +1,15 @@ package au.id.zancanaro.javacheck.state; -public class CommandResult { - private final State state; +public class CommandResult { + private final S state; private final Throwable thrown; - private CommandResult(State state, Throwable thrown) { + private CommandResult(S state, Throwable thrown) { this.state = state; this.thrown = thrown; } - public State getState() { + public S getState() { return state; } @@ -21,11 +21,11 @@ public class CommandResult { return thrown; } - public static CommandResult success(State state) { + public static CommandResult success(S state) { return new CommandResult<>(state, null); } - public static CommandResult fail(State state, Throwable ex) { + public static CommandResult fail(S state, Throwable ex) { return new CommandResult<>(state, ex); } } diff --git a/src/main/java/au/id/zancanaro/javacheck/state/CommandValue.java b/src/main/java/au/id/zancanaro/javacheck/state/CommandValue.java index 8d3f272..8584fc7 100644 --- a/src/main/java/au/id/zancanaro/javacheck/state/CommandValue.java +++ b/src/main/java/au/id/zancanaro/javacheck/state/CommandValue.java @@ -5,13 +5,31 @@ import java.util.NoSuchElementException; import java.util.function.Supplier; public class CommandValue { + public static interface Action { + T doAction() throws Throwable; + } + + public static interface VoidAction { + void doAction() throws Throwable; + } + private static Map values = null; - public static T withValues(Map newValues, Supplier action) { + public static T withValues(Map newValues, Action action) throws Throwable { + Map oldValues = values; + try { + values = newValues; + return action.doAction(); + } finally { + values = oldValues; + } + } + + public static void withValues(Map newValues, VoidAction action) throws Throwable { Map oldValues = values; try { values = newValues; - return action.get(); + action.doAction(); } finally { values = oldValues; } diff --git a/src/main/java/au/id/zancanaro/javacheck/state/GeneratedCommand.java b/src/main/java/au/id/zancanaro/javacheck/state/GeneratedCommand.java index 90d9a47..23f306b 100644 --- a/src/main/java/au/id/zancanaro/javacheck/state/GeneratedCommand.java +++ b/src/main/java/au/id/zancanaro/javacheck/state/GeneratedCommand.java @@ -1,11 +1,11 @@ package au.id.zancanaro.javacheck.state; -public class GeneratedCommand { +public class GeneratedCommand { private final int id; - private final Command command; - private final Args args; + private final Command command; + private final A args; - public GeneratedCommand(int id, Command command, Args args) { + public GeneratedCommand(int id, Command command, A args) { this.id = id; this.command = command; this.args = args; @@ -15,11 +15,11 @@ public class GeneratedCommand { return id; } - public Command getCommand() { + public Command getCommand() { return command; } - public Args getArgs() { + public A getArgs() { return args; } -- cgit v1.2.3