arbitrary

package
v0.2.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 9, 2020 License: MIT Imports: 6 Imported by: 8

Documentation

Overview

Package arbitrary contains helpers to create contexts of arbitrary values, i.e. automatically combine generators as needed using reflection.

A simple example might look like this:

func TestIntParse(t *testing.T) {
  properties := gopter.NewProperties(nil)
  arbitraries := arbitrary.DefaultArbitraries()

  properties.Property("printed integers can be parsed", arbitraries.ForAll(
		func(a int64) bool {
			str := fmt.Sprintf("%d", a)
			parsed, err := strconv.ParseInt(str, 10, 64)
			return err == nil && parsed == a
		}))

  properties.TestingRun(t)
}

Be aware that by default always the most generic generators are used. I.e. in the example above the gen.Int64 generator will be used and the condition will be tested for the full range of int64 numbers.

To adapt this one might register a generator for a specific type in an arbitraries context. I.e. by adding

arbitraries.RegisterGen(gen.Int64Range(-1000, 1000))

any generated int64 number will be between -1000 and 1000.

Example (Arbitrary_structs)
package main

import (
	"fmt"
	"time"

	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/arbitrary"
)

type MyStringType string
type MyInt8Type int8
type MyInt16Type int16
type MyInt32Type int32
type MyInt64Type int64
type MyUInt8Type uint8
type MyUInt16Type uint16
type MyUInt32Type uint32
type MyUInt64Type uint64

type Foo struct {
	Name     MyStringType
	Id1      MyInt8Type
	Id2      MyInt16Type
	Id3      MyInt32Type
	Id4      MyInt64Type
	Id5      MyUInt8Type
	Id6      MyUInt16Type
	Id7      MyUInt32Type
	Id8      MyUInt64Type
	ATime    time.Time
	ATimePtr *time.Time
}

func (f Foo) ToString() string {
	return fmt.Sprintf("For(%s, %d, %d, %d, %d, %d, %d, %d, %d, %v, %v)", f.Name, f.Id1, f.Id2, f.Id3, f.Id4, f.Id5, f.Id6, f.Id7, f.Id8, f.ATime, f.ATimePtr)
}

func main() {
	time.Local = time.UTC
	parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducible results, otherwise DefaultTestParameters() will suffice

	arbitraries := arbitrary.DefaultArbitraries()

	properties := gopter.NewProperties(parameters)

	properties.Property("MyInt64", arbitraries.ForAll(
		func(id MyInt64Type) bool {
			return id > -1000
		}))
	properties.Property("MyUInt32Type", arbitraries.ForAll(
		func(id MyUInt32Type) bool {
			return id < 2000
		}))
	properties.Property("Foo", arbitraries.ForAll(
		func(foo *Foo) bool {
			return foo.ATime.After(time.Unix(0, 0))
		}))
	properties.Property("Foo2", arbitraries.ForAll(
		func(foo Foo) bool {
			return foo.ATimePtr == nil || foo.ATimePtr.Before(time.Unix(20000, 0))
		}))

	properties.Run(gopter.ConsoleReporter(false))
}
Output:

! MyInt64: Falsified after 6 passed tests.
ARG_0: -1000
ARG_0_ORIGINAL (54 shrinks): -1601066829744837253
! MyUInt32Type: Falsified after 0 passed tests.
ARG_0: 2000
ARG_0_ORIGINAL (23 shrinks): 2161922319
+ Foo: OK, passed 100 tests.
! Foo2: Falsified after 1 passed tests.
ARG_0: {Name: Id1:0 Id2:0 Id3:0 Id4:0 Id5:0 Id6:0 Id7:0 Id8:0
   ATime:1970-01-01 00:00:00 +0000 UTC ATimePtr:1970-01-01 05:33:20 +0000
   UTC}
ARG_0_ORIGINAL (40 shrinks): {Name: Id1:-67 Id2:27301 Id3:-1350752892
   Id4:7128486677722156226 Id5:208 Id6:28663 Id7:4178604448
   Id8:16360504079646654692 ATime:2239-08-20 23:46:28.063412239 +0000 UTC
   ATimePtr:5468-08-19 13:09:39.171622464 +0000 UTC}
Example (Parseint)
package main

import (
	"fmt"
	"strconv"

	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/arbitrary"
)

