1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
19 "github.com/prometheus/client_golang/prometheus"
22 // A TestableVolume allows test suites to manipulate the state of an
23 // underlying Volume, in order to test behavior in cases that are
24 // impractical to achieve with a sequence of normal Volume operations.
25 type TestableVolume interface {
27 // [Over]write content for a locator with the given data,
28 // bypassing all constraints like readonly and serialize.
29 PutRaw(locator string, data []byte)
31 // Specify the value Mtime() should return, until the next
32 // call to Touch, TouchWithDate, or Put.
33 TouchWithDate(locator string, lastPut time.Time)
35 // Clean up, delete temporary files.
39 // MockVolumes are test doubles for Volumes, used to test handlers.
40 type MockVolume struct {
41 Store map[string][]byte
42 Timestamps map[string]time.Time
44 // Bad volumes return an error for every operation.
47 // Touchable volumes' Touch() method succeeds for a locator
48 // that has been Put().
51 // Readonly volumes return an error for Put, Delete, and
55 // Gate is a "starting gate", allowing test cases to pause
56 // volume operations long enough to inspect state. Every
57 // operation (except Status) starts by receiving from
58 // Gate. Sending one value unblocks one operation; closing the
59 // channel unblocks all operations. By default, Gate is a
60 // closed channel, so all operations proceed without
61 // blocking. See trash_worker_test.go for an example.
68 // CreateMockVolume returns a non-Bad, non-Readonly, Touchable mock
70 func CreateMockVolume() *MockVolume {
71 gate := make(chan struct{})
74 Store: make(map[string][]byte),
75 Timestamps: make(map[string]time.Time),
79 called: map[string]int{},
84 // CallCount returns how many times the named method has been called.
85 func (v *MockVolume) CallCount(method string) int {
87 defer v.mutex.Unlock()
88 c, ok := v.called[method]
95 func (v *MockVolume) gotCall(method string) {
97 defer v.mutex.Unlock()
98 if _, ok := v.called[method]; !ok {
105 func (v *MockVolume) Compare(ctx context.Context, loc string, buf []byte) error {
109 return errors.New("Bad volume")
110 } else if block, ok := v.Store[loc]; ok {
111 if fmt.Sprintf("%x", md5.Sum(block)) != loc {
114 if bytes.Compare(buf, block) != 0 {
115 return CollisionError
123 func (v *MockVolume) Get(ctx context.Context, loc string, buf []byte) (int, error) {
127 return 0, errors.New("Bad volume")
128 } else if block, ok := v.Store[loc]; ok {
129 copy(buf[:len(block)], block)
130 return len(block), nil
132 return 0, os.ErrNotExist
135 func (v *MockVolume) Put(ctx context.Context, loc string, block []byte) error {
139 return errors.New("Bad volume")
142 return MethodDisabledError
148 func (v *MockVolume) Touch(loc string) error {
152 return MethodDisabledError
155 v.Timestamps[loc] = time.Now()
158 return errors.New("Touch failed")
161 func (v *MockVolume) Mtime(loc string) (time.Time, error) {
167 err = errors.New("Bad volume")
168 } else if t, ok := v.Timestamps[loc]; ok {
176 func (v *MockVolume) IndexTo(prefix string, w io.Writer) error {
179 for loc, block := range v.Store {
180 if !IsValidLocator(loc) || !strings.HasPrefix(loc, prefix) {
183 _, err := fmt.Fprintf(w, "%s+%d %d\n",
184 loc, len(block), 123456789)
192 func (v *MockVolume) Trash(loc string) error {
196 return MethodDisabledError
198 if _, ok := v.Store[loc]; ok {
199 if time.Since(v.Timestamps[loc]) < time.Duration(theConfig.BlobSignatureTTL) {
205 return os.ErrNotExist
208 func (v *MockVolume) DeviceID() string {
209 return "mock-device-id"
212 func (v *MockVolume) Type() string {
216 func (v *MockVolume) Start(opsCounters, errCounters, ioBytes *prometheus.CounterVec) error {
220 func (v *MockVolume) Untrash(loc string) error {
224 func (v *MockVolume) Status() *VolumeStatus {
226 for _, block := range v.Store {
227 used = used + uint64(len(block))
229 return &VolumeStatus{"/bogo", 123, 1000000 - used, used}
232 func (v *MockVolume) String() string {
233 return "[MockVolume]"
236 func (v *MockVolume) Writable() bool {
240 func (v *MockVolume) Replication() int {
244 func (v *MockVolume) EmptyTrash() {
247 func (v *MockVolume) GetStorageClasses() []string {