13 // A TestableVolume allows test suites to manipulate the state of an
14 // underlying Volume, in order to test behavior in cases that are
15 // impractical to achieve with a sequence of normal Volume operations.
16 type TestableVolume interface {
18 // [Over]write content for a locator with the given data,
19 // bypassing all constraints like readonly and serialize.
20 PutRaw(locator string, data []byte)
21 // Specify the value Mtime() should return, until the next
22 // call to Touch, TouchWithDate, or Put.
23 TouchWithDate(locator string, lastPut time.Time)
24 // Clean up, delete temporary files.
28 // MockVolumes are test doubles for Volumes, used to test handlers.
29 type MockVolume struct {
30 Store map[string][]byte
31 Timestamps map[string]time.Time
32 // Bad volumes return an error for every operation.
34 // Touchable volumes' Touch() method succeeds for a locator
35 // that has been Put().
37 // Readonly volumes return an error for Put, Delete, and
40 // Gate is a "starting gate", allowing test cases to pause
41 // volume operations long enough to inspect state. Every
42 // operation (except Status) starts by receiving from
43 // Gate. Sending one value unblocks one operation; closing the
44 // channel unblocks all operations. By default, Gate is a
45 // closed channel, so all operations proceed without
46 // blocking. See trash_worker_test.go for an example.
52 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
54 func CreateMockVolume() *MockVolume {
55 gate := make(chan struct{})
58 Store: make(map[string][]byte),
59 Timestamps: make(map[string]time.Time),
63 called: map[string]int{},
68 // CallCount returns how many times the named method has been called.
69 func (v *MockVolume) CallCount(method string) int {
71 defer v.mutex.Unlock()
72 if c, ok := v.called[method]; !ok {
79 func (v *MockVolume) gotCall(method string) {
81 defer v.mutex.Unlock()
82 if _, ok := v.called[method]; !ok {
89 func (v *MockVolume) Get(loc string) ([]byte, error) {
93 return nil, errors.New("Bad volume")
94 } else if block, ok := v.Store[loc]; ok {
95 buf := bufs.Get(len(block))
99 return nil, os.ErrNotExist
102 func (v *MockVolume) Put(loc string, block []byte) error {
106 return errors.New("Bad volume")
109 return MethodDisabledError
115 func (v *MockVolume) Touch(loc string) error {
119 return MethodDisabledError
122 v.Timestamps[loc] = time.Now()
125 return errors.New("Touch failed")
128 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
134 err = errors.New("Bad volume")
135 } else if t, ok := v.Timestamps[loc]; ok {
143 func (v *MockVolume) IndexTo(prefix string, w io.Writer) error {
146 for loc, block := range v.Store {
147 if !IsValidLocator(loc) || !strings.HasPrefix(loc, prefix) {
150 _, err := fmt.Fprintf(w, "%s+%d %d\n",
151 loc, len(block), 123456789)
159 func (v *MockVolume) Delete(loc string) error {
163 return MethodDisabledError
165 if _, ok := v.Store[loc]; ok {
166 if time.Since(v.Timestamps[loc]) < blob_signature_ttl {
172 return os.ErrNotExist
175 func (v *MockVolume) Status() *VolumeStatus {
177 for _, block := range v.Store {
178 used = used + uint64(len(block))
180 return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
183 func (v *MockVolume) String() string {
184 return "[MockVolume]"
187 func (v *MockVolume) Writable() bool {