Merge branch '6934-pam' refs #6934
[arvados.git] / services / keepstore / volume_test.go
1 package main
2
3 import (
4         "errors"
5         "fmt"
6         "io"
7         "os"
8         "strings"
9         "sync"
10         "time"
11 )
12
13 // MockVolumes are test doubles for Volumes, used to test handlers.
14 type MockVolume struct {
15         Store      map[string][]byte
16         Timestamps map[string]time.Time
17         // Bad volumes return an error for every operation.
18         Bad bool
19         // Touchable volumes' Touch() method succeeds for a locator
20         // that has been Put().
21         Touchable bool
22         // Readonly volumes return an error for Put, Delete, and
23         // Touch.
24         Readonly bool
25         // Gate is a "starting gate", allowing test cases to pause
26         // volume operations long enough to inspect state. Every
27         // operation (except Status) starts by receiving from
28         // Gate. Sending one value unblocks one operation; closing the
29         // channel unblocks all operations. By default, Gate is a
30         // closed channel, so all operations proceed without
31         // blocking. See trash_worker_test.go for an example.
32         Gate   chan struct{}
33         called map[string]int
34         mutex  sync.Mutex
35 }
36
37 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
38 // volume.
39 func CreateMockVolume() *MockVolume {
40         gate := make(chan struct{})
41         close(gate)
42         return &MockVolume{
43                 Store:      make(map[string][]byte),
44                 Timestamps: make(map[string]time.Time),
45                 Bad:        false,
46                 Touchable:  true,
47                 Readonly:   false,
48                 called:     map[string]int{},
49                 Gate:       gate,
50         }
51 }
52
53 // CallCount returns how many times the named method has been called.
54 func (v *MockVolume) CallCount(method string) int {
55         v.mutex.Lock()
56         defer v.mutex.Unlock()
57         if c, ok := v.called[method]; !ok {
58                 return 0
59         } else {
60                 return c
61         }
62 }
63
64 func (v *MockVolume) gotCall(method string) {
65         v.mutex.Lock()
66         defer v.mutex.Unlock()
67         if _, ok := v.called[method]; !ok {
68                 v.called[method] = 1
69         } else {
70                 v.called[method]++
71         }
72 }
73
74 func (v *MockVolume) Get(loc string) ([]byte, error) {
75         v.gotCall("Get")
76         <-v.Gate
77         if v.Bad {
78                 return nil, errors.New("Bad volume")
79         } else if block, ok := v.Store[loc]; ok {
80                 buf := bufs.Get(len(block))
81                 copy(buf, block)
82                 return buf, nil
83         }
84         return nil, os.ErrNotExist
85 }
86
87 func (v *MockVolume) Put(loc string, block []byte) error {
88         v.gotCall("Put")
89         <-v.Gate
90         if v.Bad {
91                 return errors.New("Bad volume")
92         }
93         if v.Readonly {
94                 return MethodDisabledError
95         }
96         v.Store[loc] = block
97         return v.Touch(loc)
98 }
99
100 func (v *MockVolume) Touch(loc string) error {
101         v.gotCall("Touch")
102         <-v.Gate
103         if v.Readonly {
104                 return MethodDisabledError
105         }
106         if v.Touchable {
107                 v.Timestamps[loc] = time.Now()
108                 return nil
109         }
110         return errors.New("Touch failed")
111 }
112
113 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
114         v.gotCall("Mtime")
115         <-v.Gate
116         var mtime time.Time
117         var err error
118         if v.Bad {
119                 err = errors.New("Bad volume")
120         } else if t, ok := v.Timestamps[loc]; ok {
121                 mtime = t
122         } else {
123                 err = os.ErrNotExist
124         }
125         return mtime, err
126 }
127
128 func (v *MockVolume) IndexTo(prefix string, w io.Writer) error {
129         v.gotCall("IndexTo")
130         <-v.Gate
131         for loc, block := range v.Store {
132                 if !IsValidLocator(loc) || !strings.HasPrefix(loc, prefix) {
133                         continue
134                 }
135                 _, err := fmt.Fprintf(w, "%s+%d %d\n",
136                         loc, len(block), 123456789)
137                 if err != nil {
138                         return err
139                 }
140         }
141         return nil
142 }
143
144 func (v *MockVolume) Delete(loc string) error {
145         v.gotCall("Delete")
146         <-v.Gate
147         if v.Readonly {
148                 return MethodDisabledError
149         }
150         if _, ok := v.Store[loc]; ok {
151                 if time.Since(v.Timestamps[loc]) < blob_signature_ttl {
152                         return nil
153                 }
154                 delete(v.Store, loc)
155                 return nil
156         }
157         return os.ErrNotExist
158 }
159
160 func (v *MockVolume) Status() *VolumeStatus {
161         var used uint64
162         for _, block := range v.Store {
163                 used = used + uint64(len(block))
164         }
165         return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
166 }
167
168 func (v *MockVolume) String() string {
169         return "[MockVolume]"
170 }
171
172 func (v *MockVolume) Writable() bool {
173         return !v.Readonly
174 }