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 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 Int8() *Generator[int8]
- func Int8Max(max int8) *Generator[int8]
- func Int8Min(min int8) *Generator[int8]
- func Int8Range(min int8, max int8) *Generator[int8]
- 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 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 Uint8() *Generator[uint8]
- func Uint8Max(max uint8) *Generator[uint8]
- func Uint8Min(min uint8) *Generator[uint8]
- func Uint8Range(min uint8, max uint8) *Generator[uint8]
- 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) }
Output:
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]()) }
Output:
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.