summaryrefslogtreecommitdiff
path: root/src/main/java/au/id/zancanaro/javacheck/state/CommandListGenerator.java
blob: 0aa693dba2dc1b644e99351030231188d8fd87e7 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package au.id.zancanaro.javacheck.state;

import au.id.zancanaro.javacheck.Generator;
import au.id.zancanaro.javacheck.ShrinkTree;

import java.util.Arrays;
import java.util.Random;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Function;
import java.util.function.IntConsumer;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import static au.id.zancanaro.javacheck.Generator.pure;
import static au.id.zancanaro.javacheck.Generators.noShrink;

public class CommandListGenerator<S> implements Generator<CommandList<S>> {
    private final S initialState;
    private final Function<S, Generator<Command<S, ?, ?>>> generateCommand;

    public CommandListGenerator(S initialState, Function<S, Generator<Command<S, ?, ?>>> generateCommand) {
        this.initialState = initialState;
        this.generateCommand = generateCommand;
    }

    public Generator<GeneratedCommand<S, ?, ?>> commandGenerator(int id, S state) {
        return noShrink(generateCommand.apply(state))
                .flatMap(command -> generateSingleCommand(id, command, state));
    }

    public <A, R> Generator<GeneratedCommand<S, ?, ?>> generateSingleCommand(int id, Command<S, A, R> command, S state) {
        return command.argsGenerator(state).flatMap(generatedArgs ->
                command.preCondition(state, generatedArgs) ?
                        pure(new GeneratedCommand<>(id, command, generatedArgs)) :
                        commandGenerator(id, state));
    }

    public <A, R> S nextState(int id, GeneratedCommand<S, A, R> generatedCommand, S state) {
        return generatedCommand.getCommand().nextState(state, generatedCommand.getArgs(), new CommandValue<>(id));
    }

    @Override
    public ShrinkTree<CommandList<S>> generate(Random random, int size) {
        int count = random.nextInt(size);
        @SuppressWarnings("unchecked")
        ShrinkTree<GeneratedCommand<S, ?, ?>>[] commandTrees = (ShrinkTree<GeneratedCommand<S, ?, ?>>[]) new ShrinkTree<?>[count];
        S state = initialState;
        for (int i = 0; i < count; ++i) {
            commandTrees[i] = commandGenerator(i, state).generate(random, size);
            GeneratedCommand<S, ?, ?> generatedCommand = commandTrees[i].getValue();
            state = nextState(i, generatedCommand, state);
        }
        return ShrinkTree.combine(commandTrees, ShrinkTree::removeAndPromoteChildren)
                .map(list -> new CommandList<>(initialState, list))
                .filter(CommandList::isValid);
    }
}