Skip to content
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

Improve Arb function naming #2310

Merged
merged 3 commits into from Jun 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions documentation/docs/changelog.md
Expand Up @@ -72,7 +72,7 @@ See the full list of [extension modules](https://kotest.io/docs/extensions/exten
* In order to ensure the `EventuallyListener` is called in `eventually` when an exception is thrown the `ListenerState` field `result` was changed
from type `T` to type `T?`. This will allow insight into when the eventually producer function is failing for whatever reason
instead of appearing as if it is hanging. #2190
* Property tests now randomly cycle between edgecases and samples, rather than iterating all edgecases first. This allows greater number of edgecases to be used and avoids a combinatoral explosion. If you are implementing custom Arb's by extending the Arb class (instead of using the `arbitrary` builders), then you will need to adjust your edgecases method from `fun edgecases(): List<A>` to `fun edgecase(rs: RandomSource): A?`.
* Property tests now randomly cycle between edge cases and samples, rather than iterating all edge cases first. This allows greater number of edge cases to be used and avoids a combinatoral explosion. If you are implementing custom Arb's by extending the Arb class (instead of using the `arbitrary` builders), then you will need to adjust your edge cases method from `fun edgecases(): List<A>` to `fun edgecase(rs: RandomSource): A?`.
* Because of the above property test change, if you are setting a seed in a property test you may need to adjust the value.
* The kotlin stdlib dependencies are now marked as `compileOnly`, meaning the version in your build will be used. Kotest tries to maintain compatibility across multiple versions by not relying on features only available in the latest releases.
* Duplicated test names no longer throw an automatic error, but now mangle the name. So two tests of name 'foo' will appear as 'foo' and '(1) foo'. This enables data driven testing to work properly in javascript. To restore the original behavior, set the configuration value `Configuration.duplicateTestNameMode = Error`.
Expand Down Expand Up @@ -244,7 +244,7 @@ Note: Release 4.4.0 bumps the minimum required version of Kotlin to 1.4.21
#### Bugfix

* A Kotlin 1.4 specific method was added in 4.3.1 and reverted in 4.3.2
* Arb.choose does not currently include edgecases from input arbs #1886
* Arb.choose does not currently include edge cases from input arbs #1886
* String shrinking is not being executed #1860
* Arb.stringPattern slows down the test dramatically #1878
* AssertionMode.Error doesn't work on FeatureSpec #1864
Expand Down Expand Up @@ -449,8 +449,8 @@ Note: Release 4.4.0 bumps the minimum required version of Kotlin to 1.4.21
### 4.0.5 April 2020

* Bugfix: Focus mode would cause some nested tests to be ignored [#1376](https://github.com/kotest/kotest/issues/1376)
* Bugfix: Arb.choice would include edgecases in the generated values [#1406](https://github.com/kotest/kotest/issues/1406)
* Bugfix: Arb.int and Arb.long edgecases included values outside the specified ranged [#1405](https://github.com/kotest/kotest/issues/1405)
* Bugfix: Arb.choice would include edge cases in the generated values [#1406](https://github.com/kotest/kotest/issues/1406)
* Bugfix: Arb.int and Arb.long edge cases included values outside the specified ranged [#1405](https://github.com/kotest/kotest/issues/1405)

### 4.0.4 April 2020

Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/proptest/genops.md
Expand Up @@ -41,7 +41,7 @@ val integerStrings: Arb<String> = Arb.int().map { it.toString() }

## FlatMap

If you have an arb whose emission or edgecases depends on the emission of the previous arbitraries, you can use flatMap.
If you have an arb whose emission or edge cases depends on the emission of the previous arbitraries, you can use flatMap.
```kotlin
val dependentArbs: Arb<String> = Arb.of("foo", "bar").flatMap { prefix ->
Arb.int(1..10).map { integer ->
Expand Down
4 changes: 2 additions & 2 deletions documentation/docs/proptest/gens.md
Expand Up @@ -40,8 +40,8 @@ tested with zero, a positive number and a negative number. If only random values
appearing would be fairly low, so Kotest will always provide some "edge cases" for integers (unless you specify
otherwise).

When executing tests, the framework will alternate randomly between samples and edgecases. The split is determined
by a configuration value which defaults to 2% edgecases.
When executing tests, the framework will alternate randomly between samples and edge cases. The split is determined
by a configuration value which defaults to 2% edge cases.

Not all arbs have edge cases, but the arbs for the most common types do.
Here are some examples of edge cases used by some arbs:
Expand Down
20 changes: 10 additions & 10 deletions documentation/docs/proptest/genslist.md
Expand Up @@ -18,8 +18,8 @@ We also provide generators for [Arrow](arrow.md) as a separate module.
| `arb.orNull()` | Generates random values from the arb instance, with null values mixed in. For example, `Arb.int().orNull()` could generate `1, -1, null, 8, 17`, and so on. Has overloaded versions to control the frequency of nulls being generated.||||
| `arb.orNull(nullProbability)` | Generates random values from the arb instance, with null values mixed in using the defined probability. ||||
| **Booleans** |
| `Arb.bool()` | Returns an `Arb` that produces `Boolean`s. ||||
| `Arb.boolArray(length, content)` | Returns an `Arb` that produces `BoolArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| `Arb.boolean()` | Returns an `Arb` that produces `Boolean`s. ||||
| `Arb.booleanArray(length, content)` | Returns an `Arb` that produces `BoolArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| `Exhaustive.boolean()` | Alternatives between true and false. ||||
| **Chars** |
| `Arb.char(range1, range2,...)` | Returns random char's generated from one or more given ranges. By supporting multiple ranges, it is possible to specific non-consecutive ranges of characters to populate values from. ||||
Expand All @@ -29,14 +29,14 @@ We also provide generators for [Arrow](arrow.md) as a separate module.
| `Arb.positiveByte(min, max)` | Returns an `Arb` that produces positive `Byte`s from `min` to `max` (inclusive). The edge cases are 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.negativeByte(min, max)` | Returns an `Arb` that produces negative `Byte`s from `min` to `max` (inclusive). The edge cases are `min` and -1 which are only included if they are in the provided range. ||||
| `Arb.byteArray(length, content)` | Returns an `Arb` that produces `ByteArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| `Arb.ubyte(min, max)` | Returns an `Arb` that produces `UByte`s from `min` to `max` (inclusive). The edge cases are `min`, 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.uByte(min, max)` | Returns an `Arb` that produces `UByte`s from `min` to `max` (inclusive). The edge cases are `min`, 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.uByteArray(length, content)` | Returns an `Arb` that produces `UByteArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| **Shorts** |
| `Arb.short(min, max)` | Returns an `Arb` that produces `Short`s from `min` to `max` (inclusive). The edge cases are `min`, -1, 0, 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.positiveShort(min, max)` | Returns an `Arb` that produces positive `Short`s from `min` to `max` (inclusive). The edge cases are 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.negativeShort(min, max)` | Returns an `Arb` that produces negative `Short`s from `min` to `max` (inclusive). The edge cases are `min` and -1 which are only included if they are in the provided range. ||||
| `Arb.shortArray(length, content)` | Returns an `Arb` that produces `ShortArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| `Arb.ushort(min, max)` | Returns an `Arb` that produces `UShort`s from `min` to `max` (inclusive). The edge cases are `min`, 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.uShort(min, max)` | Returns an `Arb` that produces `UShort`s from `min` to `max` (inclusive). The edge cases are `min`, 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.uShortArray(length, content)` | Returns an `Arb` that produces `UShortArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| **Ints** |
| `Arb.int(min, max)` | Returns an `Arb` that produces `Int`s from `min` to `max` (inclusive). The edge cases are `min`, -1, 0, 1 and `max` which are only included if they are in the provided range. ||||
Expand All @@ -45,10 +45,10 @@ We also provide generators for [Arrow](arrow.md) as a separate module.
| `Arb.negativeInt(min, max)` | Returns an `Arb` that produces negative `Int`s from `min` to `max` (inclusive). The edge cases are `min` and -1 which are only included if they are in the provided range. ||||
| `Arb.nonPositiveInts(min, max)` | Returns an `Arb` that produces non positive `Int`s from `min` to `max` (inclusive). The edge cases are `min`, -1 and 0 which are only included if they are in the provided range. ||||
| `Arb.intArray(length, content)` | Returns an `Arb` that produces `IntArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| `Arb.uint(min, max)` | Returns an `Arb` that produces `UInt`s from `min` to `max` (inclusive). The edge cases are `min`, 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.uInt(min, max)` | Returns an `Arb` that produces `UInt`s from `min` to `max` (inclusive). The edge cases are `min`, 1 and `max` which are only included if they are in the provided range. ||||
| `Arb.uIntArray(length, content)` | Returns an `Arb` that produces `UIntArray`s where `length` produces the length of the arrays and `content` produces the content of the arrays. ||||
| `Exhaustive.ints(range)` | Returns all ints in the given range. ||||
| `Arb.multiple(k, max)` | Generates multiples of k up a max value. The edgecases are `0`. ||||
| `Arb.multiple(k, max)` | Generates multiples of k up a max value. The edge cases are `0`. ||||
| `Arb.factor(k)` | Generates factors of k. ||||
| **Longs** |
| `Arb.long(min, max)` | Returns an `Arb` that produces `Long`s from `min` to `max` (inclusive). The edge cases are `min`, -1, 0, 1 and `max` which are only included if they are in the provided range. ||||
Expand All @@ -74,9 +74,9 @@ We also provide generators for [Arrow](arrow.md) as a separate module.
| `Arb.enum<T>()` | Randomly selects constants from the given enum. ||||
| `Exhaustive.enum<T>()` | Iterates all the constants defined in the given enum. ||||
| **Geo** |
| `Arb.latlong()` | Generates random pair's of doubles, where each double is in the range -180 to 180. ||||
| `Arb.latlong()` | Generates random pairs of doubles, where each double is in the range -180 to 180. ||||
| **Strings** |
| `Arb.string(range)` | Generates random printable strings with a randomly chosen size from the given range. If rangei s not specified then (0..100) is used. The edgecases include empty string, a blank string and a unicode string. ||||
| `Arb.string(range)` | Generates random printable strings with a randomly chosen size from the given range. If range is not specified then (0..100) is used. The edge cases include empty string, a blank string and a unicode string. ||||
| `Arb.stringPattern(pattern)` | Generates strings that match given pattern using [Generex](https://github.com/mifmif/Generex) || | |
| `Exhaustive.azstring(range)` | Returns all A-Z strings in the given range. For example if range was 1..2 then a, b, c, ...., yz, zz would be included. ||||
| `Arb.email(localPartGen, domainGen)` | Generates random emails where the local part and domain part are random strings generated by the given generators. A default value is provided for both. ||||
Expand All @@ -102,8 +102,8 @@ We also provide generators for [Arrow](arrow.md) as a separate module.
| `Arb<T>.chunked(range)` | Generates lists where each list is populated from elements of this receiver. The size of each size is randomly chosen within the given range. ||||
| `Exhaustive.collection(list)` | Enumerates each element of the list one by one. ||||
| **Tuples**|
| `Arb.pair(arb1, arb2) | Generates `Pair` instances where each value of the pair is drawn from the two provided arbs ||||
| `Arb.triple(arb1, arb2, arb3) | Generates `Triple` instances where each value of the triple is drawn from the three provided arbs ||||
| `Arb.pair(arb1, arb2)` | Generates `Pair` instances where each value of the pair is drawn from the two provided arbs ||||
| `Arb.triple(arb1, arb2, arb3)` | Generates `Triple` instances where each value of the triple is drawn from the three provided arbs ||||
| **Dates** |
| `Arb.date(ranges)` | Generates random dates with the year between the given range | || |
| `Arb.datetime(ranges)` | Generates random date times with the year between the given range | || |
Expand Down
14 changes: 7 additions & 7 deletions kotest-property/src/commonMain/kotlin/io/kotest/property/Gen.kt
Expand Up @@ -26,8 +26,8 @@ sealed class Gen<out A> {
/**
* Returns values from this generator as a lazily generated sequence.
*
* If this gen is an [Arb], then each value will either be a sample or an edgecase. The bias
* towards edgecases or samples is given by the value of [EdgeConfig.edgecasesGenerationProbability]
* If this gen is an [Arb], then each value will either be a sample or an edge case. The bias
* towards edge cases or samples is given by the value of [EdgeConfig.edgecasesGenerationProbability]
* inside the [edgeConfig] parameter.
*
* If this gen is an [Exhaustive], then the returned values will iterate in turn, repeating
Expand Down Expand Up @@ -65,9 +65,9 @@ sealed class Gen<out A> {
}

/**
* An [Arb] (short for arbitrary) is a generator of values in two categories: edgecases and samples.
* An [Arb] (short for arbitrary) is a generator of values in two categories: edge cases and samples.
*
* Edgecases are values that are a common source of bugs. For example, a function using ints is
* Edge cases are values that are a common source of bugs. For example, a function using ints is
* more likely to fail for common edge cases like zero, minus 1, positive 1, [Int.MAX_VALUE] and [Int.MIN_VALUE]
* rather than random values like 965489. Therefore it is useful that we try to include such values
* rather than relying entirely on random values which are unlikely to generate these.
Expand All @@ -86,9 +86,9 @@ sealed class Gen<out A> {
abstract class Arb<out A> : Gen<A>() {

/**
* Returns a single edgecase for this arbitrary.
* If this arb provides mutliple edgecases, then one should be chosen randomly.
* Can return null if no edgecases are available.
* Returns a single edge case for this arbitrary.
* If this arb provides multiple edge cases, then one should be chosen randomly.
* Can return null if no edge cases are available.
*/
abstract fun edgecase(rs: RandomSource): A?

Expand Down
Expand Up @@ -6,19 +6,19 @@ import io.kotest.property.Sample

/**
* Returns a sequence of size [count] from values generated from this arb.
* Edgecases will be ignored.
* Edge cases will be ignored.
*/
fun <A> Arb<A>.take(count: Int, rs: RandomSource = RandomSource.default()): Sequence<A> =
samples(rs).map { it.value }.take(count)

/**
* Returns a single value generated from this arb ignoring edgecases.
* Returns a single value generated from this arb ignoring edge cases.
* Alias for next.
*/
fun <A> Arb<A>.single(rs: RandomSource = RandomSource.default()): A = this.samples(rs).map { it.value }.first()

/**
* Returns a single value generated from this arb ignoring edgecases.
* Returns a single value generated from this arb ignoring edge cases.
* Alias for single.
*/
fun <A> Arb<A>.next(rs: RandomSource = RandomSource.default()): A = single(rs)
Expand Down
Expand Up @@ -6,11 +6,14 @@ import io.kotest.property.Gen
/**
* Returns an [Arb] that produces [Boolean]s.
*/
fun Arb.Companion.bool(): Arb<Boolean> = arbitrary(listOf(true, false)) { it.random.nextBoolean() }
fun Arb.Companion.boolean(): Arb<Boolean> = arbitrary(listOf(true, false)) { it.random.nextBoolean() }

@Deprecated("use boolean", ReplaceWith("boolean()"))
fun Arb.Companion.bool(): Arb<Boolean> = boolean()

/**
* Returns an [Arb] that produces [BooleanArray]s where [length] produces the length of the arrays and
* [content] produces the content of the arrays.
*/
fun Arb.Companion.boolArray(length: Gen<Int>, content: Arb<Boolean>): Arb<BooleanArray> =
fun Arb.Companion.booleanArray(length: Gen<Int>, content: Arb<Boolean>): Arb<BooleanArray> =
toPrimitiveArray(length, content, Collection<Boolean>::toBooleanArray)
Expand Up @@ -28,7 +28,7 @@ fun <A> arbitrary(edgecases: List<A>, shrinker: Shrinker<A>, fn: (RandomSource)
}

/**
* Creates a new [Arb] that generates edgecases from the given [edgecaseFn] function
* Creates a new [Arb] that generates edge cases from the given [edgecaseFn] function
* and generates samples from the given [sampleFn] function.
*/
fun <A> arbitrary(edgecaseFn: (RandomSource) -> A?, sampleFn: (RandomSource) -> A): Arb<A> =
Expand All @@ -38,7 +38,7 @@ fun <A> arbitrary(edgecaseFn: (RandomSource) -> A?, sampleFn: (RandomSource) ->
}

/**
* Creates a new [Arb] that generates edgecases from the given [edgecaseFn] function,
* Creates a new [Arb] that generates edge cases from the given [edgecaseFn] function,
* performs shrinking using the supplied [Shrinker, and generates samples from the given [sampleFn] function.
*/
fun <A> arbitrary(
Expand Down
Expand Up @@ -34,15 +34,15 @@ fun Arb.Companion.negativeByte(min: Byte = Byte.MIN_VALUE): Arb<Byte> = byte(min
fun Arb.Companion.byteArray(length: Gen<Int>, content: Arb<Byte>): Arb<ByteArray> =
toPrimitiveArray(length, content, Collection<Byte>::toByteArray)

@Deprecated("use byteArray", ReplaceWith("byteArray"))
@Deprecated("use byteArray", ReplaceWith("byteArray(generateArrayLength, generateContents)"))
fun Arb.Companion.byteArrays(generateArrayLength: Gen<Int>, generateContents: Arb<Byte>): Arb<ByteArray> =
byteArray(generateArrayLength, generateContents)

/**
* Returns an [Arb] that produces [UByte]s from [min] to [max] (inclusive).
* The edge cases are [min], 1 and [max] which are only included if they are in the provided range.
*/
fun Arb.Companion.ubyte(min: UByte = UByte.MIN_VALUE, max: UByte = UByte.MAX_VALUE): Arb<UByte> =
fun Arb.Companion.uByte(min: UByte = UByte.MIN_VALUE, max: UByte = UByte.MAX_VALUE): Arb<UByte> =
arbitrary(listOf(min, 1u, max).filter { it in min..max }.distinct(), UByteShrinker) {
it.random.nextUInt(min..max).toUByte()
}
Expand Down