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