Skip to content

Commit

Permalink
Should not shrink twice towards zero
Browse files Browse the repository at this point in the history
Related to #62 and #63
  • Loading branch information
dubzzz committed Apr 6, 2018
1 parent 13bd54d commit fc57174
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 9 deletions.
22 changes: 13 additions & 9 deletions src/check/arbitrary/IntegerArbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,35 @@ class IntegerArbitrary extends ArbitraryWithShrink<number> {
this.min = min === undefined ? IntegerArbitrary.MIN_INT : min;
this.max = max === undefined ? IntegerArbitrary.MAX_INT : max;
}
private wrapper(value: number): Shrinkable<number> {
return new Shrinkable(value, () => this.shrink(value).map(v => this.wrapper(v)));
private wrapper(value: number, shrunkOnce: boolean): Shrinkable<number> {
return new Shrinkable(value, () => this.shrink(value, shrunkOnce).map(v => this.wrapper(v, true)));
}
generate(mrng: Random): Shrinkable<number> {
return this.wrapper(mrng.nextInt(this.min, this.max));
return this.wrapper(mrng.nextInt(this.min, this.max), false);
}
private shrink_to(value: number, target: number): Stream<number> {
const gap = value - target;
private shrink_to(value: number, target: number, shrunkOnce: boolean): Stream<number> {
const realGap = value - target;
function* shrink_decr(): IterableIterator<number> {
const gap = shrunkOnce ? Math.floor(realGap / 2) : realGap;
for (let toremove = gap; toremove > 0; toremove = Math.floor(toremove / 2)) {
yield value - toremove;
}
}
function* shrink_incr(): IterableIterator<number> {
const gap = shrunkOnce ? Math.ceil(realGap / 2) : realGap;
for (let toremove = gap; toremove < 0; toremove = Math.ceil(toremove / 2)) {
yield value - toremove;
}
}
return gap > 0 ? stream(shrink_decr()) : stream(shrink_incr());
return realGap > 0 ? stream(shrink_decr()) : stream(shrink_incr());
}
shrink(value: number): Stream<number> {
shrink(value: number, shrunkOnce?: boolean): Stream<number> {
if (this.min <= 0 && this.max >= 0) {
return this.shrink_to(value, 0);
return this.shrink_to(value, 0, shrunkOnce === true);
}
return value < 0 ? this.shrink_to(value, this.max) : this.shrink_to(value, this.min);
return value < 0
? this.shrink_to(value, this.max, shrunkOnce === true)
: this.shrink_to(value, this.min, shrunkOnce === true);
}
}

Expand Down
32 changes: 32 additions & 0 deletions test/unit/check/arbitrary/IntegerArbitrary.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,38 @@ describe('IntegerArbitrary', () => {
);
})
));
it('Should not shrink twice towards zero', () =>
fc.assert(
fc.property(fc.integer().noShrink(), seed => {
// all value between <sA> and <sB> are failure cases
// we have a contiguous range of failures
const mrng = stubRng.mutable.fastincrease(seed);
const sA = integer().generate(mrng);
const sB = integer().generate(mrng);
const minValue = Math.min(sA.value, sB.value);
const maxValue = Math.max(sA.value, sB.value);

let shrinkable = sA;
let numZeros = 0;

// simulate the shrinking process
// count we do not ask for zero multiple times
while (shrinkable !== null) {
const oldShrinkable = shrinkable;
shrinkable = null;
for (const smallerShrinkable of oldShrinkable.shrink()) {
if (smallerShrinkable.value === 0) {
assert.equal(numZeros, 0);
++numZeros;
}
if (minValue <= smallerShrinkable.value && smallerShrinkable.value <= maxValue) {
shrinkable = smallerShrinkable;
break;
}
}
}
})
));
});
describe('nat', () => {
it('Should generate values between 0 and 2**31 -1 by default', () =>
Expand Down

0 comments on commit fc57174

Please sign in to comment.