internal/storage/fake/fake_multi_range_downloader.go (105 lines of code) (raw):

// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package fake import ( "fmt" "io" "sync" "time" "github.com/googlecloudplatform/gcsfuse/v2/internal/storage/gcs" "github.com/googlecloudplatform/gcsfuse/v2/internal/storage/storageutil" ) // This struct is an implementation of the gcs.MultiRangeDownloader interface. type fakeMultiRangeDownloader struct { gcs.MultiRangeDownloader obj *fakeObject wg sync.WaitGroup err error defaultErr error statusErr error sleepTime time.Duration // Sleep time to simulate real-world. } func createFakeObject(obj *gcs.MinObject, data []byte) fakeObject { fullObj := storageutil.ConvertMinObjectToObject(obj) return fakeObject{ metadata: *fullObj, data: data, } } func NewFakeMultiRangeDownloader(obj *gcs.MinObject, data []byte) gcs.MultiRangeDownloader { return NewFakeMultiRangeDownloaderWithSleepAndDefaultError(obj, data, time.Millisecond, nil) } func NewFakeMultiRangeDownloaderWithSleep(obj *gcs.MinObject, data []byte, sleepTime time.Duration) gcs.MultiRangeDownloader { return NewFakeMultiRangeDownloaderWithSleepAndDefaultError(obj, data, sleepTime, nil) } func NewFakeMultiRangeDownloaderWithSleepAndDefaultError(obj *gcs.MinObject, data []byte, sleepTime time.Duration, err error) gcs.MultiRangeDownloader { fakeObj := createFakeObject(obj, data) return &fakeMultiRangeDownloader{ obj: &fakeObj, sleepTime: sleepTime, defaultErr: err, } } func NewFakeMultiRangeDownloaderWithStatusError(obj *gcs.MinObject, data []byte, err error) gcs.MultiRangeDownloader { fakeObj := createFakeObject(obj, data) return &fakeMultiRangeDownloader{ obj: &fakeObj, sleepTime: 0, defaultErr: nil, statusErr: err, } } func (fmrd *fakeMultiRangeDownloader) Add(output io.Writer, offset, length int64, callback func(int64, int64, error)) { if fmrd.defaultErr != nil { if callback != nil { callback(offset, 0, fmrd.defaultErr) } return } obj := fmrd.obj size := int64(len(obj.data)) var err error // Apply input checks as defined at https://github.com/googleapis/go-storage-prelaunch/blob/a5db2abd53775941df67b3337eabaf8d00ef0762/storage/reader.go#L373 . if length < 0 { err = fmt.Errorf("length < 0") } else if offset > size { err = fmt.Errorf("out of range. offset (%v) > size of content (%v) of %s", offset, size, obj.metadata.Name) } else if offset <= -size { offset = 0 length = size } else if offset < 0 { offset = size + offset length = min(length, size-offset) } else { length = min(length, size-offset) } if err != nil { // If inputs aren't correct, fail immediately and return callback. fmrd.err = err if callback != nil { callback(offset, 0, err) } return } // Record this additional goroutine. fmrd.wg.Add(1) go func() { // clear this goroutine from waitgroup. defer fmrd.wg.Done() time.Sleep(fmrd.sleepTime) var n int n, err = output.Write(obj.data[offset : offset+length]) if err != nil || int64(n) != length { err = fmt.Errorf("failed to write %v bytes to writer through multi-range-downloader, bytes written = %v, error = %v", length, n, err) } if callback != nil { callback(offset, int64(n), err) } // Don't clear pre-existing error in downloader. if fmrd.err != nil { fmrd.err = err } }() } func (fmrd *fakeMultiRangeDownloader) Close() error { fmrd.Wait() return fmrd.err } func (fmrd *fakeMultiRangeDownloader) Wait() { fmrd.wg.Wait() } func (fmrd *fakeMultiRangeDownloader) Error() error { return fmrd.statusErr }