blob: 54193822d4ce3d4b92215fe5b839670f5bc09a6e [file] [log] [blame]
Robert Kroeger86e37792015-07-13 14:57:42 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package servicetest
6
7import (
8 "fmt"
9 "sync"
10)
11
12// Tape holds a record of function call stimuli and each function call's
13// response. Use Tape to help build a mock framework by first creating a
14// new Tape, then SetResponses to define the mock responses and then
15// Record each function invocation. Play returns the function invocations
16// for verification in a test.
17type Tape struct {
18 sync.Mutex
19 stimuli []interface{}
20 responses []interface{}
21}
22
23// Record stores a new function invocation in a Tape and returns the
24// response for that function interface.
25func (t *Tape) Record(call interface{}) interface{} {
26 t.Lock()
27 defer t.Unlock()
28 t.stimuli = append(t.stimuli, call)
29
30 if len(t.responses) < 1 {
31 // Returning an error at this point will likely cause the mock
32 // device manager to panic trying to cast the response to what
33 // it expects. Panic'ing here at least makes the issue more
34 // apparent.
35 // TODO(caprita): Don't panic.
36 panic(fmt.Errorf("Record(%#v) had no response", call))
37 }
38 resp := t.responses[0]
39 t.responses = t.responses[1:]
40 return resp
41}
42
43// SetResponses updates the Tape's associated responses.
44func (t *Tape) SetResponses(responses ...interface{}) {
45 t.Lock()
46 defer t.Unlock()
47 t.responses = make([]interface{}, len(responses))
48 copy(t.responses, responses)
49}
50
51// Rewind resets the tape to the beginning so that it could be used again
52// for further tests.
53func (t *Tape) Rewind() {
54 t.Lock()
55 defer t.Unlock()
56 t.stimuli = make([]interface{}, 0)
57 t.responses = make([]interface{}, 0)
58}
59
60// Play returns the function call stimuli recorded to this Tape.
61func (t *Tape) Play() []interface{} {
62 t.Lock()
63 defer t.Unlock()
64 resp := make([]interface{}, len(t.stimuli))
65 copy(resp, t.stimuli)
66 return resp
67}
68
69// NewTape creates a new Tape.
70func NewTape() *Tape {
71 t := new(Tape)
72 t.Rewind()
73 return t
74}
75
76// TapeMap provides multiple tapes for different strings. Use TapeMap to
77// record separate Tapes for each suffix in a service.
78type TapeMap struct {
79 sync.Mutex
80 tapes map[string]*Tape
81}
82
83// NewTapeMap creates a new empty TapeMap.
84func NewTapeMap() *TapeMap {
85 tm := &TapeMap{
86 tapes: make(map[string]*Tape),
87 }
88 return tm
89}
90
91// ForSuffix returns the Tape for suffix s.
92func (tm *TapeMap) ForSuffix(s string) *Tape {
93 tm.Lock()
94 defer tm.Unlock()
95 t, ok := tm.tapes[s]
96 if !ok {
97 t = new(Tape)
98 tm.tapes[s] = t
99 }
100 return t
101}
102
103// Rewind rewinds all of the Tapes in the TapeMap.
104func (tm *TapeMap) Rewind() {
105 tm.Lock()
106 defer tm.Unlock()
107 for _, t := range tm.tapes {
108 t.Rewind()
109 }
110}