15 // A TestableVolume allows test suites to manipulate the state of an
16 // underlying Volume, in order to test behavior in cases that are
17 // impractical to achieve with a sequence of normal Volume operations.
18 type TestableVolume interface {
20 // [Over]write content for a locator with the given data,
21 // bypassing all constraints like readonly and serialize.
22 PutRaw(locator string, data []byte)
24 // Specify the value Mtime() should return, until the next
25 // call to Touch, TouchWithDate, or Put.
26 TouchWithDate(locator string, lastPut time.Time)
28 // Clean up, delete temporary files.
32 // MockVolumes are test doubles for Volumes, used to test handlers.
33 type MockVolume struct {
34 Store map[string][]byte
35 Timestamps map[string]time.Time
37 // Bad volumes return an error for every operation.
40 // Touchable volumes' Touch() method succeeds for a locator
41 // that has been Put().
44 // Readonly volumes return an error for Put, Delete, and
48 // Gate is a "starting gate", allowing test cases to pause
49 // volume operations long enough to inspect state. Every
50 // operation (except Status) starts by receiving from
51 // Gate. Sending one value unblocks one operation; closing the
52 // channel unblocks all operations. By default, Gate is a
53 // closed channel, so all operations proceed without
54 // blocking. See trash_worker_test.go for an example.
61 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
63 func CreateMockVolume() *MockVolume {
64 gate := make(chan struct{})
67 Store: make(map[string][]byte),
68 Timestamps: make(map[string]time.Time),
72 called: map[string]int{},
77 // CallCount returns how many times the named method has been called.
78 func (v *MockVolume) CallCount(method string) int {
80 defer v.mutex.Unlock()
81 c, ok := v.called[method]
88 func (v *MockVolume) gotCall(method string) {
90 defer v.mutex.Unlock()
91 if _, ok := v.called[method]; !ok {
98 func (v *MockVolume) Compare(loc string, buf []byte) error {
102 return errors.New("Bad volume")
103 } else if block, ok := v.Store[loc]; ok {
104 if fmt.Sprintf("%x", md5.Sum(block)) != loc {
107 if bytes.Compare(buf, block) != 0 {
108 return CollisionError
116 func (v *MockVolume) Get(loc string) ([]byte, error) {
120 return nil, errors.New("Bad volume")
121 } else if block, ok := v.Store[loc]; ok {
122 buf := bufs.Get(len(block))
126 return nil, os.ErrNotExist
129 func (v *MockVolume) Put(loc string, block []byte) error {
133 return errors.New("Bad volume")
136 return MethodDisabledError
142 func (v *MockVolume) Touch(loc string) error {
146 return MethodDisabledError
149 v.Timestamps[loc] = time.Now()
152 return errors.New("Touch failed")
155 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
161 err = errors.New("Bad volume")
162 } else if t, ok := v.Timestamps[loc]; ok {
170 func (v *MockVolume) IndexTo(prefix string, w io.Writer) error {
173 for loc, block := range v.Store {
174 if !IsValidLocator(loc) || !strings.HasPrefix(loc, prefix) {
177 _, err := fmt.Fprintf(w, "%s+%d %d\n",
178 loc, len(block), 123456789)
186 func (v *MockVolume) Delete(loc string) error {
190 return MethodDisabledError
192 if _, ok := v.Store[loc]; ok {
193 if time.Since(v.Timestamps[loc]) < blobSignatureTTL {
199 return os.ErrNotExist
202 func (v *MockVolume) Status() *VolumeStatus {
204 for _, block := range v.Store {
205 used = used + uint64(len(block))
207 return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
210 func (v *MockVolume) String() string {
211 return "[MockVolume]"
214 func (v *MockVolume) Writable() bool {
218 func (v *MockVolume) Replication() int {