Skip to content

Commit c790104

Browse files
authored
Merge pull request #165 from dwijnand/cap-Size
Cap Size to [1-100] by clamping
2 parents 8eb0e35 + a27cd86 commit c790104

File tree

3 files changed

+53
-9
lines changed

3 files changed

+53
-9
lines changed

core/shared/src/main/scala/hedgehog/Range.scala

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import hedgehog.predef.{DecimalPlus, IntegralPlus}
77
* Tests are parameterized by the size of the randomly-generated data, the
88
* meaning of which depends on the particular generator used.
99
*/
10-
case class Size(value: Int) {
10+
sealed abstract case class Size private (value: Int) {
1111

1212
/** Represents the size as a percentage (0 - 1) which is useful for range calculations */
1313
def percentage: Double =
@@ -24,6 +24,10 @@ case class Size(value: Int) {
2424
}
2525

2626
object Size {
27+
def apply(value: Int): Size = {
28+
val remainder = value % max
29+
new Size(if (remainder <= 0) remainder + max else remainder) {}
30+
}
2731

2832
def max: Int =
2933
100
@@ -131,13 +135,13 @@ object Range {
131135
* parameter.
132136
*
133137
* {{{
134-
* scala> Range.linear(0, 10).bounds(Size(0))
138+
* scala> Range.linear(0, 10).bounds(Size(1))
135139
* (0,0)
136140
*
137141
* scala> Range.linear(0, 10).bounds(Size(50))
138142
* (0,5)
139143
*
140-
* scala> Range.linear(0, 10).bounds(Size(99))
144+
* scala> Range.linear(0, 10).bounds(Size(100))
141145
* (0,10)
142146
* }}}
143147
*/
@@ -149,13 +153,13 @@ object Range {
149153
* parameter.
150154
*
151155
* {{{
152-
* scala> Range.linearFrom(0, -10, 10).bounds(Size(0))
156+
* scala> Range.linearFrom(0, -10, 10).bounds(Size(1))
153157
* (0,0)
154158
*
155159
* scala> Range.linearFrom(0, -10, 20).bounds(Size(50))
156160
* (-5,10)
157161
*
158-
* scala> Range.linearFrom(0, -10, 20).bounds(Size(20))
162+
* scala> Range.linearFrom(0, -10, 20).bounds(Size(100))
159163
* (-10,20)
160164
* }}}
161165
*/

core/shared/src/main/scala/hedgehog/core/PropertyT.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,10 @@ trait PropertyTReporting {
170170
// Increase the size proportionally to the number of tests to ensure better coverage of the desired range
171171
val sizeInc = Size(Math.max(1, Size.max / config.testLimit.value))
172172
// Start the size at whatever remainder we have to ensure we run with "max" at least once
173-
val sizeInit = Size(Size.max % Math.min(config.testLimit.value, Size.max)).incBy(sizeInc)
173+
val sizeInit = Size((Size.max % Math.min(config.testLimit.value, Size.max)) + sizeInc.value)
174174
@annotation.tailrec
175175
def loop(successes: SuccessCount, discards: DiscardCount, size: Size, seed: Seed, coverage: Coverage[CoverCount]): Report =
176-
if (size.value > Size.max)
177-
loop(successes, discards, sizeInit, seed, coverage)
178-
else if (successes.value >= config.testLimit.value)
176+
if (successes.value >= config.testLimit.value)
179177
// we've hit the test limit
180178
Coverage.split(coverage, successes) match {
181179
case (_, Nil) =>

test/shared/src/test/scala/hedgehog/RangeTest.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ object RangeTest extends Properties {
1010
, example("double", testFractional)
1111
, example("long", testLong)
1212
, property("double should not generate values outside of the range", testDoubleWithinRange).withTests(1000)
13+
, example("size", testSize)
14+
, property("size is always within the 1-100 range", testSizeWithinRange).withTests(1000)
1315
)
1416

1517
def testIntegral: Result = {
@@ -66,4 +68,44 @@ object RangeTest extends Properties {
6668
, Result.assert(d >= Double.MinValue).log("min_value")
6769
, Result.assert(d <= Double.MaxValue).log("max_value")
6870
))
71+
72+
def testSize: Result = {
73+
Result.all(List(
74+
Size(-1).value ==== 99 // using modulus, not remainder
75+
, Size(0).value ==== 100
76+
, Size(1).value ==== 1
77+
, Size(2).value ==== 2
78+
, Size(99).value ==== 99
79+
, Size(100).value ==== 100
80+
, Size(101).value ==== 1
81+
))
82+
}
83+
84+
def testSizeWithinRange: Property = {
85+
for {
86+
n <- sizeValueGen.forAll
87+
.cover(1, "too small", _ < 1)
88+
.cover(1, "min", _ == 1)
89+
.cover(9, "normal", x => x > 1 && x < 100)
90+
.cover(1, "max", _ == 100)
91+
.cover(1, "too big", _ > 100)
92+
} yield {
93+
val obtained = Size(n).value
94+
Result.all(List(
95+
Result.assert(obtained >= 1).log(s"expected size value to be >= 1, but it was $obtained")
96+
, Result.assert(obtained <= 100).log(s"expected size value to be <= 100, but it was $obtained")
97+
))
98+
}
99+
}
100+
101+
def sizeValueGen: Gen[Int] =
102+
for {
103+
n <- Gen.int(Range.linearFrom(0, -5, 5))
104+
i <- Gen.choice1(
105+
Gen.constant(n) // near min
106+
, Gen.constant(100 - n) // near max
107+
, Gen.int(Range.linear(1, 100)) // normal range
108+
, Gen.int(Range.linearFrom(1, Int.MinValue, Int.MaxValue)) // total range
109+
)
110+
} yield i
69111
}

0 commit comments

Comments
 (0)