/
niceSOFT
/
git-lfs
Обзор
Документация
Войти
/
niceSOFT
/
git-lfs
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tools/copycallback_test.go
384 строки
9 KB
Chris Darroch
lfs,t,tools: make callback when final total known
26 июн 2026, 09:16
26 июн 2026, 09:16
47d1e25
Код
Авторство
О чём код?
package tools import ( "bytes" "errors" "io" "os" "testing" "github.com/git-lfs/git-lfs/v3/internal/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestBothCallbackReadersInvokeCallbackOnEagerEOF(t *testing.T) { var ( calls int actualTotalSize int64 actualReadSoFar int64 actualReadSinceLast int ) cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { calls++ actualTotalSize = totalSize actualReadSoFar = readSoFar actualReadSinceLast = readSinceLast return nil } // We simulate a larger buffer which has not yet been fully read. buf := []byte{0x1} bufSize := len(buf) initialTotalSize := 3 * int64(bufSize) initialReadSize := initialTotalSize - int64(bufSize) r := NewCallbackReader(testutil.NewEagerEOFByteReader(buf), initialTotalSize, cb) br := NewBodyWithCallback(testutil.NewEagerEOFByteReader(buf), initialTotalSize, cb) r.state.readSize = initialReadSize br.state.readSize = initialReadSize p := make([]byte, bufSize+1) for _, reader := range []io.Reader{r, br} { t.Logf("testing with reader: %T", reader) n, err := reader.Read(p) assert.Equal(t, bufSize, n) assert.Equal(t, io.EOF, err) assert.Equal(t, 1, calls, "expected 1 call to callback, got %d", calls) assert.EqualValues(t, initialTotalSize, actualTotalSize) assert.EqualValues(t, initialTotalSize, actualReadSoFar) assert.Equal(t, bufSize, actualReadSinceLast) // Read again and check that no callback is made after last // byte has been read (since the simulated initial total // matched the simulated number of bytes read). calls = 0 n, err = reader.Read(p) assert.Zero(t, n) assert.Equal(t, io.EOF, err) assert.Zero(t, calls, "expected no call to callback, got %d", calls) calls = 0 actualTotalSize = 0 actualReadSoFar = 0 actualReadSinceLast = 0 } } func TestBothCallbackReadersCountReads(t *testing.T) { var actualReadSoFar int64 cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { actualReadSoFar = readSoFar return nil } buf := []byte{0x1, 0x2, 0x3, 0x4} bufSize := len(buf) r := NewCallbackReader(bytes.NewReader(buf), int64(bufSize), cb) br := NewBodyWithCallback(NewClosingByteReader(buf), int64(bufSize), cb) p := make([]byte, 1) for _, reader := range []io.Reader{r, br} { t.Logf("testing with reader: %T", reader) for i := 1; i <= bufSize; i++ { n, err := reader.Read(p) // The underlying bytes.Reader should always return // a nil error when the last byte is read. assert.Equal(t, 1, n) assert.Nil(t, err) assert.EqualValues(t, i, actualReadSoFar) } actualReadSoFar = 0 } } func TestBothCallbackReadersPreferCallbackErrorOverEOF(t *testing.T) { cbErr := errors.New("callback error") cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { return cbErr } buf := []byte{0x1, 0x2} bufSize := len(buf) r := NewCallbackReader(testutil.NewEagerEOFByteReader(buf), int64(bufSize), cb) br := NewBodyWithCallback(testutil.NewEagerEOFByteReader(buf), int64(bufSize), cb) p := make([]byte, bufSize-1) for _, reader := range []io.Reader{r, br} { t.Logf("testing with reader: %T", reader) n, err := reader.Read(p) assert.Equal(t, bufSize-1, n) assert.Equal(t, cbErr, err) n, err = reader.Read(p) // We expect the EOF from EagerEOFByteReader's Read() method // to be replaced with the callback function's own error. assert.Equal(t, 1, n) assert.Equal(t, cbErr, err) // We expect no callback to be performed when no bytes are // available to be read, so no error should be returned. n, err = reader.Read(p) assert.Zero(t, n) assert.Equal(t, io.EOF, err) } } func TestBothCallbackReadersSkipCallbackAfterReadError(t *testing.T) { var calls int cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { calls++ return nil } buf := []byte{0x1, 0x2} bufSize := len(buf) r := NewCallbackReader(testutil.NewErrReader(io.ErrUnexpectedEOF), int64(bufSize), cb) br := NewBodyWithCallback(testutil.NewErrReader(io.ErrUnexpectedEOF), int64(bufSize), cb) p := make([]byte, bufSize-1) for _, reader := range []io.Reader{r, br} { t.Logf("testing with reader: %T", reader) n, err := reader.Read(p) assert.Zero(t, n) assert.Equal(t, io.ErrUnexpectedEOF, err) assert.Zero(t, calls, "expected no call to callback, got %d", calls) } } func TestBothCallbackReadersInvokeCallbackOnEOFWhenTotalSizeIncorrect(t *testing.T) { var ( calls int actualTotalSize int64 actualReadSoFar int64 ) cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { calls++ actualTotalSize = totalSize actualReadSoFar = readSoFar return nil } buf := []byte{0x1, 0x2} bufSize := len(buf) p := make([]byte, bufSize-1) readBufSize := len(p) testCases := []struct { description string initialTotalSize int64 }{ {"initial total unknown", -1}, {"initial total overestimated", int64(bufSize) + 1}, {"initial total underestimated", int64(bufSize) - 1}, } for _, tc := range testCases { t.Logf("testing with %s", tc.description) r := NewCallbackReader(testutil.NewEagerEOFByteReader(buf), tc.initialTotalSize, cb) br := NewBodyWithCallback(testutil.NewEagerEOFByteReader(buf), tc.initialTotalSize, cb) for _, reader := range []io.Reader{r, br} { t.Logf("testing with reader: %T", reader) n, err := reader.Read(p) assert.Equal(t, readBufSize, n) assert.Nil(t, err) assert.Equal(t, 1, calls, "expected 1 call to callback, got %d", calls) assert.EqualValues(t, tc.initialTotalSize, actualTotalSize) assert.EqualValues(t, readBufSize, actualReadSoFar) n, err = reader.Read(p) // We expect an immediate EOF from the EagerEOFByteReader's // Read() method once the final byte has been read. assert.Equal(t, 1, n) assert.Equal(t, io.EOF, err) assert.Equal(t, 2, calls, "expected 2 calls to callback, got %d", calls) assert.EqualValues(t, bufSize, actualTotalSize) assert.EqualValues(t, bufSize, actualReadSoFar) // Read again and check that no callback is made after last // byte has been read (since the updated total matched the // number of bytes read). calls = 0 n, err = reader.Read(p) assert.Zero(t, n) assert.Equal(t, io.EOF, err) assert.Zero(t, calls, "expected no call to callback, got %d", calls) actualTotalSize = 0 actualReadSoFar = 0 } } } func TestBodyCallbackReaderUpdatesOffsetOnSeek(t *testing.T) { var calls int cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { calls++ return nil } buf := []byte{0x1, 0x2, 0x3, 0x4} bufSize := len(buf) br := NewBodyWithCallback(NewClosingByteReader(buf), int64(bufSize), cb) offset := 1 br.Seek(int64(offset), io.SeekStart) assert.EqualValues(t, offset, br.state.readSize) offset++ br.Seek(1, io.SeekCurrent) assert.EqualValues(t, offset, br.state.readSize) p := make([]byte, bufSize) n, err := br.Read(p) // The underlying bytes.Reader should always return // a nil error when the last byte is read. assert.Equal(t, bufSize-offset, n) assert.Nil(t, err) assert.Equal(t, buf[offset:], p[:bufSize-offset]) assert.Equal(t, 1, calls, "expected 1 call to callback, got %d", calls) br.Seek(-1, io.SeekEnd) assert.EqualValues(t, bufSize-1, br.state.readSize) n, err = br.Read(p) assert.Equal(t, 1, n) assert.Nil(t, err) assert.Equal(t, buf[bufSize-1], p[0]) assert.Equal(t, 2, calls, "expected 2 calls to callback, got %d", calls) } func TestBodyCallbackReaderResetsProgress(t *testing.T) { var ( calls int actualTotalSize int64 actualReadSoFar int64 actualReadSinceLast int ) cb := func(totalSize int64, readSoFar int64, readSinceLast int) error { calls++ actualTotalSize = totalSize actualReadSoFar = readSoFar actualReadSinceLast = readSinceLast return nil } buf := []byte{0x1, 0x2, 0x3, 0x4} bufSize := len(buf) br := NewBodyWithCallback(NewClosingByteReader(buf), int64(bufSize), cb) p := make([]byte, bufSize-1) readBufSize := len(p) n, err := br.Read(p) assert.Equal(t, readBufSize, n) assert.Nil(t, err) assert.Equal(t, 1, calls, "expected 1 call to callback, got %d", calls) assert.EqualValues(t, bufSize, actualTotalSize) assert.EqualValues(t, readBufSize, actualReadSoFar) assert.Equal(t, readBufSize, actualReadSinceLast) // Note that ResetProgress() is only used to pass a decrement // to the callback, as the caller is expected to create a new // underlying buffer and retry the read operation. br.ResetProgress() assert.Equal(t, 2, calls, "expected 2 calls to callback, got %d", calls) assert.EqualValues(t, bufSize, actualTotalSize) assert.EqualValues(t, readBufSize, actualReadSoFar) assert.Equal(t, -readBufSize, actualReadSinceLast) } func TestFileBodyCallbackReaderIgnoresClose(t *testing.T) { buf := []byte{0x1, 0x2, 0x3, 0x4} bufSize := len(buf) filePath := t.TempDir() + "/testFileBodyCallback" err := os.WriteFile(filePath, buf, 0644) require.Nil(t, err) f, err := os.Open(filePath) require.Nil(t, err) defer f.Close() fbr := NewFileBodyWithCallback(f, int64(bufSize), nil) // The underlying os.File should not be closed and reads should // still proceed. err = fbr.Close() assert.Nil(t, err) p := make([]byte, bufSize-1) n, err := fbr.Read(p) assert.Equal(t, bufSize-1, n) assert.Nil(t, err) // Closing the underlying os.File should result in a read error. err = f.Close() assert.Nil(t, err) n, err = fbr.Read(p) assert.Error(t, err) }