
commonMain.io.kotest.properties.gens.kt Maven / Gradle / Ivy
package io.kotest.properties
import io.kotest.properties.shrinking.ChooseShrinker
import io.kotest.properties.shrinking.DoubleShrinker
import io.kotest.properties.shrinking.FloatShrinker
import io.kotest.properties.shrinking.IntShrinker
import io.kotest.properties.shrinking.ListShrinker
import io.kotest.properties.shrinking.Shrinker
import io.kotest.properties.shrinking.StringShrinker
import kotlin.jvm.JvmOverloads
import kotlin.math.abs
import kotlin.random.Random
import kotlin.random.nextInt
import kotlin.random.nextLong
/**
* Returns a stream of values where each value is a random
* printed string.
*
* The constant values are:
* The empty string
* A line separator
* Multi-line string
* a UTF8 string.
*/
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.string(minSize: Int = 0, maxSize: Int = 100): Gen = object : Gen {
val literals = listOf("",
"\n",
"\nabc\n123\n",
"\u006c\u0069b/\u0062\u002f\u006d\u0069nd/m\u0061x\u002e\u0070h\u0070")
override fun constants(): Iterable = literals.filter { it.length in minSize..maxSize }
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence {
r.nextPrintableString(minSize + r.nextInt(maxSize - minSize + 1))
}
}
override fun shrinker(): Shrinker? = StringShrinker
}
/**
* Returns a stream of values where each value is a randomly
* chosen [Int]. The values always returned include
* the following edge cases: [[Int.MIN_VALUE], [Int.MAX_VALUE], 0, 1, -1]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.int() = object : Gen {
val literals = listOf(Int.MIN_VALUE, Int.MAX_VALUE, 0, 1, -1)
override fun constants(): Iterable = literals
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextInt() }
}
override fun shrinker() = IntShrinker
}
/**
* Returns a stream of values where each value is a randomly
* chosen [UInt]. The values always returned include
* the following edge cases: [[UInt.MIN_VALUE], [UInt.MAX_VALUE]]
*/
@ExperimentalUnsignedTypes
fun Gen.Companion.uint() = object : Gen {
val literals = listOf(UInt.MIN_VALUE, UInt.MAX_VALUE)
override fun constants(): Iterable = literals
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextInt().toUInt() }
}
}
/**
* Returns a stream of values where each value is a randomly
* chosen [Short]. The values always returned include
* the following edge cases: [[Short.MIN_VALUE], [Short.MAX_VALUE], 0]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.short() = int().map { it.ushr(Int.SIZE_BITS - Short.SIZE_BITS).toShort() }
/**
* Returns a stream of values where each value is a randomly
* chosen [UShort]. The values always returned include
* the following edge cases: [[UShort.MIN_VALUE], [UShort.MAX_VALUE]]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
@ExperimentalUnsignedTypes
fun Gen.Companion.ushort() = uint().map { it.shr(UInt.SIZE_BITS - UShort.SIZE_BITS).toUShort() }
/**
* Returns a stream of values where each value is a randomly
* chosen [Byte]. The values always returned include
* the following edge cases: [[Byte.MIN_VALUE], [Byte.MAX_VALUE], 0]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.byte() = int().map { it.ushr(Int.SIZE_BITS - Byte.SIZE_BITS).toByte() }
/**
* Returns a stream of values where each value is a randomly
* chosen [UByte]. The values always returned include
* the following edge cases: [[UByte.MIN_VALUE], [UByte.MAX_VALUE]]
*/
@ExperimentalUnsignedTypes
fun Gen.Companion.ubyte() = uint().map { it.shr(UInt.SIZE_BITS - UByte.SIZE_BITS).toByte() }
/**
* Returns a stream of values where each value is a randomly
* chosen positive value. The values returned always include
* the following edge cases: [Int.MAX_VALUE]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.positiveIntegers(): Gen = int().filter { it > 0 }
/**
* Returns a stream of values where each value is a randomly
* chosen natural number. The values returned always include
* the following edge cases: [Int.MAX_VALUE]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.nats(): Gen = int().filter { it >= 0 }
/**
* Returns a stream of values where each value is a randomly
* chosen negative value. The values returned always include
* the following edge cases: [Int.MIN_VALUE]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.negativeIntegers(): Gen = int().filter { it < 0 }
/**
* Returns a stream of values where each value is a randomly
* chosen Double.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.double(): Gen = object : Gen {
val literals = listOf(0.0,
1.0,
-1.0,
1e300,
Double.MIN_VALUE,
Double.MAX_VALUE,
Double.NEGATIVE_INFINITY,
Double.NaN,
Double.POSITIVE_INFINITY)
override fun constants(): Iterable = literals
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextDouble() }
}
override fun shrinker(): Shrinker? = DoubleShrinker
}
/**
* Returns a [Gen] which is the same as [Gen.double] but does not include +INFINITY, -INFINITY or NaN.
*
* This will only generate numbers ranging from [from] (inclusive) to [to] (inclusive)
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.numericDoubles(from: Double = Double.MIN_VALUE,
to: Double = Double.MAX_VALUE
): Gen = object : Gen {
val literals = listOf(0.0, 1.0, -1.0, 1e300, Double.MIN_VALUE, Double.MAX_VALUE).filter { it in (from..to) }
override fun constants(): Iterable = literals
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextDouble(from, to) }
}
override fun shrinker(): Shrinker? = DoubleShrinker
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.positiveDoubles(): Gen = double().filter { it > 0.0 }
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.negativeDoubles(): Gen = double().filter { it < 0.0 }
/**
* Returns a stream of values where each value is a randomly
* chosen Float.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.float(): Gen = object : Gen {
val literals = listOf(0F,
Float.MIN_VALUE,
Float.MAX_VALUE,
Float.NEGATIVE_INFINITY,
Float.NaN,
Float.POSITIVE_INFINITY)
override fun constants(): Iterable = literals
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextFloat() }
}
override fun shrinker() = FloatShrinker
}
/**
* Returns a [Gen] which is the same as [Gen.float] but does not include +INFINITY, -INFINITY or NaN.
*
* This will only generate numbers ranging from [from] (inclusive) to [to] (inclusive)
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.numericFloats(
from: Float = Float.MIN_VALUE,
to: Float = Float.MAX_VALUE
): Gen = object : Gen {
val literals = listOf(0.0F, 1.0F, -1.0F, Float.MIN_VALUE, Float.MAX_VALUE).filter { it in (from..to) }
override fun constants(): Iterable = literals
// There's no nextFloat(from, to) method, so borrowing it from Double
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence {
r.nextDouble(from.toDouble(), to.toDouble()).toFloat()
}
}
override fun shrinker(): Shrinker? = FloatShrinker
}
/**
* Returns a stream of values where each value is a randomly
* chosen long. The values returned always include
* the following edge cases: [[Long.MIN_VALUE], [Long.MAX_VALUE], 0, 1, -1]
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.long(): Gen = object : Gen {
val literals = listOf(Long.MIN_VALUE, Long.MAX_VALUE, 0L, 1L, -1L)
override fun constants(): Iterable = literals
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { abs(r.nextLong()) }
}
}
/**
* Returns a stream of values where each value is a randomly
* chosen [ULong]. The values returned always include
* the following edge cases: [[ULong.MIN_VALUE], [ULong.MAX_VALUE]]
*/
@ExperimentalUnsignedTypes
fun Gen.Companion.ulong(): Gen = object : Gen {
val literals = listOf(ULong.MIN_VALUE, ULong.MAX_VALUE)
override fun constants(): Iterable = literals
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextLong().toULong() }
}
}
/**
* Returns both boolean values
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bool(): Gen = object : Gen {
override fun constants(): Iterable = listOf(true, false)
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextBoolean() }
}
}
private object CharSets {
val CONTROL = listOf('\u0000'..'\u001F', '\u007F'..'\u007F')
val WHITESPACE = listOf('\u0020'..'\u0020', '\u0009'..'\u0009', '\u000A'..'\u000A')
val BASIC_LATIN = listOf('\u0021'..'\u007E')
}
/**
* Returns a stream of randomly-chosen Chars. Custom characters can be generated by
* providing CharRanges. Distribution will be even across the ranges of Chars.
* For example:
* Gen.char('A'..'C', 'D'..'E')
* Ths will choose A, B, C, D, and E each 20% of the time.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.char(range: CharRange, vararg ranges: CharRange): Gen {
return Gen.char(listOf(range) + ranges)
}
/**
* Returns a stream of randomly-chosen Chars. Custom characters can be generated by
* providing a list of CharRanges. Distribution will be even across the ranges of Chars.
* For example:
* Gen.char(listOf('A'..'C', 'D'..'E')
* Ths will choose A, B, C, D, and E each 20% of the time.
*
* If no parameter is given, ASCII characters will be generated.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.char(ranges: List = CharSets.BASIC_LATIN): Gen = object : Gen {
init {
require(ranges.all { !it.isEmpty() }) { "Ranges cannot be empty" }
require(ranges.isNotEmpty()) { "List of ranges must have at least one range" }
}
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
val genRange =
if (ranges.size == 1) Gen.constant(ranges.first())
else makeRangeWeightedGen()
return generateSequence { genRange.next(seed = seed).random(r) }
}
// Convert the list of CharRanges into a weighted Gen in which
// the ranges are chosen from the list using the length of the
// range as the weight.
private fun makeRangeWeightedGen(): Gen {
val weightPairs = ranges.map { range ->
val weight = range.last.toInt() - range.first.toInt() + 1
Pair(weight, range)
}
return Gen.choose(weightPairs[0], weightPairs[1], *weightPairs.drop(2).toTypedArray())
}
}
/**
* Returns a stream of values, where each value is
* a set of values generated by the given generator.
*/
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.set(gen: Gen, maxSize: Int = 100): Gen> = object : Gen> {
init {
require(maxSize >= 0) { "maxSize must be positive" }
}
override fun constants(): Iterable> = listOf(gen.constants().take(maxSize).toSet())
override fun random(seed: Long?): Sequence> {
val r = getRandomFor(seed)
return generateSequence {
val size = r.nextInt(maxSize)
gen.random().take(size).toSet()
}
}
}
/**
* Returns a stream of values, where each value is
* a list of values generated by the underlying generator.
*/
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.list(gen: Gen, maxSize: Int = 100): Gen> = object : Gen> {
init {
require(maxSize >= 0) { "maxSize must be positive" }
}
override fun constants(): Iterable> = listOf(gen.constants().take(maxSize).toList())
override fun random(seed: Long?): Sequence> {
val r = getRandomFor(seed)
return generateSequence {
val size = r.nextInt(maxSize)
gen.random().take(size).toList()
}
}
override fun shrinker() = ListShrinker()
}
/**
* Returns a [[Gen]] where each value is a [[Triple]] generated
* by a value from each of three supplied generators.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.triple(genA: Gen,
genB: Gen,
genC: Gen): Gen> = object : Gen> {
override fun constants(): Iterable> {
return genA.constants().zip(genB.constants()).zip(genC.constants()).map {
Triple(it.first.first,
it.first.second,
it.second)
}
}
override fun random(seed: Long?): Sequence> = genA.random(seed).zip(genB.random(seed)).zip(
genC.random(
seed)).map {
Triple(it.first.first,
it.first.second,
it.second)
}
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bind(gena: Gen, createFn: (A) -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence = gena.random().map { createFn(it) }
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bind(gena: Gen, genb: Gen, createFn: (A, B) -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence =
gena.random().zip(genb.random(seed)).map { createFn(it.first, it.second) }
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bind(gena: Gen,
genb: Gen,
genc: Gen,
createFn: (A, B, C) -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence =
gena.random().zip(genb.random(seed)).zip(genc.random()).map {
createFn(it.first.first,
it.first.second,
it.second)
}
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bind(gena: Gen, genb: Gen, genc: Gen, gend: Gen,
createFn: (A, B, C, D) -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence =
gena.random()
.zip(genb.random(seed))
.zip(genc.random(seed))
.zip(gend.random(seed))
.map { createFn(it.first.first.first, it.first.first.second, it.first.second, it.second) }
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bind(gena: Gen, genb: Gen, genc: Gen, gend: Gen, gene: Gen,
createFn: (A, B, C, D, E) -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence =
gena.random()
.zip(genb.random(seed))
.zip(genc.random(seed))
.zip(gend.random(seed))
.zip(gene.random(seed))
.map {
createFn(it.first.first.first.first,
it.first.first.first.second,
it.first.first.second,
it.first.second,
it.second)
}
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bind(gena: Gen,
genb: Gen,
genc: Gen,
gend: Gen,
gene: Gen,
genf: Gen,
createFn: (A, B, C, D, E, F) -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence =
gena.random()
.zip(genb.random(seed))
.zip(genc.random(seed))
.zip(gend.random(seed))
.zip(gene.random(seed))
.zip(genf.random(seed))
.map {
createFn(
it.first.first.first.first.first,
it.first.first.first.first.second,
it.first.first.first.second,
it.first.first.second,
it.first.second,
it.second)
}
}
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.bind(gena: Gen,
genb: Gen,
genc: Gen,
gend: Gen,
gene: Gen,
genf: Gen,
geng: Gen,
createFn: (A, B, C, D, E, F, G) -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence =
gena.random()
.zip(genb.random(seed))
.zip(genc.random(seed))
.zip(gend.random(seed))
.zip(gene.random(seed))
.zip(genf.random(seed))
.zip(geng.random(seed))
.map {
createFn(
it.first.first.first.first.first.first,
it.first.first.first.first.first.second,
it.first.first.first.first.second,
it.first.first.first.second,
it.first.first.second,
it.first.second,
it.second)
}
}
fun Gen.Companion.oneOf(vararg gens: Gen): Gen = object : Gen {
override fun constants(): Iterable = gens.flatMap { it.constants() }
override fun random(seed: Long?): Sequence {
require(gens.isNotEmpty()) { "List of generators cannot be empty" }
val iterators = gens.map { it.random(seed).iterator() }
val r = getRandomFor(seed)
return generateInfiniteSequence {
val iteratorLocation = r.nextInt(0, iterators.size)
val iterator = iterators[iteratorLocation]
iterator.next()
}
}
}
/**
* Returns a stream of values, where each
* value is generated from the given function
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
inline fun Gen.Companion.create(crossinline fn: () -> T): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence = generateInfiniteSequence { fn() }
}
/**
* Adapts a list into a generator, where random
* values will be picked. May not choose every
* item in the list.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.from(values: List): Gen = object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateInfiniteSequence { values[r.nextInt(0, values.size)] }
}
}
/**
* @return a new [Gen] created from the given [values] (see [from] List for more details)
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.from(values: Array): Gen = from(values.toList())
/**
* Returns a stream of values, where each value is
* a random Int between the given min and max.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0. Use Arb.ints(min, max)")
fun Gen.Companion.choose(min: Int, max: Int): Gen {
require(min < max) { "min must be < max" }
return object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextInt(min..max) }
}
override fun shrinker() = ChooseShrinker(min, max)
}
}
/**
* Returns a stream of values, where each value is a
* Long between the given min and max.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0. Use Arb.longs(min, max)")
fun Gen.Companion.choose(min: Long, max: Long): Gen {
require(min < max) { "min must be < max" }
return object : Gen {
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
return generateSequence { r.nextLong(min..max) }
}
}
}
/**
* Returns a stream of values based on weights:
*
* Gen.choose(1 to 'A', 2 to 'B') will generate 'A' 33% of the time
* and 'B' 66% of the time.
*
* @throws IllegalArgumentException If any negative weight is given or only
* weights of zero are given.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0. Use Arb.choose(a, b, c, ...)")
fun Gen.Companion.choose(a: Pair, b: Pair, vararg cs: Pair): Gen {
val allPairs = listOf(a, b) + cs
val weights = allPairs.map { it.first }
require(weights.all { it >=0 }) { "Negative weights not allowed" }
require(weights.any { it > 0 }) { "At least one weight must be greater than zero"}
return object : Gen {
// The algorithm for pick is a migration of
// the algorithm from Haskell QuickCheck
// http://hackage.haskell.org/package/QuickCheck
// See function frequency in the package Test.QuickCheck
private tailrec fun pick(n: Int, l: Sequence>): T {
val (w, e) = l.first()
return if (n <= w) e
else pick(n - w, l.drop(1))
}
override fun constants(): Iterable = emptyList()
override fun random(seed: Long?): Sequence {
val r = getRandomFor(seed)
val total = weights.sum()
return generateSequence {
val n = r.nextInt(1, total + 1)
pick(n, allPairs.asSequence())
}
}
}
}
/**
* Returns a stream of values, where each value is
* a pair generated by the underlying generators.
*/
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.pair(genK: Gen, genV: Gen): Gen> = object : Gen> {
override fun constants(): Iterable> {
val keys = genK.constants().toList()
return keys.zip(genV.random().take(keys.size).toList())
}
override fun random(seed: Long?): Sequence> = genK.random(seed).zip(genV.random(seed))
}
/**
* Returns a stream of values, where each value is
* a Map, which contains keys and values generated
* from the underlying generators.
*/
@JvmOverloads
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun Gen.Companion.map(genK: Gen, genV: Gen, maxSize: Int = 100): Gen
© 2015 - 2025 Weber Informatics LLC | Privacy Policy