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  }