mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 22:12:28 +01:00
205 lines
6.5 KiB
Go
205 lines
6.5 KiB
Go
package archive
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAcquireContent_randomAccessReadersAreUsedInPlace(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: 0, MaxDiskBytes: 0}).Charge()
|
|
reader := bytes.NewReader([]byte("already random access"))
|
|
|
|
content, err := acquireContent(reader, "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
|
|
assert.Same(t, reader, content.ReaderAtSeeker)
|
|
assert.Empty(t, filesIn(t, workDir))
|
|
mem, disk := charge.held()
|
|
assert.Zero(t, mem, "nothing is held, so nothing is charged")
|
|
assert.Zero(t, disk)
|
|
content.Release()
|
|
}
|
|
|
|
func TestHoldContent_heldWhileTheLimitAdmitsIt(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
body := []byte(strings.Repeat("a", 100))
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: 1000, MaxDiskBytes: 1000}).Charge()
|
|
|
|
content, err := holdContent(bytes.NewReader(body), "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, body, readAll(t, content))
|
|
assert.Empty(t, filesIn(t, workDir))
|
|
mem, disk := charge.held()
|
|
assert.Equal(t, int64(100), mem)
|
|
assert.Zero(t, disk)
|
|
|
|
content.Release()
|
|
mem, _ = charge.held()
|
|
assert.Zero(t, mem, "releasing held content refunds its memory")
|
|
}
|
|
|
|
func TestHoldContent_writtenToDiskWhenTheLimitDoesNotAdmitIt(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
body := []byte(strings.Repeat("b", 250))
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: 100, MaxDiskBytes: 10_000}).Charge()
|
|
|
|
content, err := holdContent(bytes.NewReader(body), "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, []string{contentFileName}, filesIn(t, workDir))
|
|
assert.Equal(t, body, readAll(t, content), "the bytes read before the limit refused must not be lost")
|
|
mem, disk := charge.held()
|
|
assert.Zero(t, mem, "nothing stays charged to a limit that refused")
|
|
assert.Equal(t, int64(250), disk)
|
|
|
|
content.Release()
|
|
assert.Empty(t, filesIn(t, workDir), "releasing removes the file")
|
|
_, disk = charge.held()
|
|
assert.Zero(t, disk, "and refunds it")
|
|
}
|
|
|
|
// peakTrackingReader records the highest memory charge observed while being read.
|
|
type peakTrackingReader struct {
|
|
r io.Reader
|
|
charge *Charge
|
|
peak int64
|
|
}
|
|
|
|
func (p *peakTrackingReader) Read(b []byte) (int, error) {
|
|
n, err := p.r.Read(b)
|
|
if mem, _ := p.charge.held(); mem > p.peak {
|
|
p.peak = mem
|
|
}
|
|
return n, err
|
|
}
|
|
|
|
func TestHoldContent_largeArchiveNeverBuffersBeyondTheLimit(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
const limit = int64(3 * copyChunkSize)
|
|
body := []byte(strings.Repeat("c", 10*copyChunkSize))
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: limit, MaxDiskBytes: int64(len(body)) + 1}).Charge()
|
|
tracked := &peakTrackingReader{r: bytes.NewReader(body), charge: charge}
|
|
|
|
content, err := holdContent(tracked, "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
t.Cleanup(content.Release)
|
|
|
|
assert.Equal(t, []string{contentFileName}, filesIn(t, workDir))
|
|
assert.Equal(t, body, readAll(t, content))
|
|
assert.LessOrEqual(t, tracked.peak, limit, "memory charged must never exceed the limit, even transiently")
|
|
mem, disk := charge.held()
|
|
assert.Zero(t, mem)
|
|
assert.Equal(t, int64(len(body)), disk)
|
|
}
|
|
|
|
func TestHoldContent_diskLimitIsTerminal(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
limits Limits
|
|
}{
|
|
{"a disk limit that is exceeded", Limits{MaxDiskBytes: 100}},
|
|
{"a zero disk limit", Limits{MaxMemoryBytes: 10, MaxDiskBytes: 0}},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
charge := NewLimiter(tt.limits).Charge()
|
|
|
|
_, err := holdContent(bytes.NewReader([]byte(strings.Repeat("d", 500))), "app.zip", WorkDirAt(workDir), charge)
|
|
require.ErrorIs(t, err, ErrDiskLimitReached)
|
|
|
|
assert.Empty(t, filesIn(t, workDir), "the partial file must not be left behind")
|
|
mem, disk := charge.held()
|
|
assert.Zero(t, mem)
|
|
assert.Zero(t, disk, "bytes no longer on disk must be refunded")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHoldContent_threeStateLimits(t *testing.T) {
|
|
body := []byte(strings.Repeat("e", 500))
|
|
|
|
t.Run("a zero memory limit sends everything to disk", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: 0, MaxDiskBytes: 10_000}).Charge()
|
|
|
|
content, err := holdContent(bytes.NewReader(body), "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
t.Cleanup(content.Release)
|
|
|
|
assert.Equal(t, []string{contentFileName}, filesIn(t, workDir))
|
|
mem, disk := charge.held()
|
|
assert.Zero(t, mem)
|
|
assert.Equal(t, int64(500), disk)
|
|
})
|
|
|
|
t.Run("a negative memory limit holds content regardless of how much is already held", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: -1, MaxDiskBytes: 10_000}).Charge()
|
|
require.True(t, charge.Memory(1_000_000))
|
|
|
|
content, err := holdContent(bytes.NewReader(body), "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
t.Cleanup(content.Release)
|
|
|
|
assert.Empty(t, filesIn(t, workDir))
|
|
mem, disk := charge.held()
|
|
assert.Equal(t, int64(1_000_000+len(body)), mem)
|
|
assert.Zero(t, disk)
|
|
})
|
|
|
|
t.Run("a zero disk limit still holds what memory admits", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: 10_000, MaxDiskBytes: 0}).Charge()
|
|
|
|
content, err := holdContent(bytes.NewReader(body), "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
t.Cleanup(content.Release)
|
|
|
|
assert.Empty(t, filesIn(t, workDir))
|
|
mem, disk := charge.held()
|
|
assert.Equal(t, int64(len(body)), mem)
|
|
assert.Zero(t, disk)
|
|
})
|
|
|
|
t.Run("a negative disk limit writes with no ceiling", func(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
charge := NewLimiter(Limits{MaxMemoryBytes: 0, MaxDiskBytes: -1}).Charge()
|
|
|
|
content, err := holdContent(bytes.NewReader(body), "app.zip", WorkDirAt(workDir), charge)
|
|
require.NoError(t, err)
|
|
t.Cleanup(content.Release)
|
|
|
|
assert.Equal(t, []string{contentFileName}, filesIn(t, workDir))
|
|
assert.Equal(t, body, readAll(t, content))
|
|
})
|
|
}
|
|
|
|
func TestHoldContent_nilChargeHoldsEverythingInMemory(t *testing.T) {
|
|
workDir := t.TempDir()
|
|
body := []byte("small enough")
|
|
|
|
content, err := holdContent(bytes.NewReader(body), "app.zip", WorkDirAt(workDir), nil)
|
|
require.NoError(t, err)
|
|
t.Cleanup(content.Release)
|
|
|
|
assert.Equal(t, body, readAll(t, content))
|
|
assert.Empty(t, filesIn(t, workDir))
|
|
}
|
|
|
|
func readAll(t *testing.T, c Content) []byte {
|
|
t.Helper()
|
|
_, err := c.Seek(0, io.SeekStart)
|
|
require.NoError(t, err)
|
|
body, err := io.ReadAll(c)
|
|
require.NoError(t, err)
|
|
return body
|
|
}
|