All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.kotest.properties.Gen.kt Maven / Gradle / Ivy

package io.kotest.properties

import io.kotest.properties.shrinking.Shrinker
import kotlin.random.Random

/**
 * A Generator, or [Gen] is responsible for generating data
 * to be used in property testing. Each generator will
 * generate data for a specific type .
 *
 * The idea behind property testing is the testing framework
 * will automatically test a range of different values,
 * including edge cases and random values.
 *
 * There are two types of values to consider.
 *
 * The first are values that should always be included - those
 * edge cases values which are common sources of bugs. For
 * example, a generator for [Int]s should always include
 * values like zero, minus 1, positive 1, [Int.MAX_VALUE]
 * and [Int.MIN_VALUE].
 *
 * Another example would be for a generator for enums. That
 * should include _all_ the values of the enum to ensure
 * each value is tested.
 *
 * The second set of values are random values, which are
 * used to give us a greater breadth of values tested.
 * The [Int] generator example should return a random int
 * from across the entire integer range.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
interface Gen {

   companion object

   /**
    * Returns the values that should always be used
    * if this generator is to give complete coverage.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun constants(): Iterable

   /**
    * Generate a random sequence of type T, that is compatible
    * with the constraints of this generator.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun random(seed: Long? = null): Sequence

   /**
    * @return the [Shrinker] for this gen or null if shrinking is not supported.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun shrinker(): Shrinker? = null

   /**
    * Create a new [Gen] by filtering the output of this gen.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun filter(pred: (T) -> Boolean): Gen {
      val outer = this
      return object : Gen {
         override fun constants(): Iterable = outer.constants().filter(pred)
         override fun random(seed: Long?): Sequence = outer.random(seed).filter(pred)
         override fun shrinker(): Shrinker? {
            val s = outer.shrinker()
            return if (s == null) null else object : Shrinker {
               override fun shrink(failure: T): List = s.shrink(failure).filter(pred)
            }
         }
      }
   }

   /**
    * @return a new [Gen] by filtering this gen's output by the negated function [f]
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun filterNot(f: (T) -> Boolean): Gen = filter { !f(it) }

   /**
    * Create a new [Gen] by mapping the output of this gen.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun  flatMap(f: (T) -> Gen): Gen {
      val outer = this
      return object : Gen {
         override fun constants(): Iterable = outer.constants().flatMap { f(it).constants() }
         override fun random(seed: Long?): Sequence = outer.random(seed).flatMap { f(it).random(seed) }
      }
   }

   /**
    * Create a new [Gen] by mapping the output of this gen.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun  map(f: (T) -> U): Gen {
      val outer = this
      return object : Gen {
         override fun constants(): Iterable = outer.constants().map(f)
         override fun random(seed: Long?): Sequence = outer.random(seed).map(f)
      }
   }

   /**
    * Create a new [Gen] which will return the values of this gen plus null.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun orNull(): Gen {
      val outer = this
      return object : Gen {
         override fun constants(): Iterable = outer.constants() + listOf(null)
         override fun random(seed: Long?): Sequence = outer.random(seed)
         override fun shrinker(): Shrinker? {
            val s = outer.shrinker()
            return if (s == null) null else object : Shrinker {
               override fun shrink(failure: T?): List = if (failure == null) emptyList() else s.shrink(failure)
            }
         }
      }
   }

   /**
    * Returns a new [[Gen]] which will return the values from this gen and the values of
    * the supplied gen together. The supplied gen must be a subtype of the
    * type of this gen.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun  merge(gen: Gen): Gen {
      val outer = this
      return object : Gen {
         override fun constants(): Iterable = outer.constants() + gen.constants()
         override fun random(seed: Long?): Sequence = outer.random(seed).zip(gen.random(seed)).flatMap {
            sequenceOf(it.first, it.second)
         }
      }
   }

   /**
    * Returns a new [[Gen]] which will return the values from this gen and only once values
    * of this gen exhaust it will return the values from the supplied gen.
    * The supplied gen must be a subtype of the type of this gen.
    */
   @Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
   fun  concat(gen: Gen): Gen {
      val outer = this
      return object : Gen {
         override fun constants(): Iterable = outer.constants() + gen.constants()
         override fun random(seed: Long?): Sequence = outer.random(seed) + gen.random(seed)
      }
   }
}

/**
 * Create a new [Gen] by keeping only instances of U generated by this gen.
 * This is useful if you have a type hierarchy and only want to retain
 * a particular subtype.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
inline fun  Gen.filterIsInstance(): Gen {
   val outer = this
   return object : Gen {
      override fun constants(): Iterable = outer.constants().filterIsInstance()
      override fun random(seed: Long?): Sequence = outer.random(seed).filterIsInstance()
   }
}

/**
 * Create a infinite sequence of the given [generator]
 *
 * ```kotlin
 * // Example
 * generateInfiniteSequence { 42 }
 *     .take(3)
 *     .joinToString()
 *     .let { println(it) }
 * // Prints: 42, 42, 42
 * ```
 *
 * @return a new [Sequence] of [T] which always returns the result of [generator]
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
inline fun  generateInfiniteSequence(crossinline generator: () -> T): Sequence =
   Sequence {
      object : Iterator {
         override fun hasNext() = true
         override fun next() = generator()
      }
   }

/**
 * Draws [amount] values from this generator
 *
 * This method will draw values from the generator until it generates [amount] values. This first draws from the
 * [constants] pool, and if necessary starts drawing from the [random] pool.
 *
 * This is useful if you want the generated values, but don't want to execute a property test over them (for example,
 * by using [assertAll] or [forAll]
 *
 * ```
 * val gen = Gen.string()
 * val generatedValues: List = gen.take(20)
 * ```
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.take(amount: Int, seed: Long? = null): List {
   require(amount >= 0) { "Amount must be >= 0, but was $amount" }

   if (amount == 0) return emptyList()

   val generatedValues = (constants() + random(seed).take(amount)).take(amount)
   val generatedSize = generatedValues.size

   check(generatedSize == amount) { "Gen could only generate $generatedSize values while you requested $amount." }
   return generatedValues
}

/**
 * Draws a random value from this generator
 *
 * This method will draw a single value from the [random] values, that matches [predicate] (defaults to every
 * value)
 *
 * This expects that [random] will return an infinite, random sequence. Due to this, a call to [Sequence.first] is
 * made. As usually random is infinite, this should always return a different value. For fixed sequences, this will
 * always return the first value of the sequence.
 *
 * This is useful if you want a randomized value, but don't want to execute a property test over them (for example, by
 * using [assertAll] or [forAll]).
 *
 * IMPORTANT: This will not draw from the [constants] pool. Only [random] values.
 *
 * ```
 * val gen = Gen.string()
 * val generatedValue: String = gen.next()
 * val filteredValue: String = gen.next { it != "hello" }
 * ```
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0. Use Arb.single()")
fun  Gen.next(predicate: (T) -> Boolean = { true }, seed: Long?): T {
   return random(seed).first(predicate)
}

/**
 * @return the result of calling [next] with the given [predicate] defaulting seed to `null`
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.next(predicate: (T) -> Boolean = { true }): T = next(predicate, null)

/**
 * Creates a sequence of unique values from the contents of [random], using [seed] to seed the random function.
 */
@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
fun  Gen.uniqueRandoms(seed: Long? = null): Sequence = sequence {
   yieldAll(random(seed).distinct())
}

@Deprecated("Deprecated and will be removed in 5.0. Migrate to the new property test classes in 4.0")
internal fun getRandomFor(aSeed:Long?):Random {
   return if (aSeed == null) Random.Default else Random(aSeed)
}