-
-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ActionSequenceArbitrary#sequences(Arbitrary<? extends List<? extends Action<M>>>) for dependent actions #300
Comments
@vlsi Interesting use case. I'm wondering if
is really true, because at first glance it looks like the simplest way to satisfy the need. The markov chain solution also sounds interesting b/c it would be more generic. I think it deserves an issue of its own. |
That is what I tried in my workaround, however, it did not work because jqwik protects from keeping state in For now, I am looking into implementing my own Yet another issue is that I am willing to discard "incompatible" actions during the shrinking phase. |
If I remember correctly, actions will be removed during shrinking if their precondition does not hold, but I may be wrong. If I am wrong, I think this could be fixed. |
Here's an implementation of class CompositeAction<T> implements Action<T> {
private final Action<T> first;
private final Action<T> second;
public CompositeAction(Action<T> first, Action<T> second) {
this.first = first;
this.second = second;
}
@Override
public boolean precondition(T state) {
return first.precondition(state);
}
@Override
public T run(T state) {
T stateAfterFirst = first.run(state);
if (second.precondition(stateAfterFirst)) {
return second.run(stateAfterFirst);
} else {
return stateAfterFirst;
}
}
@Override
public String toString() {
return String.format("[%s then %s]", first, second);
}
} It seems to work for me, but I might have overlooked something. It's also an open point of discussion if the failure of a subsequent precondition should exclude the composite action from being considered or not. |
It terminates execution as soon as any precondition fails. |
Yes, but this code is not executed during shrinking. AFAIR repeated runs are currently only used in tests to verify that a shrunk sequence produces the expected result model. The code you quote makes sure that the same sequence (not a shrunk one) will use the same sequence of actions (or fail) on a second, third etc run. You can see how actions sequences are shrunk in: |
@vlsi Are composite actions still relevant in the context of the new stateful properties approach? If so, feel free to reopen the issue. The implementation should still be rather straightforward. |
I think the issue is still relevant. I do not think the new API provides a possibility to generate dependent chains. |
Yes it does, that was the whole point. A simple chain example to generate regular expressions: class RegexChainExample {
@Property(tries = 10)
void generateABPlusC(@ForAll("abplusc") Chain<String> regex) {
String string = null;
for (String value : regex) {
string = value;
}
System.out.println(string);
assertThat(string).matches("ab+c");
}
@Provide
Arbitrary<Chain<String>> abplusc() {
return Chain.startWith(() -> "")
.addTransformation(Transformation.when(String::isEmpty).provide(just(s -> s + "a")))
.addTransformation(Transformation.<String>when(s -> s.endsWith("a")).provide(just(s -> s + "b")))
.addTransformation(Transformation.<String>when(s -> s.endsWith("b")).provide(
frequency(
Tuple.of(5, s -> s + "b"),
Tuple.of(1, s -> s + "c")
)
))
.addTransformation(Transformation.<String>when(s -> s.endsWith("c")).provide(just(Transformer.endOfChain())))
.infinite().dontShrink();
}
}
|
I know I can use Here's a sample case from the issue description:
In other words, if I generate Well, technically speaking, I can implement a state machine in the state: |
So it's about always doing those together? You'll never want to just addTable and then do something completely different? |
Well, a table can't be created without a column, so it just makes no sense to create a table without columns. After I create the table as a package of actions (add table, add column, etc), I still want the other |
OK. There's value in that. I reopen. |
For now, I use a single |
Testing Problem
See #134 (comment)
The case is that input data is hierarchical, and certain states are known to be invalid.
For instance, I know "table without columns" can't be created in the database, so I would like to avoid generating that state during testing.
At the same time, having action classes for
add table
,add column
,remove column
,remove table
seems to be helpful for code deduplication.In other words, what I want is to ensure that
add table
is always accompanied withadd column
action.Then, there's a limit on the number of columns in a single table (e.g. OracleDB table can have up to 1000 columns), so I want to have 1000 columns as an edge case. In other words, the edge case would be
add table
, then repeat 1000 times (add column
,update column
).Note, that
CompositeAction(add table, add column)
can't work since itsprecondition
can't be implemented properly.add column
verifies that the table with given name exists, and the verification should be performed only afteradd table
is executed.Suggested Solution
Implement
ActionSequenceArbitrary#sequences(Arbitrary<? extends List<? extends Action<M>>>)
, so the users canreturn multiple actions in sequence
Discussion
Alternative options could be
a) a Markov chain generator instead of
frequencyOf
. E.g. users would provide a matrix of weights, soadd column
would be the only continuation afteradd table
.A fixed weight matrix would allow jqwik to figure out edge cases automatically (it would know which arbitraries can ever be created just like in
frequencyOf
)b) Something like Stream#iterate(T seed, UnaryOperator f).
It looks like the current
net.jqwik.api.Arbitraries#lazy(Supplier<Arbitrary<T>>)
can be used for a stateful generation, however,lazy
does not seem to support edge casesThe text was updated successfully, but these errors were encountered: