forked from leanovate/gopter
/
commands.go
84 lines (73 loc) · 2.93 KB
/
commands.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package commands
import (
"reflect"
"github.com/leanovate/gopter"
"github.com/leanovate/gopter/gen"
"github.com/leanovate/gopter/prop"
)
// Commands provide an entry point for testing a stateful system
type Commands interface {
// NewSystemUnderTest should create a new/isolated system under test
NewSystemUnderTest(initialState State) SystemUnderTest
// DestroySystemUnderTest may perform any cleanup tasks to destroy a system
DestroySystemUnderTest(SystemUnderTest)
// GenInitialState provides a generator for the initial State.
// IMPORTANT: The generated state itself may be mutable, but this generator
// is supposed to generate a clean and reproductable state every time.
// Do not use an external random generator and be especially vary about
// `gen.Const(<pointer to some mutable struct>)`.
GenInitialState() gopter.Gen
// GenCommand provides a generator for applicable commands to for a state
GenCommand(state State) gopter.Gen
// InitialPreCondition checks if the initial state is valid
InitialPreCondition(state State) bool
}
// ProtoCommands is a prototype implementation of the Commands interface
type ProtoCommands struct {
NewSystemUnderTestFunc func(initialState State) SystemUnderTest
DestroySystemUnderTestFunc func(SystemUnderTest)
InitialStateGen gopter.Gen
GenCommandFunc func(State) gopter.Gen
InitialPreConditionFunc func(State) bool
}
// NewSystemUnderTest should create a new/isolated system under test
func (p *ProtoCommands) NewSystemUnderTest(initialState State) SystemUnderTest {
if p.NewSystemUnderTestFunc != nil {
return p.NewSystemUnderTestFunc(initialState)
}
return nil
}
// DestroySystemUnderTest may perform any cleanup tasks to destroy a system
func (p *ProtoCommands) DestroySystemUnderTest(systemUnderTest SystemUnderTest) {
if p.DestroySystemUnderTestFunc != nil {
p.DestroySystemUnderTestFunc(systemUnderTest)
}
}
// GenCommand provides a generator for applicable commands to for a state
func (p *ProtoCommands) GenCommand(state State) gopter.Gen {
if p.GenCommandFunc != nil {
return p.GenCommandFunc(state)
}
return gen.Fail(reflect.TypeOf((*Command)(nil)).Elem())
}
// GenInitialState provides a generator for the initial State
func (p *ProtoCommands) GenInitialState() gopter.Gen {
return p.InitialStateGen.SuchThat(func(state State) bool {
return p.InitialPreCondition(state)
})
}
// InitialPreCondition checks if the initial state is valid
func (p *ProtoCommands) InitialPreCondition(state State) bool {
if p.InitialPreConditionFunc != nil {
return p.InitialPreConditionFunc(state)
}
return true
}
// Prop creates a gopter.Prop from Commands
func Prop(commands Commands) gopter.Prop {
return prop.ForAll(func(actions *actions) (*gopter.PropResult, error) {
systemUnderTest := commands.NewSystemUnderTest(actions.initialStateProvider())
defer commands.DestroySystemUnderTest(systemUnderTest)
return actions.run(systemUnderTest)
}, genActions(commands))
}