Merge branch '3198-inode-cache' into 3198-writable-fuse, fix tests.
[arvados.git] / services / keepstore / volume_test.go
1 package main
2
3 import (
4         "errors"
5         "fmt"
6         "os"
7         "strings"
8         "sync"
9         "time"
10 )
11
12 // MockVolumes are test doubles for Volumes, used to test handlers.
13 type MockVolume struct {
14         Store      map[string][]byte
15         Timestamps map[string]time.Time
16         // Bad volumes return an error for every operation.
17         Bad        bool
18         // Touchable volumes' Touch() method succeeds for a locator
19         // that has been Put().
20         Touchable  bool
21         // Readonly volumes return an error for Put, Delete, and
22         // Touch.
23         Readonly   bool
24         called     map[string]int
25         mutex      sync.Mutex
26 }
27
28 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
29 // volume.
30 func CreateMockVolume() *MockVolume {
31         return &MockVolume{
32                 Store:      make(map[string][]byte),
33                 Timestamps: make(map[string]time.Time),
34                 Bad:        false,
35                 Touchable:  true,
36                 Readonly:   false,
37                 called:     map[string]int{},
38         }
39 }
40
41 // CallCount returns how many times the named method has been called.
42 func (v *MockVolume) CallCount(method string) int {
43         v.mutex.Lock()
44         defer v.mutex.Unlock()
45         if c, ok := v.called[method]; !ok {
46                 return 0
47         } else {
48                 return c
49         }
50 }
51
52 func (v *MockVolume) gotCall(method string) {
53         v.mutex.Lock()
54         defer v.mutex.Unlock()
55         if _, ok := v.called[method]; !ok {
56                 v.called[method] = 1
57         } else {
58                 v.called[method]++
59         }
60 }
61
62 func (v *MockVolume) Get(loc string) ([]byte, error) {
63         v.gotCall("Get")
64         if v.Bad {
65                 return nil, errors.New("Bad volume")
66         } else if block, ok := v.Store[loc]; ok {
67                 return block, nil
68         }
69         return nil, os.ErrNotExist
70 }
71
72 func (v *MockVolume) Put(loc string, block []byte) error {
73         v.gotCall("Put")
74         if v.Bad {
75                 return errors.New("Bad volume")
76         }
77         if v.Readonly {
78                 return MethodDisabledError
79         }
80         v.Store[loc] = block
81         return v.Touch(loc)
82 }
83
84 func (v *MockVolume) Touch(loc string) error {
85         v.gotCall("Touch")
86         if v.Readonly {
87                 return MethodDisabledError
88         }
89         if v.Touchable {
90                 v.Timestamps[loc] = time.Now()
91                 return nil
92         }
93         return errors.New("Touch failed")
94 }
95
96 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
97         v.gotCall("Mtime")
98         var mtime time.Time
99         var err error
100         if v.Bad {
101                 err = errors.New("Bad volume")
102         } else if t, ok := v.Timestamps[loc]; ok {
103                 mtime = t
104         } else {
105                 err = os.ErrNotExist
106         }
107         return mtime, err
108 }
109
110 func (v *MockVolume) Index(prefix string) string {
111         v.gotCall("Index")
112         var result string
113         for loc, block := range v.Store {
114                 if IsValidLocator(loc) && strings.HasPrefix(loc, prefix) {
115                         result = result + fmt.Sprintf("%s+%d %d\n",
116                                 loc, len(block), 123456789)
117                 }
118         }
119         return result
120 }
121
122 func (v *MockVolume) Delete(loc string) error {
123         v.gotCall("Delete")
124         if v.Readonly {
125                 return MethodDisabledError
126         }
127         if _, ok := v.Store[loc]; ok {
128                 if time.Since(v.Timestamps[loc]) < permission_ttl {
129                         return nil
130                 }
131                 delete(v.Store, loc)
132                 return nil
133         }
134         return os.ErrNotExist
135 }
136
137 func (v *MockVolume) Status() *VolumeStatus {
138         var used uint64
139         for _, block := range v.Store {
140                 used = used + uint64(len(block))
141         }
142         return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
143 }
144
145 func (v *MockVolume) String() string {
146         return "[MockVolume]"
147 }
148
149 func (v *MockVolume) Writable() bool {
150         return !v.Readonly
151 }