Documentation
¶
Overview ¶
Package rapid implements utilities for property-based testing.
Rapid checks that properties you define hold for a large number of automatically generated test cases. If a failure is found, rapid fails the current test and presents an automatically minimized version of the failing test case.
Here is what a trivial test using rapid looks like:
package rapid_test
import (
"net"
"testing"
"pgregory.net/rapid"
)
func TestParseValidIPv4(t *testing.T) {
const ipv4re = `(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` +
`\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` +
`\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])` +
`\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])`
rapid.Check(t, func(t *rapid.T) {
addr := rapid.StringMatching(ipv4re).Draw(t, "addr")
ip := net.ParseIP(addr)
if ip == nil || ip.String() != addr {
t.Fatalf("parsed %q into %v", addr, ip)
}
})
}
Index ¶
- func Check(t *testing.T, prop func(*T))
- func ID[V any](v V) V
- func MakeCheck(prop func(*T)) func(*testing.T)
- func MakeFuzz(prop func(*T)) func(*testing.T, []byte)
- func Run[M StateMachine]() func(*T)
- type Generator
- func Bool() *Generator[bool]
- func Byte() *Generator[byte]
- func ByteMax(max byte) *Generator[byte]
- func ByteMin(min byte) *Generator[byte]
- func ByteRange(min byte, max byte) *Generator[byte]
- func Custom[V any](fn func(*T) V) *Generator[V]
- func Deferred[V any](fn func() *Generator[V]) *Generator[V]
- func Float32() *Generator[float32]
- func Float32Max(max float32) *Generator[float32]
- func Float32Min(min float32) *Generator[float32]
- func Float32Range(min float32, max float32) *Generator[float32]
- func Float64() *Generator[float64]
- func Float64Max(max float64) *Generator[float64]
- func Float64Min(min float64) *Generator[float64]
- func Float64Range(min float64, max float64) *Generator[float64]
- func Int() *Generator[int]
- func Int8() *Generator[int8]
- func Int8Max(max int8) *Generator[int8]
- func Int8Min(min int8) *Generator[int8]
- func Int8Range(min int8, max int8) *Generator[int8]
- func Int16() *Generator[int16]
- func Int16Max(max int16) *Generator[int16]
- func Int16Min(min int16) *Generator[int16]
- func Int16Range(min int16, max int16) *Generator[int16]
- func Int32() *Generator[int32]
- func Int32Max(max int32) *Generator[int32]
- func Int32Min(min int32) *Generator[int32]
- func Int32Range(min int32, max int32) *Generator[int32]
- func Int64() *Generator[int64]
- func Int64Max(max int64) *Generator[int64]
- func Int64Min(min int64) *Generator[int64]
- func Int64Range(min int64, max int64) *Generator[int64]
- func IntMax(max int) *Generator[int]
- func IntMin(min int) *Generator[int]
- func IntRange(min int, max int) *Generator[int]
- func Just[V any](val V) *Generator[V]
- func Make[V any]() *Generator[V]
- func MapOf[K comparable, V any](key *Generator[K], val *Generator[V]) *Generator[map[K]V]
- func MapOfN[K comparable, V any](key *Generator[K], val *Generator[V], minLen int, maxLen int) *Generator[map[K]V]
- func MapOfNValues[K comparable, V any](val *Generator[V], minLen int, maxLen int, keyFn func(V) K) *Generator[map[K]V]
- func MapOfValues[K comparable, V any](val *Generator[V], keyFn func(V) K) *Generator[map[K]V]
- func OneOf[V any](gens ...*Generator[V]) *Generator[V]
- func Permutation[S ~[]E, E any](s S) *Generator[S]
- func Ptr[E any](elem *Generator[E], allowNil bool) *Generator[*E]
- func Rune() *Generator[rune]
- func RuneFrom(runes []rune, tables ...*unicode.RangeTable) *Generator[rune]
- func SampledFrom[S ~[]E, E any](slice S) *Generator[E]
- func SliceOf[E any](elem *Generator[E]) *Generator[[]E]
- func SliceOfBytesMatching(expr string) *Generator[[]byte]
- func SliceOfDistinct[E any, K comparable](elem *Generator[E], keyFn func(E) K) *Generator[[]E]
- func SliceOfN[E any](elem *Generator[E], minLen int, maxLen int) *Generator[[]E]
- func SliceOfNDistinct[E any, K comparable](elem *Generator[E], minLen int, maxLen int, keyFn func(E) K) *Generator[[]E]
- func String() *Generator[string]
- func StringMatching(expr string) *Generator[string]
- func StringN(minRunes int, maxRunes int, maxLen int) *Generator[string]
- func StringOf(elem *Generator[rune]) *Generator[string]
- func StringOfN(elem *Generator[rune], minRunes int, maxRunes int, maxLen int) *Generator[string]
- func Transform[U any, V any](g *Generator[U], fn func(U) V) *Generator[V]
- func Uint() *Generator[uint]
- func Uint8() *Generator[uint8]
- func Uint8Max(max uint8) *Generator[uint8]
- func Uint8Min(min uint8) *Generator[uint8]
- func Uint8Range(min uint8, max uint8) *Generator[uint8]
- func Uint16() *Generator[uint16]
- func Uint16Max(max uint16) *Generator[uint16]
- func Uint16Min(min uint16) *Generator[uint16]
- func Uint16Range(min uint16, max uint16) *Generator[uint16]
- func Uint32() *Generator[uint32]
- func Uint32Max(max uint32) *Generator[uint32]
- func Uint32Min(min uint32) *Generator[uint32]
- func Uint32Range(min uint32, max uint32) *Generator[uint32]
- func Uint64() *Generator[uint64]
- func Uint64Max(max uint64) *Generator[uint64]
- func Uint64Min(min uint64) *Generator[uint64]
- func Uint64Range(min uint64, max uint64) *Generator[uint64]
- func UintMax(max uint) *Generator[uint]
- func UintMin(min uint) *Generator[uint]
- func UintRange(min uint, max uint) *Generator[uint]
- func Uintptr() *Generator[uintptr]
- func UintptrMax(max uintptr) *Generator[uintptr]
- func UintptrMin(min uintptr) *Generator[uintptr]
- func UintptrRange(min uintptr, max uintptr) *Generator[uintptr]
- type StateMachine
- type T
- func (t *T) Error(args ...any)
- func (t *T) Errorf(format string, args ...any)
- func (t *T) Fail()
- func (t *T) FailNow()
- func (t *T) Failed() bool
- func (t *T) Fatal(args ...any)
- func (t *T) Fatalf(format string, args ...any)
- func (t *T) Log(args ...any)
- func (t *T) Logf(format string, args ...any)
- func (t *T) Skip(args ...any)
- func (t *T) SkipNow()
- func (t *T) Skipf(format string, args ...any)
- type TB
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Check ¶
Check fails the current test if rapid can find a test case which falsifies prop.
Property is falsified in case of a panic or a call to *T.Fatalf, *T.Fatal, *T.Errorf, *T.Error, *T.FailNow or *T.Fail.
Example (ParseDate) ¶
Rename to TestParseDate(t *testing.T) to make an actual (failing) test.
package main
import (
"fmt"
"strconv"
"testing"
"pgregory.net/rapid"
)
// ParseDate parses dates in the YYYY-MM-DD format.
func ParseDate(s string) (int, int, int, error) {
if len(s) != 10 {
return 0, 0, 0, fmt.Errorf("%q has wrong length: %v instead of 10", s, len(s))
}
if s[4] != '-' || s[7] != '-' {
return 0, 0, 0, fmt.Errorf("'-' separators expected in %q", s)
}
y, err := strconv.Atoi(s[0:4])
if err != nil {
return 0, 0, 0, fmt.Errorf("failed to parse year: %v", err)
}
m, err := strconv.Atoi(s[6:7])
if err != nil {
return 0, 0, 0, fmt.Errorf("failed to parse month: %v", err)
}
d, err := strconv.Atoi(s[8:10])
if err != nil {
return 0, 0, 0, fmt.Errorf("failed to parse day: %v", err)
}
return y, m, d, nil
}
func testParseDate(t *rapid.T) {
y := rapid.IntRange(0, 9999).Draw(t, "y")
m := rapid.IntRange(1, 12).Draw(t, "m")
d := rapid.IntRange(1, 31).Draw(t, "d")
s := fmt.Sprintf("%04d-%02d-%02d", y, m, d)
y_, m_, d_, err := ParseDate(s)
if err != nil {
t.Fatalf("failed to parse date %q: %v", s, err)
}
if y_ != y || m_ != m || d_ != d {
t.Fatalf("got back wrong date: (%d, %d, %d)", y_, m_, d_)
}
}
// Rename to TestParseDate(t *testing.T) to make an actual (failing) test.
func main() {
var t *testing.T
rapid.Check(t, testParseDate)
}
func ID ¶ added in v0.5.0
func ID[V any](v V) V
ID returns its argument as is. ID is a helper for use with SliceOfDistinct and similar functions.
func MakeCheck ¶
MakeCheck is a convenience function for defining subtests suitable for *testing.T.Run. It allows you to write this:
t.Run("subtest name", rapid.MakeCheck(func(t *rapid.T) {
// test code
}))
instead of this:
t.Run("subtest name", func(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
// test code
})
})
func MakeFuzz ¶ added in v0.5.2
MakeFuzz creates a fuzz target for *testing.F.Fuzz:
func FuzzFoo(f *testing.F) {
f.Fuzz(rapid.MakeFuzz(func(t *rapid.T) {
// test code
}))
}
func Run ¶ added in v0.3.1
func Run[M StateMachine]() func(*T)
Run is a convenience function for defining "state machine" tests, to be run by Check or MakeCheck.
State machine test is a pattern for testing stateful systems that looks like this:
m := new(StateMachineType)
m.Init(t) // optional
defer m.Cleanup() // optional
m.Check(t)
for {
m.RandomAction(t)
m.Check(t)
}
Run synthesizes such test from the M type, which must be a pointer, using reflection.
Example (Queue) ¶
Rename to TestQueue(t *testing.T) to make an actual (failing) test.
package main
import (
"testing"
"pgregory.net/rapid"
)
// Queue implements integer queue with a fixed maximum size.
type Queue struct {
buf []int
in int
out int
}
func NewQueue(n int) *Queue {
return &Queue{
buf: make([]int, n+1),
}
}
// Precondition: Size() > 0.
func (q *Queue) Get() int {
i := q.buf[q.out]
q.out = (q.out + 1) % len(q.buf)
return i
}
// Precondition: Size() < n.
func (q *Queue) Put(i int) {
q.buf[q.in] = i
q.in = (q.in + 1) % len(q.buf)
}
func (q *Queue) Size() int {
return (q.in - q.out) % len(q.buf)
}
// queueMachine is a description of a rapid state machine for testing Queue
type queueMachine struct {
q *Queue // queue being tested
n int // maximum queue size
state []int // model of the queue
}
// Init is an action for initializing a queueMachine instance.
func (m *queueMachine) Init(t *rapid.T) {
n := rapid.IntRange(1, 1000).Draw(t, "n")
m.q = NewQueue(n)
m.n = n
}
// Get is a conditional action which removes an item from the queue.
func (m *queueMachine) Get(t *rapid.T) {
if m.q.Size() == 0 {
t.Skip("queue empty")
}
i := m.q.Get()
if i != m.state[0] {
t.Fatalf("got invalid value: %v vs expected %v", i, m.state[0])
}
m.state = m.state[1:]
}
// Put is a conditional action which adds an items to the queue.
func (m *queueMachine) Put(t *rapid.T) {
if m.q.Size() == m.n {
t.Skip("queue full")
}
i := rapid.Int().Draw(t, "i")
m.q.Put(i)
m.state = append(m.state, i)
}
// Check runs after every action and verifies that all required invariants hold.
func (m *queueMachine) Check(t *rapid.T) {
if m.q.Size() != len(m.state) {
t.Fatalf("queue size mismatch: %v vs expected %v", m.q.Size(), len(m.state))
}
}
// Rename to TestQueue(t *testing.T) to make an actual (failing) test.
func main() {
var t *testing.T
rapid.Check(t, rapid.Run[*queueMachine]())
}
Types ¶
type Generator ¶
type Generator[V any] struct { // contains filtered or unexported fields }
func Custom ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
type point struct {
x int
y int
}
gen := rapid.Custom(func(t *rapid.T) point {
return point{
x: rapid.IntRange(-100, 100).Draw(t, "x"),
y: rapid.IntRange(-100, 100).Draw(t, "y"),
}
})
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: {-1 23} {-3 -50} {0 94} {-2 -50} {11 -57}
func Deferred ¶ added in v0.5.0
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func recursive() *rapid.Generator[any] {
return rapid.OneOf(
rapid.Bool().AsAny(),
rapid.SliceOfN(rapid.Deferred(recursive), 1, 2).AsAny(),
)
}
func main() {
gen := recursive()
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: [[[[false] false]]] false [[true [[[true]]]]] true true
func Float32 ¶
Float32 is a shorthand for Float32Range(-math.MaxFloat32, math.MaxFloat32).
func Float32Max ¶
Float32Max is a shorthand for Float32Range(-math.MaxFloat32, max).
func Float32Min ¶
Float32Min is a shorthand for Float32Range(min, math.MaxFloat32).
func Float32Range ¶
Float32Range returns a generator of 32-bit floating-point numbers in range [min, max]. Both min and max can be infinite.
func Float64 ¶
Float64 is a shorthand for Float64Range(-math.MaxFloat64, math.MaxFloat64).
func Float64Max ¶
Float64Max is a shorthand for Float64Range(-math.MaxFloat64, max).
func Float64Min ¶
Float64Min is a shorthand for Float64Range(min, math.MaxFloat64).
func Float64Range ¶
Float64Range returns a generator of 64-bit floating-point numbers in range [min, max]. Both min and max can be infinite.
func Just ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.Just(42)
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: 42 42 42 42 42
func Make ¶ added in v0.5.0
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.Make[map[int]bool]()
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: map[-433:true -261:false -53:false -23:false 1:true 184:false] map[-3:true 0:true] map[4:true] map[-359:true -154:true -71:true -17:false -1:false 590:false 22973756520:true] map[]
Example (Tree) ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
type tree struct {
Value int
Left, Right *tree
}
func (t *tree) String() string {
if t == nil {
return "nil"
}
return fmt.Sprintf("(%s %v %s)", t.Left.String(), t.Value, t.Right.String())
}
func main() {
gen := rapid.Make[*tree]()
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: (nil 1 (nil 184 nil)) (((nil -1 (((((nil -485 ((nil -2 ((((nil -5 nil) -9898554875447 nil) -34709387 ((nil 50440 nil) 113 (((((nil -442 nil) -66090341586 nil) 179745 nil) 494 (((nil -2 nil) 543360606020 nil) 15261837 nil)) -1778 nil))) -21034573818 nil)) -5 nil)) 15606609 nil) 882666 (nil 3 nil)) -12 (nil -2 ((nil 1 nil) -2 (((nil 11 nil) -187307 ((nil -198 (nil -6895 nil)) 12027 (nil -539313 nil))) 1532 (nil 6 nil))))) 1745354 nil)) -2 nil) -3 nil) nil (((nil -15 (nil 6598 nil)) -131 (nil 317121006373596 ((nil 14 ((nil -9223372036854775808 nil) 1 nil)) 14668 nil))) 590 nil) nil
func MapOf ¶
func MapOf[K comparable, V any](key *Generator[K], val *Generator[V]) *Generator[map[K]V]
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.MapOf(rapid.Int(), rapid.StringMatching(`[a-z]+`))
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: map[1:nhlgqwasbggbaociac 561860:r] map[-3752:pizpv -3:bacuabp 0:bi] map[-33086515648293:gewf -264276:b -1313:a -258:v -4:b -2:fdhbzcz 4:ubfsdbowrja 1775:tcozav 8334:lvcprss 376914:braigey] map[-350:h 590:coaaamcasnapgaad] map[]
func MapOfN ¶
func MapOfN[K comparable, V any](key *Generator[K], val *Generator[V], minLen int, maxLen int) *Generator[map[K]V]
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.MapOfN(rapid.Int(), rapid.StringMatching(`[a-z]+`), 5, 5)
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: map[-130450326583:bd -2983:bbdbcs 1:nhlgqwasbggbaociac 31:kmdnpmcbuagzr 561860:r] map[-82024404:d -3752:pizpv -3:bacuabp 0:bi 179745:rzkneb] map[-33086515648293:gewf -258:v 4:ubfsdbowrja 1775:tcozav 8334:lvcprss] map[-4280678227:j -25651:aafmd -3308:o -350:h 590:coaaamcasnapgaad] map[-9614404661322:gsb -378:y 2:paai 4629136912:otg 1476419818092:qign]
func MapOfNValues ¶
func MapOfNValues[K comparable, V any](val *Generator[V], minLen int, maxLen int, keyFn func(V) K) *Generator[map[K]V]
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.MapOfNValues(rapid.StringMatching(`[a-z]+`), 5, 5, func(s string) int { return len(s) })
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: map[1:s 2:dr 3:anc 7:xguehfc 11:sbggbaociac] map[1:b 2:bp 4:ydag 5:jarxz 6:ebzkwa] map[1:j 3:gjl 5:eeeqa 7:stcozav 9:fxmcadagf] map[2:ub 8:waraafmd 10:bfiqcaxazu 16:rjgqimcasnapgaad 17:gckfbljafcedhcvfc] map[1:k 2:ay 3:wzb 4:dign 7:faabhcb]
func MapOfValues ¶
func MapOfValues[K comparable, V any](val *Generator[V], keyFn func(V) K) *Generator[map[K]V]
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.MapOfValues(rapid.StringMatching(`[a-z]+`), func(s string) int { return len(s) })
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: map[2:dr 7:xguehfc 11:sbggbaociac] map[2:bp 5:jarxz 6:ebzkwa] map[1:j 2:aj 3:gjl 4:vayt 5:eeeqa 6:riacaa 7:stcozav 8:mfdhbzcz 9:fxmcadagf 10:bgsbraigey 15:gxongygnxqlovib] map[2:ub 8:waraafmd 10:bfiqcaxazu 16:rjgqimcasnapgaad 17:gckfbljafcedhcvfc] map[]
func OneOf ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.OneOf(rapid.Int32Range(1, 10).AsAny(), rapid.Float32Range(100, 1000).AsAny())
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: 997.0737 10 475.3125 2 9
func Permutation ¶ added in v0.5.3
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.Permutation([]int{1, 2, 3})
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: [2 3 1] [3 2 1] [2 1 3] [3 2 1] [1 2 3]
func Ptr ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.Ptr(rapid.Int(), true)
for i := 0; i < 5; i++ {
v := gen.Example(i)
if v == nil {
fmt.Println("<nil>")
} else {
fmt.Println("(*int)", *v)
}
}
}
Output: (*int) 1 (*int) -3 <nil> (*int) 590 <nil>
func Rune ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.Rune()
for i := 0; i < 25; i++ {
if i%5 == 0 {
fmt.Println()
} else {
fmt.Print(" ")
}
fmt.Printf("%q", gen.Example(i))
}
}
Output: '\n' '\x1b' 'A' 'a' '*' '0' '@' '?' '\'' '\ue05d' '<' '%' '!' '\u0604' 'A' '%' '╷' '~' '!' '/' '\u00ad' '𝪪' '@' '҈' ' '
func RuneFrom ¶
func RuneFrom(runes []rune, tables ...*unicode.RangeTable) *Generator[rune]
Example ¶
package main
import (
"fmt"
"unicode"
"pgregory.net/rapid"
)
func main() {
gens := []*rapid.Generator[rune]{
rapid.RuneFrom([]rune{'A', 'B', 'C'}),
rapid.RuneFrom(nil, unicode.Cyrillic, unicode.Greek),
rapid.RuneFrom([]rune{'⌘'}, &unicode.RangeTable{
R32: []unicode.Range32{{0x1F600, 0x1F64F, 1}},
}),
}
for _, gen := range gens {
for i := 0; i < 5; i++ {
if i > 0 {
fmt.Print(" ")
}
fmt.Printf("%q", gen.Example(i))
}
fmt.Println()
}
}
Output: 'A' 'A' 'A' 'B' 'A' 'Ͱ' 'Ѥ' 'Ͱ' 'ͱ' 'Ϳ' '😀' '⌘' '😀' '😁' '😋'
func SampledFrom ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.SampledFrom([]int{1, 2, 3})
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: 2 3 2 3 1
func SliceOf ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.SliceOf(rapid.Int())
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: [1 -1902 7 -236 14 -433 -1572631 -1 4219826 -50 1414 -3890044391133 -9223372036854775808 5755498240 -10 680558 10 -80458281 0 -27] [-3 -2 -1 -3 -2172865589 -5 -2 -2503553836720] [4 308 -2 21 -5843 3 1 78 6129321692 -59] [590 -131 -15 -769 16 -1 14668 14 -1 -58784] []
func SliceOfBytesMatching ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.SliceOfBytesMatching(`[CAGT]+`)
for i := 0; i < 5; i++ {
fmt.Printf("%q\n", gen.Example(i))
}
}
Output: "CCTTGAGAGCGATACGGAAG" "GCAGAACT" "AACCGTCGAG" "GGGAAAAGAT" "AGTG"
func SliceOfDistinct ¶
func SliceOfDistinct[E any, K comparable](elem *Generator[E], keyFn func(E) K) *Generator[[]E]
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.SliceOfDistinct(rapid.IntMin(0), func(i int) int { return i % 2 })
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: [1] [2 1] [4 1] [590] []
func SliceOfN ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.SliceOfN(rapid.Int(), 5, 5)
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: [1 -1902 7 -236 14] [-3 -2 -1 -3 -2172865589] [4 308 -2 21 -5843] [590 -131 -15 -769 16] [4629136912 270 141395 -129322425838843911 -7]
func SliceOfNDistinct ¶
func SliceOfNDistinct[E any, K comparable](elem *Generator[E], minLen int, maxLen int, keyFn func(E) K) *Generator[[]E]
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.SliceOfNDistinct(rapid.IntMin(0), 2, 2, func(i int) int { return i % 2 })
for i := 0; i < 5; i++ {
fmt.Println(gen.Example(i))
}
}
Output: [4219826 49] [2 1] [4 1] [0 58783] [4629136912 141395]
func String ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.String()
for i := 0; i < 5; i++ {
fmt.Printf("%q\n", gen.Example(i))
}
}
Output: "\n߾⃝?\rA�֍" "\u2006𑰼" "A¢\u0603ᾢ" "+^#.[#৲" ""
func StringMatching ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.StringMatching(`\(?([0-9]{3})\)?([ .-]?)([0-9]{3})([ .-]?)([0-9]{4})`)
for i := 0; i < 5; i++ {
fmt.Printf("%q\n", gen.Example(i))
}
}
Output: "(532) 649-9610" "901)-5783983" "914.444.1575" "(316 696.3584" "816)0861080"
func StringN ¶
Example ¶
package main
import (
"fmt"
"pgregory.net/rapid"
)
func main() {
gen := rapid.StringN(5, 5, -1)
for i := 0; i < 5; i++ {
fmt.Printf("%q\n", gen.Example(i))
}
}
Output: "\n߾⃝?\r" "\u2006𑰼#`\x1b" "A¢\u0603ᾢÉ" "+^#.[" ".A<a¤"
func StringOf ¶
Example ¶
package main
import (
"fmt"
"unicode"
"pgregory.net/rapid"
)
func main() {
gen := rapid.StringOf(rapid.RuneFrom(nil, unicode.Tibetan))
for i := 0; i < 5; i++ {
fmt.Printf("%q\n", gen.Example(i))
}
}
Output: "༁༭༇ཬ༆༐༖ༀྸ༁༆༎ༀ༁ཱི༂༨ༀ༂" "༂༁ༀ༂༴ༀ༁ྵ" "ༀ༴༁༅ན༃༁༎ྼ༄༽" "༎༂༎ༀༀༀཌྷ༂ༀྥ" ""
func StringOfN ¶
Example ¶
package main
import (
"fmt"
"unicode"
"pgregory.net/rapid"
)
func main() {
gen := rapid.StringOfN(rapid.RuneFrom(nil, unicode.ASCII_Hex_Digit), 6, 6, -1)
for i := 0; i < 5; i++ {
fmt.Printf("%q\n", gen.Example(i))
}
}
Output: "1D7B6a" "2102e0" "0e15c3" "E2E000" "aEd623"
func UintptrMax ¶
func UintptrMin ¶
type StateMachine ¶
type StateMachine interface {
// Check is ran after every action and should contain invariant checks.
//
// Other public methods are treated as follows:
// - Init(t *rapid.T), if present, is ran at the beginning of each test case
// to initialize the state machine instance;
// - Cleanup(), if present, is called at the end of each test case;
// - All other public methods should have a form ActionName(t *rapid.T)
// and are used as possible actions. At least one action has to be specified.
//
Check(*T)
}
type T ¶
type T struct {
// contains filtered or unexported fields
}
T is similar to testing.T, but with extra bookkeeping for property-based tests.
For tests to be reproducible, they should generally run in a single goroutine. If concurrency is unavoidable, methods on *T, such as *testing.T.Helper and *T.Errorf, are safe for concurrent calls, but *Generator.Draw from a given *T is not.
func (*T) SkipNow ¶
func (t *T) SkipNow()
SkipNow marks the current test case as invalid (except state machine tests, where it marks current action as non-applicable instead). If too many test cases are skipped, rapid will mark the test as failing due to inability to generate enough valid test cases.
Prefer *Generator.Filter to SkipNow, and prefer generators that always produce valid test cases to Filter.
type TB ¶ added in v0.4.8
type TB interface {
Helper()
Name() string
Logf(format string, args ...any)
Log(args ...any)
Skipf(format string, args ...any)
Skip(args ...any)
SkipNow()
Errorf(format string, args ...any)
Error(args ...any)
Fatalf(format string, args ...any)
Fatal(args ...any)
FailNow()
Fail()
Failed() bool
}
TB is a common interface between *testing.T, *testing.B and *T.