141 lines · 3.0 KB · Go
RawDownload
// Copyright 2019 Gregory Petrosyan <gregory.petrosyan@gmail.com>//// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at https://mozilla.org/MPL/2.0/. package rapid import (	"fmt"	"reflect"	"testing") const (	actionLabel       = "action"	validActionTries  = 100 // hack, but probably good enough for now	noValidActionsMsg = "can't find a valid (non-skipped) action") // Repeat executes a random sequence of actions (often called a "state machine" test).//// Check, if set, is ran initially once and after every action. It should contain// invariant checks.//// For complex state machines, it can be more convenient to specify actions as// methods of a special state machine type. In this case, [StateMachineActions]// can be used to create an actions generator from state machine methods using reflection.func (t *T) Repeat(actions *Generator[StateMachineAction], check func(*T)) {	t.Helper() 	steps := flags.steps	if testing.Short() {		steps /= 2	} 	repeat := newRepeat(-1, -1, float64(steps), "Repeat")	sm := stateMachine{		actions: actions,	} 	if check != nil {		check(t)	}	t.failOnError()	for repeat.more(t.s) {		ok := sm.executeAction(t)		if ok {			if check != nil {				check(t)			}			t.failOnError()		} else {			repeat.reject()		}	}} type smActionName interface {	ActionName() string} type StateMachineAction struct {	Name string	Func func(*T)} func (sa StateMachineAction) ActionName() string {	return sa.Name} func (sa StateMachineAction) String() string {	return fmt.Sprintf("%q", sa.Name)} // StateMachineActions creates an actions generator for [*T.Repeat]// from methods of sm using reflection.func StateMachineActions(sm interface{}) *Generator[StateMachineAction] {	var (		v = reflect.ValueOf(sm)		t = v.Type()		n = t.NumMethod()	) 	var actions []StateMachineAction	for i := 0; i < n; i++ {		name := t.Method(i).Name		m, ok := v.Method(i).Interface().(func(*T))		if ok {			actions = append(actions, StateMachineAction{				Name: name,				Func: m,			})		}	} 	assertf(len(actions) > 0, "state machine of type %v has no actions specified", t) 	return SampledFrom(actions)} type stateMachine struct {	actions *Generator[StateMachineAction]} func (sm *stateMachine) executeAction(t *T) bool {	t.Helper() 	for n := 0; n < validActionTries; n++ {		i := t.s.beginGroup(actionLabel, false)		action := sm.actions.Draw(t, "action")		invalid, skipped := runAction(t, action)		t.s.endGroup(i, false) 		if skipped {			continue		} else {			return !invalid		}	} 	panic(stopTest(noValidActionsMsg))} func runAction(t *T, action StateMachineAction) (invalid bool, skipped bool) {	defer func(draws int) {		if r := recover(); r != nil {			if _, ok := r.(invalidData); ok {				invalid = true				skipped = t.draws == draws			} else {				panic(r)			}		}	}(t.draws) 	action.Func(t)	t.failOnError() 	return false, false}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t