~rogpeppe/juju-core/254-txn-tester

« back to all changes in this revision

Viewing changes to testing/txntest/txntest.go

  • Committer: Roger Peppe
  • Date: 2013-03-25 08:59:24 UTC
  • Revision ID: roger.peppe@canonical.com-20130325085924-jdzjizz6ypepfm82
txntest: make args in to Params; add test actions

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
package txntest
2
2
 
3
3
import (
 
4
        "errors"
4
5
        "fmt"
5
6
        "launchpad.net/juju-core/log"
6
7
        "strconv"
7
 
        "errors"
8
8
        "strings"
9
9
)
10
10
 
75
75
        explore := false
76
76
        if strings.HasSuffix(s, "-") {
77
77
                explore = true
78
 
                s = s[0:len(s)-1]
 
78
                s = s[0 : len(s)-1]
79
79
        }
80
80
        parts := strings.Split(s, ".")
81
81
        var a *Action
89
89
                switch p[len(p)-1] {
90
90
                case 't':
91
91
                        a.priorState = mutate
92
 
                        p = p[0:len(p)-1]
 
92
                        p = p[0 : len(p)-1]
93
93
                case '*':
94
94
                        a.terminate = true
95
 
                        p = p[0:len(p)-1]
 
95
                        p = p[0 : len(p)-1]
96
96
                        fallthrough
97
97
                default:
98
98
                        a.priorState = look
127
127
        // db holds the current database state.
128
128
        db DB
129
129
        // validate holds the post-op validation function.
130
 
         validate func() error
 
130
        validate func() error
131
131
        // ops holds the set of operations that
132
132
        // are to be tested together.
133
133
        ops []func(s DB) error
191
191
        return look
192
192
}
193
193
 
 
194
// Params holds the parameters to a Run call.
 
195
type Params struct {
 
196
        // Reset is called before starting the set of operation;
 
197
        // it should reset the database to the same initial state each time
 
198
        // and return the database to operate on. It should also clean
 
199
        // up anypreviously returned database.
 
200
        Reset func() DB
 
201
 
 
202
        // Each time all the operations have terminated, Validate is called
 
203
        // to check that the database has ended up in an expected state.
 
204
        Validate func() error
 
205
 
 
206
        // Ops holds the set of operations to test concurrently with
 
207
        // each other.  Each operation must change state only by calling
 
208
        // Run on the database that it is given as an argument, and it
 
209
        // should make deterministic decisions about what transactions
 
210
        // to run based only on the that state.
 
211
        Ops []func(DB) error
 
212
 
 
213
        // Test holds a set of action traces to verify.
 
214
        // If it is empty, all possible traces will be verified.
 
215
        Test []*Action
 
216
}
 
217
 
194
218
// Result holds the results of the transaction testing.
195
219
type Result struct {
196
220
        // ActionCount holds the total number of actions
197
221
        // executed.
198
222
        ActionCount int64
 
223
 
199
224
        // Failures holds details of an operations that have failed.
200
225
        Failures []Failure
 
226
 
201
227
        // Complete holds actions that resulted in the
202
228
        // termination of all operations.
203
229
        Complete []*Action
204
230
}
205
231
 
206
 
// Run explores the state space of a set of concurrent operation.
207
 
// Before running any operations, reset is called,
208
 
// which should reset the database to the same initial state each time
209
 
// and return the database to operate on. It should also close any
210
 
// previously returned database.
211
 
//
212
 
// Each time all the operations have terminated, validate is called
213
 
// to check that the database has ended up in an expected state.
214
 
// 
215
 
// Each operation must change state only by calling Run on the database
216
 
// that it is given as an argument, and it should make deterministic
217
 
// decisions about what transactions to run based only on the
218
 
// that state.
219
 
//
220
 
func Run(reset func() DB, validate func() error, ops ...func(db DB) error) Result {
 
232
// Run explores the state space of a set of concurrent operations.
 
233
func Run(p Params) Result {
221
234
        t := &context{
222
 
                ops: ops,
223
 
                validate: validate,
 
235
                ops:      p.Ops,
 
236
                validate: p.Validate,
224
237
        }
225
 
        // Prime pending with first states to explore.
226
 
        for i := range ops {
227
 
                t.pending = append(t.pending, &Action{
228
 
                        op:         i,
229
 
                        priorState: look,
230
 
                        explore: true,
231
 
                })
 
238
        if len(p.Test) == 0 {
 
239
                // Prime pending with first states to explore.
 
240
                for i := range t.ops {
 
241
                        t.pending = append(t.pending, &Action{
 
242
                                op:         i,
 
243
                                priorState: look,
 
244
                                explore:    true,
 
245
                        })
 
246
                }
232
247
        }
233
248
        for len(t.pending) > 0 {
234
249
                log.Infof("-------------------------------------")
235
250
                log.Infof("pending %v", t.pending)
236
 
                t.db = reset()
 
251
                t.db = p.Reset()
237
252
                t.init()
238
253
                a := t.pending[0]
239
254
                t.pending = t.pending[1:]
345
360
                default:
346
361
                        panic(fmt.Errorf("unknown or unexpected state %v", act.priorState))
347
362
                }
 
363
                if len(t.running) == 0 {
 
364
                        log.Infof("terminate %v", act)
 
365
                        t.result.Complete = append(t.result.Complete, act)
 
366
                        if err := t.validate(); err != nil {
 
367
                                t.addFailure(act, err)
 
368
                        }
 
369
                }
348
370
                if !act.explore {
349
371
                        // We're only testing whether this state can
350
372
                        // be reached successfully; no need to explore
368
390
                                prior:      act,
369
391
                                op:         rop.op,
370
392
                                priorState: rop.state(),
371
 
                                explore: true,
 
393
                                explore:    true,
372
394
                        })
373
395
                }
374
396
                if len(possible) == 0 {
375
397
                        if len(t.running) != 0 {
376
398
                                log.Infof("premature terminate %v", act)
377
 
                                return
378
 
                        }
379
 
                        log.Infof("terminate %v", act)
380
 
                        t.result.Complete = append(t.result.Complete, act)
381
 
                        if err := t.validate(); err != nil {
382
 
                                t.addFailure(act, err)
383
399
                        }
384
400
                        return
385
401
                }