Repository navigation
Expand file tree
/
Copy pathread_test.go
More file actions
120 lines (98 loc) · 4.07 KB
/
Copy pathread_test.go
File metadata and controls
120 lines (98 loc) · 4.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package keepcurrent
import (
"bytes"
"io"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// FromFile (no preprocessor) must hand back a size-aware reader so the Runner's
// readAll pre-sizes its buffer rather than falling back to io.ReadAll's
// doubling growth — the cached-mmdb-at-startup case that regressed otherwise.
func TestFileSourceIsSizeAware(t *testing.T) {
payload := bytes.Repeat([]byte("m"), 3*1024*1024) // 3 MiB
path := filepath.Join(t.TempDir(), "cached.bin")
require.NoError(t, os.WriteFile(path, payload, 0o644))
rc, err := FromFile(path).Fetch(time.Time{})
require.NoError(t, err)
defer rc.Close()
n, ok := knownSize(rc)
assert.True(t, ok, "FromFile reader should report its size")
assert.EqualValues(t, len(payload), n)
got, err := readAll(rc)
require.NoError(t, err)
assert.Equal(t, payload, got)
assert.LessOrEqual(t, cap(got), len(payload)+bytes.MinRead, "must be a single pre-sized allocation, not grown")
}
// Fetch must honour its "only if modified since" contract: when ifNewerThan is
// at or after the file's modtime, it returns ErrUnmodified and leaves no reader
// (hence no leaked descriptor) for the caller to close.
func TestFileSourceUnmodified(t *testing.T) {
path := filepath.Join(t.TempDir(), "cached.bin")
require.NoError(t, os.WriteFile(path, []byte("payload"), 0o644))
fi, err := os.Stat(path)
require.NoError(t, err)
// A cutoff strictly after the modtime => unmodified.
rc, err := FromFile(path).Fetch(fi.ModTime().Add(time.Hour))
assert.ErrorIs(t, err, ErrUnmodified)
assert.Nil(t, rc)
// A cutoff strictly before the modtime => the file is newer, so we fetch.
rc, err = FromFile(path).Fetch(fi.ModTime().Add(-time.Hour))
require.NoError(t, err)
require.NotNil(t, rc)
require.NoError(t, rc.Close())
}
// unsizedReader hides any size its wrapped reader might otherwise expose (it
// implements only Read), forcing readAll down the io.ReadAll fallback path.
type unsizedReader struct{ r io.Reader }
func (u *unsizedReader) Read(p []byte) (int, error) { return u.r.Read(p) }
func TestReadAllPreSizesKnownReaders(t *testing.T) {
payload := bytes.Repeat([]byte("x"), 1<<20) // 1 MiB
// *bytes.Reader reports Len(), so readAll should allocate exactly once and
// not overshoot the way io.ReadAll's doubling does.
got, err := readAll(bytes.NewReader(payload))
require.NoError(t, err)
assert.Equal(t, payload, got)
assert.Equalf(t, len(payload)+bytes.MinRead, cap(got),
"buffer for a sized reader should be pre-allocated to the payload size, not grown")
// sizedReadCloser (what the web/tar.gz sources return) is also recognised.
got, err = readAll(bytesReadCloser(payload))
require.NoError(t, err)
assert.Equal(t, payload, got)
assert.Equal(t, len(payload)+bytes.MinRead, cap(got))
}
func TestReadAllFallsBackForUnsizedReaders(t *testing.T) {
payload := bytes.Repeat([]byte("y"), 4096)
// An opaque reader exposes no size; readAll must still return the full data.
got, err := readAll(&unsizedReader{bytes.NewReader(payload)})
require.NoError(t, err)
assert.Equal(t, payload, got)
}
func TestReadAllFallsBackWhenSizeExceedsCap(t *testing.T) {
// A reader that reports a huge size (e.g. a bogus/hostile Content-Length)
// but only delivers a small payload. readAll must not attempt the giant
// pre-allocation; it should fall back to io.ReadAll and still return the
// real bytes.
payload := bytes.Repeat([]byte("z"), 1024)
r := sizedReadCloser{ReadCloser: io.NopCloser(bytes.NewReader(payload)), n: maxPreAlloc + 1}
n, ok := knownSize(r)
require.True(t, ok)
require.Greater(t, n, int64(maxPreAlloc))
got, err := readAll(r)
require.NoError(t, err)
assert.Equal(t, payload, got)
assert.LessOrEqual(t, cap(got), maxPreAlloc, "must not pre-allocate the reported (bogus) size")
}
func TestKnownSize(t *testing.T) {
n, ok := knownSize(bytes.NewReader(make([]byte, 42)))
assert.True(t, ok)
assert.EqualValues(t, 42, n)
n, ok = knownSize(bytesReadCloser(make([]byte, 7)))
assert.True(t, ok)
assert.EqualValues(t, 7, n)
_, ok = knownSize(&unsizedReader{})
assert.False(t, ok)
}