6260: Expose queue sizes in /status.json. Fix sleep/race in trash_worker_test.
[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         // Every operation (except Status) starts by receiving from
26         // Gate. Send one value to unblock one operation; close the
27         // channel to unblock all. By default, it is a closed channel,
28         // so all operations proceed without blocking.
29         Gate   chan struct{}
30         called map[string]int
31         mutex  sync.Mutex
32 }
33
34 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
35 // volume.
36 func CreateMockVolume() *MockVolume {
37         gate := make(chan struct{})
38         close(gate)
39         return &MockVolume{
40                 Store:      make(map[string][]byte),
41                 Timestamps: make(map[string]time.Time),
42                 Bad:        false,
43                 Touchable:  true,
44                 Readonly:   false,
45                 called:     map[string]int{},
46                 Gate:       gate,
47         }
48 }
49
50 // CallCount returns how many times the named method has been called.
51 func (v *MockVolume) CallCount(method string) int {
52         v.mutex.Lock()
53         defer v.mutex.Unlock()
54         if c, ok := v.called[method]; !ok {
55                 return 0
56         } else {
57                 return c
58         }
59 }
60
61 func (v *MockVolume) gotCall(method string) {
62         v.mutex.Lock()
63         defer v.mutex.Unlock()
64         if _, ok := v.called[method]; !ok {
65                 v.called[method] = 1
66         } else {
67                 v.called[method]++
68         }
69 }
70
71 func (v *MockVolume) Get(loc string) ([]byte, error) {
72         v.gotCall("Get")
73         <-v.Gate
74         if v.Bad {
75                 return nil, errors.New("Bad volume")
76         } else if block, ok := v.Store[loc]; ok {
77                 buf := bufs.Get(len(block))
78                 copy(buf, block)
79                 return buf, nil
80         }
81         return nil, os.ErrNotExist
82 }
83
84 func (v *MockVolume) Put(loc string, block []byte) error {
85         v.gotCall("Put")
86         <-v.Gate
87         if v.Bad {
88                 return errors.New("Bad volume")
89         }
90         if v.Readonly {
91                 return MethodDisabledError
92         }
93         v.Store[loc] = block
94         return v.Touch(loc)
95 }
96
97 func (v *MockVolume) Touch(loc string) error {
98         v.gotCall("Touch")
99         <-v.Gate
100         if v.Readonly {
101                 return MethodDisabledError
102         }
103         if v.Touchable {
104                 v.Timestamps[loc] = time.Now()
105                 return nil
106         }
107         return errors.New("Touch failed")
108 }
109
110 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
111         v.gotCall("Mtime")
112         <-v.Gate
113         var mtime time.Time
114         var err error
115         if v.Bad {
116                 err = errors.New("Bad volume")
117         } else if t, ok := v.Timestamps[loc]; ok {
118                 mtime = t
119         } else {
120                 err = os.ErrNotExist
121         }
122         return mtime, err
123 }
124
125 func (v *MockVolume) IndexTo(prefix string, w io.Writer) error {
126         v.gotCall("IndexTo")
127         <-v.Gate
128         for loc, block := range v.Store {
129                 if !IsValidLocator(loc) || !strings.HasPrefix(loc, prefix) {
130                         continue
131                 }
132                 _, err := fmt.Fprintf(w, "%s+%d %d\n",
133                         loc, len(block), 123456789)
134                 if err != nil {
135                         return err
136                 }
137         }
138         return nil
139 }
140
141 func (v *MockVolume) Delete(loc string) error {
142         v.gotCall("Delete")
143         <-v.Gate
144         if v.Readonly {
145                 return MethodDisabledError
146         }
147         if _, ok := v.Store[loc]; ok {
148                 if time.Since(v.Timestamps[loc]) < blob_signature_ttl {
149                         return nil
150                 }
151                 delete(v.Store, loc)
152                 return nil
153         }
154         return os.ErrNotExist
155 }
156
157 func (v *MockVolume) Status() *VolumeStatus {
158         var used uint64
159         for _, block := range v.Store {
160                 used = used + uint64(len(block))
161         }
162         return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
163 }
164
165 func (v *MockVolume) String() string {
166         return "[MockVolume]"
167 }
168
169 func (v *MockVolume) Writable() bool {
170         return !v.Readonly
171 }