16 // A TestableVolume allows test suites to manipulate the state of an
17 // underlying Volume, in order to test behavior in cases that are
18 // impractical to achieve with a sequence of normal Volume operations.
19 type TestableVolume interface {
21 // [Over]write content for a locator with the given data,
22 // bypassing all constraints like readonly and serialize.
23 PutRaw(locator string, data []byte)
25 // Specify the value Mtime() should return, until the next
26 // call to Touch, TouchWithDate, or Put.
27 TouchWithDate(locator string, lastPut time.Time)
29 // Clean up, delete temporary files.
33 // MockVolumes are test doubles for Volumes, used to test handlers.
34 type MockVolume struct {
35 Store map[string][]byte
36 Timestamps map[string]time.Time
38 // Bad volumes return an error for every operation.
41 // Touchable volumes' Touch() method succeeds for a locator
42 // that has been Put().
45 // Readonly volumes return an error for Put, Delete, and
49 // Gate is a "starting gate", allowing test cases to pause
50 // volume operations long enough to inspect state. Every
51 // operation (except Status) starts by receiving from
52 // Gate. Sending one value unblocks one operation; closing the
53 // channel unblocks all operations. By default, Gate is a
54 // closed channel, so all operations proceed without
55 // blocking. See trash_worker_test.go for an example.
62 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
64 func CreateMockVolume() *MockVolume {
65 gate := make(chan struct{})
68 Store: make(map[string][]byte),
69 Timestamps: make(map[string]time.Time),
73 called: map[string]int{},
78 // CallCount returns how many times the named method has been called.
79 func (v *MockVolume) CallCount(method string) int {
81 defer v.mutex.Unlock()
82 c, ok := v.called[method]
89 func (v *MockVolume) gotCall(method string) {
91 defer v.mutex.Unlock()
92 if _, ok := v.called[method]; !ok {
99 func (v *MockVolume) Compare(ctx context.Context, loc string, buf []byte) error {
103 return errors.New("Bad volume")
104 } else if block, ok := v.Store[loc]; ok {
105 if fmt.Sprintf("%x", md5.Sum(block)) != loc {
108 if bytes.Compare(buf, block) != 0 {
109 return CollisionError
117 func (v *MockVolume) Get(ctx context.Context, loc string, buf []byte) (int, error) {
121 return 0, errors.New("Bad volume")
122 } else if block, ok := v.Store[loc]; ok {
123 copy(buf[:len(block)], block)
124 return len(block), nil
126 return 0, os.ErrNotExist
129 func (v *MockVolume) Put(ctx context.Context, 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) Trash(loc string) error {
190 return MethodDisabledError
192 if _, ok := v.Store[loc]; ok {
193 if time.Since(v.Timestamps[loc]) < time.Duration(theConfig.BlobSignatureTTL) {
199 return os.ErrNotExist
202 func (v *MockVolume) Type() string {
206 func (v *MockVolume) Start() error {
210 func (v *MockVolume) Untrash(loc string) error {
214 func (v *MockVolume) Status() *VolumeStatus {
216 for _, block := range v.Store {
217 used = used + uint64(len(block))
219 return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
222 func (v *MockVolume) String() string {
223 return "[MockVolume]"
226 func (v *MockVolume) Writable() bool {
230 func (v *MockVolume) Replication() int {
234 func (v *MockVolume) EmptyTrash() {