Best-practice for guarding against infinite-looping arbitraries? #4659
-
Hey folks, we've started to see this issue crop up more and more in which a buggy I've seen some recommendations around using Here's an example of an arbitrary that infinite loops, and can't be killed by it('Should interrupt the test run after a short time', async () => {
fc.assert(
fc.property(
fc.nat().filter(() => false),
(_value) => {}
),
{ interruptAfterTimeLimit: 1000 }
);
}); (this arbitrary is obviously wrong, but there are some invocations that can be surprisingly hard to spot) Are there any configs that will cause this type of test not to loop indefinitely? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
At the moment, the only built-in thing that can protect you against filtering leading to an infinite loop is For now, I think the only trick that could make it for function buildStoppeable(filter) {
let consecutiveErrors = 0;
return function stoppeableFilter(...args) {
const out = filter(...args);
if (out) {
consecutiveErrors = 0;
return out;
}
if (++consecutiveErrors > 100) throw new Error('To many errors!');
return out;
}
}
it('Should interrupt the test run after a short time', async () => {
fc.assert(
fc.property(
fc.nat().filter(buildStoppeable(() => false)),
(_value) => {}
),
{ interruptAfterTimeLimit: 1000 }
);
}); And if you want to make it the default for filter (note: this prototype-based version might break on next minors as it plays with altering internals): const originalFilter = fc.Arbitrary.prototype.filter;
fc.Arbitrary.prototype.filter = function (...args) {
const f = buildStoppeable(args[0]);
return originalFilter.call(this, f, ...args.slice(1));
} Side-note: For the moment, anything around time limit, timeout and similar is only related to things related to the execution of the predicates. They are controlled outside of the arbitraries and can only stop synchronous code. |
Beta Was this translation helpful? Give feedback.
At the moment, the only built-in thing that can protect you against filtering leading to an infinite loop is
fc.pre
.For now, I think the only trick that could make it for
filter
would be to do something like: