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