func main() {
	parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducible results, otherwise DefaultTestParameters() will suffice

	arbitraries := arbitrary.DefaultArbitraries()
	properties := gopter.NewProperties(parameters)

	properties.Property("printed integers can be parsed", arbitraries.ForAll(
		func(a int64) bool {
			str := fmt.Sprintf("%d", a)
			parsed, err := strconv.ParseInt(str, 10, 64)
			return err == nil && parsed == a
		}))

	// When using testing.T you might just use: properties.TestingRun(t)
	properties.Run(gopter.ConsoleReporter(false))
}
Output:

+ printed integers can be parsed: OK, passed 100 tests.
Example (Quadratic)
package main

import (
	"errors"
	"math/cmplx"

	"github.com/leanovate/gopter"
	"github.com/leanovate/gopter/arbitrary"
	"github.com/leanovate/gopter/gen"
)

type QudraticEquation struct {
	A, B, C complex128
}

func (q *QudraticEquation) Eval(x complex128) complex128 {
	return q.A*x*x + q.B*x + q.C
}

func (q *QudraticEquation) Solve() (complex128, complex128, error) {
	if q.A == 0 {
		return 0, 0, errors.New("No solution")
	}
	v := q.B*q.B - 4*q.A*q.C
	v = cmplx.Sqrt(v)
	return (-q.B + v) / 2 / q.A, (-q.B - v) / 2 / q.A, nil
}

func main() {
	parameters := gopter.DefaultTestParametersWithSeed(1234) // Example should generate reproducible results, otherwise DefaultTestParameters() will suffice

	arbitraries := arbitrary.DefaultArbitraries()
	arbitraries.RegisterGen(gen.Complex128Box(-1e8-1e8i, 1e8+1e8i)) // Only use complex values within a range

	properties := gopter.NewProperties(parameters)

	properties.Property("Quadratic equations can be solved (as pointer)", arbitraries.ForAll(
		func(quadratic *QudraticEquation) bool {
			x1, x2, err := quadratic.Solve()
			if err != nil {
				return true
			}

			return cmplx.Abs(quadratic.Eval(x1)) < 1e-5 && cmplx.Abs(quadratic.Eval(x2)) < 1e-5
		}))

	properties.Property("Quadratic equations can be solved (as struct)", arbitraries.ForAll(
		func(quadratic QudraticEquation) bool {
			x1, x2, err := quadratic.Solve()
			if err != nil {
				return true
			}

			return cmplx.Abs(quadratic.Eval(x1)) < 1e-5 && cmplx.Abs(quadratic.Eval(x2)) < 1e-5
		}))

	properties.Property("Quadratic equations can be solved alternative", arbitraries.ForAll(
		func(a, b, c complex128) bool {
			quadratic := &QudraticEquation{
				A: a,
				B: b,
				C: c,
			}
			x1, x2, err := quadratic.Solve()
			if err != nil {
				return true
			}

			return cmplx.Abs(quadratic.Eval(x1)) < 1e-5 && cmplx.Abs(quadratic.Eval(x2)) < 1e-5
		}))

	// When using testing.T you might just use: properties.TestingRun(t)
	properties.Run(gopter.ConsoleReporter(false))
}
Output:

+ Quadratic equations can be solved (as pointer): OK, passed 100 tests.
+ Quadratic equations can be solved (as struct): OK, passed 100 tests.
+ Quadratic equations can be solved alternative: OK, passed 100 tests.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Arbitraries

type Arbitraries struct {
	// contains filtered or unexported fields
}

Arbitraries defines a context to generate arbitrary values of any kind. Values are generated by either providing a generator for a specific type or by creating a generator on the fly using golang reflection.

func DefaultArbitraries

func DefaultArbitraries() *Arbitraries

DefaultArbitraries creates a default arbitrary context with the widest possible ranges for all types.

func (*Arbitraries) ForAll

func (a *Arbitraries) ForAll(condition interface{}) gopter.Prop

ForAll creates a property that requires the check condition to be true for all values, if the condition falsiies the generated values will be shrunk.

"condition" has to be a function with the any number of parameters that can generated in context of the Arbitraries. The function may return a simple bool, a *PropResult, a boolean with error or a *PropResult with error.

func (*Arbitraries) GenForType

func (a *Arbitraries) GenForType(rt reflect.Type) gopter.Gen

GenForType gets a generator for a generator for a type

func (*Arbitraries) RegisterGen

func (a *Arbitraries) RegisterGen(gen gopter.Gen)

RegisterGen registers a generator

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL