Compare commits
4 commits
Author | SHA1 | Date | |
---|---|---|---|
2982d729bf | |||
32a69d734b | |||
8e01e9d6c8 | |||
40fe036ce4 |
11 changed files with 441 additions and 46 deletions
|
@ -1,5 +1,5 @@
|
|||
// See: https://github.com/scalameta/scalafmt/tags for the latest tags.
|
||||
version = 3.8.1
|
||||
version = 3.8.2
|
||||
runner.dialect = scala3
|
||||
maxColumn = 80
|
||||
|
||||
|
|
|
@ -7,14 +7,18 @@ Test data generation library for Scala 3. `gs-datagen` provides _composable_
|
|||
generator definitions that can be reused across tests.
|
||||
|
||||
- [Usage](#usage)
|
||||
- [Dependency](#dependency)
|
||||
- [Imports](#imports)
|
||||
- [Examples](#examples)
|
||||
- [Example: Generate a Random User](#example-generate-a-random-user)
|
||||
- [Example: Require Input](#example-require-input)
|
||||
- [Supported Generators](#supported-generators)
|
||||
- [Donate](#donate)
|
||||
|
||||
## Usage
|
||||
|
||||
### Dependency
|
||||
|
||||
```
|
||||
object Gs {
|
||||
val Datagen: ModuleID =
|
||||
|
@ -28,7 +32,7 @@ The standard way to use `gs-datagen` is to import the entire package, which
|
|||
pulls in `Gen[A]`, `Generated[A]` and `Datagen[A, -I]`:
|
||||
|
||||
```scala
|
||||
import gs.datagen.v0.*
|
||||
import gs.datagen.v0._
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
val scala3: String = "3.4.1"
|
||||
val scala3: String = "3.6.4"
|
||||
|
||||
ThisBuild / scalaVersion := scala3
|
||||
ThisBuild / versionScheme := Some("semver-spec")
|
||||
|
@ -22,7 +22,7 @@ val sharedSettings = Seq(
|
|||
|
||||
lazy val testSettings = Seq(
|
||||
libraryDependencies ++= Seq(
|
||||
"org.scalameta" %% "munit" % "1.0.0-M12" % Test
|
||||
"org.scalameta" %% "munit" % "1.1.0" % Test
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -39,6 +39,6 @@ lazy val core = project
|
|||
.settings(name := s"${gsProjectName.value}-core-v${semVerMajor.value}")
|
||||
.settings(
|
||||
libraryDependencies ++= Seq(
|
||||
"gs" %% "gs-uuid-v0" % "0.2.3"
|
||||
"gs" %% "gs-uuid-v0" % "0.4.0"
|
||||
)
|
||||
)
|
||||
|
|
|
@ -7,11 +7,23 @@ trait Generated[A]:
|
|||
|
||||
object Generated:
|
||||
|
||||
/** Summon an instance of the [[Generated]] type class.
|
||||
*
|
||||
* @param G
|
||||
* The type class instance.
|
||||
* @return
|
||||
* The summoned instance of [[Generated]].
|
||||
*/
|
||||
def apply[A](
|
||||
using
|
||||
G: Generated[A]
|
||||
): Generated[A] = G
|
||||
|
||||
/** Implementation of [[Generated]] based on an instance of [[Gen]].
|
||||
*
|
||||
* @param gen
|
||||
* The underlying [[Gen]] used to produce values.
|
||||
*/
|
||||
final class FromGenerator[A](gen: Gen[A]) extends Generated[A]:
|
||||
override def generate(): A = gen.gen()
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package gs.datagen.v0
|
||||
|
||||
import gs.datagen.v0.generators.Alphabet
|
||||
import gs.datagen.v0.generators.GenBool
|
||||
import gs.datagen.v0.generators.GenFiniteDuration
|
||||
import gs.datagen.v0.generators.GenInteger
|
||||
import gs.datagen.v0.generators.GenList
|
||||
|
@ -9,16 +10,17 @@ import gs.datagen.v0.generators.GenLong
|
|||
import gs.datagen.v0.generators.GenMap
|
||||
import gs.datagen.v0.generators.GenOneOf
|
||||
import gs.datagen.v0.generators.GenOneOfGen
|
||||
import gs.datagen.v0.generators.GenOption
|
||||
import gs.datagen.v0.generators.GenSet
|
||||
import gs.datagen.v0.generators.GenString
|
||||
import gs.datagen.v0.generators.GenUUID
|
||||
import gs.datagen.v0.generators.MinMax
|
||||
import gs.datagen.v0.generators.Size
|
||||
import gs.uuid.v0.UUID
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.util.Random
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import scala.concurrent.duration.DAYS
|
||||
|
@ -88,11 +90,66 @@ object Gen:
|
|||
*/
|
||||
def single[A](value: A): Gen[A] = Datagen.pure(value)
|
||||
|
||||
/** Generator for a list of some [[Size]] based on a generator for the list
|
||||
* elements.
|
||||
/** Generator which produces Boolean values.
|
||||
*
|
||||
* @return
|
||||
* New generator that randomly produces true/false.
|
||||
*/
|
||||
def boolean(): Gen[Boolean] = new GenBool()
|
||||
|
||||
object option:
|
||||
|
||||
/** Generator that will randomly determine whether `Some` or `None` is
|
||||
* returned for the given underlying generator.
|
||||
*
|
||||
* @param generator
|
||||
* The generator that populates the content.
|
||||
* @return
|
||||
* The generator for the optional content.
|
||||
*/
|
||||
def apply[A](generator: Gen[A]): Gen[Option[A]] =
|
||||
GenOption[A](generator)
|
||||
|
||||
/** Generator that will randomly determine whether `Some` or `None` is
|
||||
* returned for the given underlying generator.
|
||||
*
|
||||
* @param generator
|
||||
* The generator that populates the content.
|
||||
* @return
|
||||
* The generator for the optional content.
|
||||
*/
|
||||
def random[A](generator: Gen[A]): Gen[Option[A]] =
|
||||
apply(generator)
|
||||
|
||||
/** Generator that will always return `Some`, using some underlying
|
||||
* generator.
|
||||
*
|
||||
* @param generator
|
||||
* The generator that populates the content.
|
||||
* @return
|
||||
* The generator for the optional content.
|
||||
*/
|
||||
def alwaysSome[A](generator: Gen[A]): GenOption[A] =
|
||||
GenOption.alwaysSome(generator)
|
||||
|
||||
/** Generator that will always return `None`, and therefore will never
|
||||
* actually invoke the underlying generator.
|
||||
*
|
||||
* @param generator
|
||||
* The generator that populates the content.
|
||||
* @return
|
||||
* The generator for the optional content.
|
||||
*/
|
||||
def alwaysNone[A](generator: Gen[A]): GenOption[A] =
|
||||
GenOption.alwaysNone(generator)
|
||||
|
||||
end option
|
||||
|
||||
/** Generator for a list of some [[gs.datagen.v0.generators.Size]] based on a
|
||||
* generator for the list elements.
|
||||
*
|
||||
* @param size
|
||||
* The [[Size]] of the list.
|
||||
* The [[gs.datagen.v0.generators.Size]] of the list.
|
||||
* @param gen
|
||||
* The generator for the list elements.
|
||||
*/
|
||||
|
@ -101,12 +158,12 @@ object Gen:
|
|||
gen: Gen[A]
|
||||
): Gen[List[A]] = new GenList[A](size, gen)
|
||||
|
||||
/** Generator for a set of some [[Size]] based on a generator for the set
|
||||
* elements.
|
||||
/** Generator for a set of some [[gs.datagen.v0.generators.Size]] based on a
|
||||
* generator for the set elements.
|
||||
*
|
||||
* @param size
|
||||
* The goal [[Size]] of the set. If duplicate elements are generated, the
|
||||
* size will be less then specified.
|
||||
* The goal [[gs.datagen.v0.generators.Size]] of the set. If duplicate
|
||||
* elements are generated, the size will be less then specified.
|
||||
* @param gen
|
||||
* The generator for the list elements.
|
||||
*/
|
||||
|
@ -177,7 +234,7 @@ object Gen:
|
|||
* values.
|
||||
*
|
||||
* @param size
|
||||
* The [[Size]] of the generated map.
|
||||
* The [[gs.datagen.v0.generators.Size]] of the generated map.
|
||||
* @param keyGen
|
||||
* The generator for keys.
|
||||
* @param valueGen
|
||||
|
@ -198,7 +255,7 @@ object Gen:
|
|||
* keys to values.
|
||||
*
|
||||
* @param size
|
||||
* The [[Size]] of the generated map.
|
||||
* The [[gs.datagen.v0.generators.Size]] of the generated map.
|
||||
* @param keyValueGen
|
||||
* The generator for key/value pairs.
|
||||
*/
|
||||
|
@ -217,8 +274,9 @@ object Gen:
|
|||
*/
|
||||
object string:
|
||||
|
||||
/** Generator for a string of the specified [[Size]], where the characters
|
||||
* are restricted by the given alphabet.
|
||||
/** Generator for a string of the specified
|
||||
* [[gs.datagen.v0.generators.Size]], where the characters are restricted
|
||||
* by the given alphabet.
|
||||
*
|
||||
* @param size
|
||||
* The size constraints for the generated string.
|
||||
|
@ -234,8 +292,9 @@ object Gen:
|
|||
size = size
|
||||
)
|
||||
|
||||
/** Generator for a string of the specified [[Size]], where the characters
|
||||
* are restricted to ASCII lowercase letters.
|
||||
/** Generator for a string of the specified
|
||||
* [[gs.datagen.v0.generators.Size]], where the characters are restricted
|
||||
* to ASCII lowercase letters.
|
||||
*
|
||||
* @param size
|
||||
* The size constraints for the generated string.
|
||||
|
@ -248,8 +307,9 @@ object Gen:
|
|||
size = size
|
||||
)
|
||||
|
||||
/** Generator for a string of the specified [[Size]], where the characters
|
||||
* are restricted to ASCII uppercase letters.
|
||||
/** Generator for a string of the specified
|
||||
* [[gs.datagen.v0.generators.Size]], where the characters are restricted
|
||||
* to ASCII uppercase letters.
|
||||
*
|
||||
* @param size
|
||||
* The size constraints for the generated string.
|
||||
|
@ -262,8 +322,9 @@ object Gen:
|
|||
size = size
|
||||
)
|
||||
|
||||
/** Generator for a string of the specified [[Size]], where the characters
|
||||
* are restricted to ASCII letters.
|
||||
/** Generator for a string of the specified
|
||||
* [[gs.datagen.v0.generators.Size]], where the characters are restricted
|
||||
* to ASCII letters.
|
||||
*
|
||||
* @param size
|
||||
* The size constraints for the generated string.
|
||||
|
@ -276,8 +337,9 @@ object Gen:
|
|||
size = size
|
||||
)
|
||||
|
||||
/** Generator for a string of the specified [[Size]], where the characters
|
||||
* are restricted to ASCII letters and numbers.
|
||||
/** Generator for a string of the specified
|
||||
* [[gs.datagen.v0.generators.Size]], where the characters are restricted
|
||||
* to ASCII letters and numbers.
|
||||
*
|
||||
* @param size
|
||||
* The size constraints for the generated string.
|
||||
|
@ -290,8 +352,9 @@ object Gen:
|
|||
size = size
|
||||
)
|
||||
|
||||
/** Generator for a string of the specified [[Size]], where the characters
|
||||
* are restricted to lowercase ASCII letters and numbers.
|
||||
/** Generator for a string of the specified
|
||||
* [[gs.datagen.v0.generators.Size]], where the characters are restricted
|
||||
* to lowercase ASCII letters and numbers.
|
||||
*
|
||||
* @param size
|
||||
* The size constraints for the generated string.
|
||||
|
@ -304,8 +367,9 @@ object Gen:
|
|||
size = size
|
||||
)
|
||||
|
||||
/** Generator for a string of the specified [[Size]], where the characters
|
||||
* are restricted to uppercase ASCII letters and numbers.
|
||||
/** Generator for a string of the specified
|
||||
* [[gs.datagen.v0.generators.Size]], where the characters are restricted
|
||||
* to uppercase ASCII letters and numbers.
|
||||
*
|
||||
* @param size
|
||||
* The size constraints for the generated string.
|
||||
|
@ -320,7 +384,7 @@ object Gen:
|
|||
|
||||
end string
|
||||
|
||||
/** Generators for integers.
|
||||
/** Generators for integers (32-bit integers).
|
||||
*/
|
||||
object integer:
|
||||
|
||||
|
@ -354,7 +418,7 @@ object Gen:
|
|||
|
||||
end integer
|
||||
|
||||
/** Generators for longs.
|
||||
/** Generators for longs (64-bit integers).
|
||||
*/
|
||||
object long:
|
||||
|
||||
|
@ -396,7 +460,7 @@ object Gen:
|
|||
*
|
||||
* ## Example
|
||||
*
|
||||
* The following generator produces a date before today where:
|
||||
* The following generator produces a date before the given date where:
|
||||
*
|
||||
* - The day component is subtracted by 0 to 10.
|
||||
* - The month component is subtracted by 1 to 11.
|
||||
|
@ -437,6 +501,37 @@ object Gen:
|
|||
years = years
|
||||
)
|
||||
|
||||
/** Generate a date before the current date.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* The following generator produces a date before **today** where:
|
||||
*
|
||||
* - The day component is subtracted by 0 to 10.
|
||||
* - The month component is subtracted by 1 to 11.
|
||||
* - The year component is subtracted by 0 to 30.
|
||||
*
|
||||
* The produced date will be, at most, 30 years, 11 months and 10 days
|
||||
* prior to the selected pivot. It will be at least 1 month prior to the
|
||||
* selected pivot.
|
||||
*
|
||||
* {{{
|
||||
* Gen.date.beforeToday(
|
||||
* days = MinMax.nonNegative(0, 10),
|
||||
* months = MinMax.nonNegative(1, 11),
|
||||
* years = MinMax.nonNegative(0, 30)
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* @param date
|
||||
* The maximum date.
|
||||
* @param days
|
||||
* The range of days prior to today.
|
||||
* @param months
|
||||
* The range of months prior to today.
|
||||
* @param years
|
||||
* The range of years prior to today.
|
||||
*/
|
||||
def beforeToday(
|
||||
days: MinMax.NonNegative,
|
||||
months: MinMax.NonNegative = MinMax.Zero,
|
||||
|
@ -450,6 +545,38 @@ object Gen:
|
|||
years = years
|
||||
)
|
||||
|
||||
/** Generate a date after the given date.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* The following generator produces a date after the given date where:
|
||||
*
|
||||
* - The day component is added by 0 to 10.
|
||||
* - The month component is added by 1 to 11.
|
||||
* - The year component is added by 0 to 30.
|
||||
*
|
||||
* The produced date will be, at most, 30 years, 11 months and 10 days
|
||||
* after the selected pivot. It will be at least 1 month after the selected
|
||||
* pivot.
|
||||
*
|
||||
* {{{
|
||||
* Gen.date.after(
|
||||
* date = LocalDate.now(),
|
||||
* days = MinMax.nonNegative(0, 10),
|
||||
* months = MinMax.nonNegative(1, 11),
|
||||
* years = MinMax.nonNegative(0, 30)
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* @param date
|
||||
* The maximum date.
|
||||
* @param days
|
||||
* The range of days after the given date.
|
||||
* @param months
|
||||
* The range of months after the given date.
|
||||
* @param years
|
||||
* The range of years after the given date.
|
||||
*/
|
||||
def after(
|
||||
date: LocalDate,
|
||||
days: MinMax.NonNegative,
|
||||
|
@ -463,6 +590,38 @@ object Gen:
|
|||
years = years
|
||||
)
|
||||
|
||||
/** Generate a date after the current date.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* The following generator produces a date after the **current** date
|
||||
* where:
|
||||
*
|
||||
* - The day component is added by 0 to 10.
|
||||
* - The month component is added by 1 to 11.
|
||||
* - The year component is added by 0 to 30.
|
||||
*
|
||||
* The produced date will be, at most, 30 years, 11 months and 10 days
|
||||
* after the selected pivot. It will be at least 1 month after the selected
|
||||
* pivot.
|
||||
*
|
||||
* {{{
|
||||
* Gen.date.afterToday(
|
||||
* days = MinMax.nonNegative(0, 10),
|
||||
* months = MinMax.nonNegative(1, 11),
|
||||
* years = MinMax.nonNegative(0, 30)
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* @param date
|
||||
* The maximum date.
|
||||
* @param days
|
||||
* The range of days after today.
|
||||
* @param months
|
||||
* The range of months after today.
|
||||
* @param years
|
||||
* The range of years after today.
|
||||
*/
|
||||
def afterToday(
|
||||
days: MinMax.NonNegative,
|
||||
months: MinMax.NonNegative = MinMax.Zero,
|
||||
|
@ -476,26 +635,89 @@ object Gen:
|
|||
years = years
|
||||
)
|
||||
|
||||
/** Generate a date around (centered upon) the given date.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* The following generator produces a date around the given date where:
|
||||
*
|
||||
* - The day component is added or subtracted by 0 to 10.
|
||||
* - The month component is added or subtracted by 1 to 11.
|
||||
* - The year component is added or subtracted by 0 to 30.
|
||||
*
|
||||
* The produced date will be, at most, 30 years, 11 months and 10 days
|
||||
* before or after the selected pivot. It will be at least 1 month before
|
||||
* or after the selected pivot.
|
||||
*
|
||||
* {{{
|
||||
* Gen.date.around(
|
||||
* date = LocalDate.now(),
|
||||
* days = MinMax.nonNegative(0, 10),
|
||||
* months = MinMax.nonNegative(1, 11),
|
||||
* years = MinMax.nonNegative(0, 30)
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* @param date
|
||||
* The maximum date.
|
||||
* @param days
|
||||
* The range of days before or after the given date.
|
||||
* @param months
|
||||
* The range of months before or after the given date.
|
||||
* @param years
|
||||
* The range of years before or after the given date.
|
||||
*/
|
||||
def around(
|
||||
date: LocalDate,
|
||||
days: MinMax,
|
||||
months: MinMax = MinMax.Zero,
|
||||
years: MinMax = MinMax.Zero
|
||||
): Gen[LocalDate] =
|
||||
new GenLocalDate.After(
|
||||
new GenLocalDate.Around(
|
||||
pivot = date,
|
||||
days = days,
|
||||
months = months,
|
||||
years = years
|
||||
)
|
||||
|
||||
/** Generate a date around (centered upon) today.
|
||||
*
|
||||
* ## Example
|
||||
*
|
||||
* The following generator produces a date around today where:
|
||||
*
|
||||
* - The day component is added or subtracted by 0 to 10.
|
||||
* - The month component is added or subtracted by 1 to 11.
|
||||
* - The year component is added or subtracted by 0 to 30.
|
||||
*
|
||||
* The produced date will be, at most, 30 years, 11 months and 10 days
|
||||
* before or after today. It will be at least 1 month before or after
|
||||
* today.
|
||||
*
|
||||
* {{{
|
||||
* Gen.date.around(
|
||||
* days = MinMax.nonNegative(0, 10),
|
||||
* months = MinMax.nonNegative(1, 11),
|
||||
* years = MinMax.nonNegative(0, 30)
|
||||
* )
|
||||
* }}}
|
||||
*
|
||||
* @param date
|
||||
* The maximum date.
|
||||
* @param days
|
||||
* The range of days before or after today.
|
||||
* @param months
|
||||
* The range of months before or after today.
|
||||
* @param years
|
||||
* The range of years before or after today.
|
||||
*/
|
||||
def aroundToday(
|
||||
days: MinMax,
|
||||
months: MinMax = MinMax.Zero,
|
||||
years: MinMax = MinMax.Zero,
|
||||
clock: Clock = DefaultClock
|
||||
): Gen[LocalDate] =
|
||||
new GenLocalDate.After(
|
||||
new GenLocalDate.Around(
|
||||
pivot = LocalDate.now(clock),
|
||||
days = days,
|
||||
months = months,
|
||||
|
@ -504,7 +726,7 @@ object Gen:
|
|||
|
||||
end date
|
||||
|
||||
/** Geneators for `java.util.UUID` values.
|
||||
/** Geneators for GS `UUID` values.
|
||||
*/
|
||||
object uuid:
|
||||
|
||||
|
@ -514,12 +736,7 @@ object Gen:
|
|||
|
||||
/** Generator for UUIDs represented as strings.
|
||||
*/
|
||||
def string(): Gen[String] = uuid.random().map(id => id.toString)
|
||||
|
||||
/** Generator for UUIDs represented as strings, without dashes.
|
||||
*/
|
||||
def noDashes(): Gen[String] =
|
||||
uuid.random().map(id => id.toString.replace("-", ""))
|
||||
def string(): Gen[String] = uuid.random().map(id => id.str())
|
||||
|
||||
end uuid
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package gs.datagen.v0.generators
|
||||
|
||||
import gs.datagen.v0.Gen
|
||||
|
||||
final class GenBool() extends Gen[Boolean]:
|
||||
|
||||
override def generate(input: Any): Boolean =
|
||||
Gen.rng().nextBoolean()
|
|
@ -2,11 +2,28 @@ package gs.datagen.v0.generators
|
|||
|
||||
import gs.datagen.v0.Gen
|
||||
import java.time.LocalDate
|
||||
import java.util.Random
|
||||
|
||||
/** Base for generators that produce `java.time.LocalDate` values.
|
||||
*/
|
||||
trait GenLocalDate extends Gen[LocalDate]
|
||||
|
||||
/** Provids implementations for [[GenLocalDate]].
|
||||
*/
|
||||
object GenLocalDate:
|
||||
|
||||
/** Implementation of [[GenLocalDate]] that selects random dates before some
|
||||
* given date.
|
||||
*
|
||||
* @param pivot
|
||||
* The pivot date before which random values are selected.
|
||||
* @param days
|
||||
* The day bound.
|
||||
* @param months
|
||||
* The month bound.
|
||||
* @param years
|
||||
* The year bound.
|
||||
*/
|
||||
final class Before(
|
||||
val pivot: LocalDate,
|
||||
val days: MinMax,
|
||||
|
@ -14,6 +31,8 @@ object GenLocalDate:
|
|||
val years: MinMax
|
||||
) extends GenLocalDate:
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def generate(input: Any): LocalDate =
|
||||
val rng = Gen.rng()
|
||||
pivot
|
||||
|
@ -21,6 +40,18 @@ object GenLocalDate:
|
|||
.minusMonths(months.select(rng).toInt)
|
||||
.minusYears(years.select(rng).toInt)
|
||||
|
||||
/** Implementation of [[GenLocalDate]] that selects random dates after some
|
||||
* given date.
|
||||
*
|
||||
* @param pivot
|
||||
* The pivot date after which random values are selected.
|
||||
* @param days
|
||||
* The day bound.
|
||||
* @param months
|
||||
* The month bound.
|
||||
* @param years
|
||||
* The year bound.
|
||||
*/
|
||||
final class After(
|
||||
val pivot: LocalDate,
|
||||
val days: MinMax,
|
||||
|
@ -28,6 +59,8 @@ object GenLocalDate:
|
|||
val years: MinMax
|
||||
) extends GenLocalDate:
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def generate(input: Any): LocalDate =
|
||||
val rng = Gen.rng()
|
||||
pivot
|
||||
|
@ -35,4 +68,82 @@ object GenLocalDate:
|
|||
.plusMonths(months.select(rng).toInt)
|
||||
.plusYears(years.select(rng).toInt)
|
||||
|
||||
/** Implementation of [[GenLocalDate]] that selects random dates centered on
|
||||
* some given date.
|
||||
*
|
||||
* @param pivot
|
||||
* The pivot date around which random values are selected.
|
||||
* @param days
|
||||
* The day bound.
|
||||
* @param months
|
||||
* The month bound.
|
||||
* @param years
|
||||
* The year bound.
|
||||
*/
|
||||
final class Around(
|
||||
val pivot: LocalDate,
|
||||
val days: MinMax,
|
||||
val months: MinMax,
|
||||
val years: MinMax
|
||||
) extends GenLocalDate:
|
||||
|
||||
/** @inheritDocs
|
||||
*/
|
||||
override def generate(input: Any): LocalDate =
|
||||
val rng = Gen.rng()
|
||||
pivot
|
||||
.plusOrMinusDays(rng, days)
|
||||
.plusOrMinusMonths(rng, months)
|
||||
.plusOrMinusYears(rng, years)
|
||||
|
||||
extension (base: LocalDate)
|
||||
|
||||
/** Select a bounded random number of days before or after the base date.
|
||||
*
|
||||
* @param rng
|
||||
* The random generator.
|
||||
* @param range
|
||||
* The bounded range.
|
||||
* @return
|
||||
* The new date.
|
||||
*/
|
||||
def plusOrMinusDays(
|
||||
rng: Random,
|
||||
range: MinMax
|
||||
): LocalDate =
|
||||
if rng.nextBoolean() then base.plusDays(range.select(rng).toInt)
|
||||
else base.minusDays(range.select(rng).toInt)
|
||||
|
||||
/** Select a bounded random number of months before or after the base date.
|
||||
*
|
||||
* @param rng
|
||||
* The random generator.
|
||||
* @param range
|
||||
* The bounded range.
|
||||
* @return
|
||||
* The new date.
|
||||
*/
|
||||
def plusOrMinusMonths(
|
||||
rng: Random,
|
||||
range: MinMax
|
||||
): LocalDate =
|
||||
if rng.nextBoolean() then base.plusMonths(range.select(rng).toInt)
|
||||
else base.minusMonths(range.select(rng).toInt)
|
||||
|
||||
/** Select a bounded random number of years before or after the base date.
|
||||
*
|
||||
* @param rng
|
||||
* The random generator.
|
||||
* @param range
|
||||
* The bounded range.
|
||||
* @return
|
||||
* The new date.
|
||||
*/
|
||||
def plusOrMinusYears(
|
||||
rng: Random,
|
||||
range: MinMax
|
||||
): LocalDate =
|
||||
if rng.nextBoolean() then base.plusYears(range.select(rng).toInt)
|
||||
else base.minusYears(range.select(rng).toInt)
|
||||
|
||||
end GenLocalDate
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package gs.datagen.v0.generators
|
||||
|
||||
import GenOption.DeterminationType
|
||||
import gs.datagen.v0.Gen
|
||||
|
||||
final class GenOption[A](
|
||||
val determinationType: DeterminationType,
|
||||
val generator: Gen[A]
|
||||
) extends Gen[Option[A]]:
|
||||
|
||||
override def generate(input: Any): Option[A] =
|
||||
determinationType match
|
||||
case DeterminationType.AlwaysSome =>
|
||||
Some(generator.gen())
|
||||
case DeterminationType.AlwaysNone =>
|
||||
None
|
||||
case DeterminationType.Random =>
|
||||
if Gen.rng().nextBoolean() then Some(generator.gen()) else None
|
||||
|
||||
object GenOption:
|
||||
|
||||
def apply[A](generator: Gen[A]): GenOption[A] =
|
||||
new GenOption[A](DeterminationType.Random, generator)
|
||||
|
||||
def random[A](generator: Gen[A]): GenOption[A] = apply[A](generator)
|
||||
|
||||
def alwaysSome[A](generator: Gen[A]): GenOption[A] =
|
||||
new GenOption[A](DeterminationType.AlwaysSome, generator)
|
||||
|
||||
def alwaysNone[A](generator: Gen[A]): GenOption[A] =
|
||||
new GenOption[A](DeterminationType.AlwaysNone, generator)
|
||||
|
||||
enum DeterminationType:
|
||||
case AlwaysSome, AlwaysNone, Random
|
||||
|
||||
object DeterminationType:
|
||||
|
||||
given CanEqual[DeterminationType, DeterminationType] = CanEqual.derived
|
||||
|
||||
end DeterminationType
|
||||
|
||||
end GenOption
|
|
@ -1,9 +1,10 @@
|
|||
package gs.datagen.v0.generators
|
||||
|
||||
import gs.datagen.v0.Gen
|
||||
import java.util.UUID
|
||||
import gs.uuid.v0.UUID
|
||||
|
||||
final class GenUUID extends Gen[UUID]:
|
||||
given UUID.Generator = UUID.Generator.version4
|
||||
|
||||
override def generate(input: Any): UUID =
|
||||
UUID.randomUUID()
|
||||
UUID.generate()
|
||||
|
|
|
@ -1 +1 @@
|
|||
sbt.version=1.9.9
|
||||
sbt.version=1.10.11
|
||||
|
|
|
@ -28,6 +28,6 @@ externalResolvers := Seq(
|
|||
"Garrity Software Releases" at "https://maven.garrity.co/gs"
|
||||
)
|
||||
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.11")
|
||||
addSbtPlugin("gs" % "sbt-garrity-software" % "0.3.0")
|
||||
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.3.1")
|
||||
addSbtPlugin("gs" % "sbt-garrity-software" % "0.5.0")
|
||||
addSbtPlugin("gs" % "sbt-gs-semver" % "0.3.0")
|
||||
|
|
Loading…
Add table
Reference in a new issue