internal/pkg: improve artifact interface
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m35s
Test / Hakurei (push) Successful in 3m36s
Test / ShareFS (push) Successful in 3m41s
Test / Hpkg (push) Successful in 4m19s
Test / Sandbox (race detector) (push) Successful in 4m52s
Test / Hakurei (race detector) (push) Successful in 5m52s
Test / Flake checks (push) Successful in 1m53s
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m35s
Test / Hakurei (push) Successful in 3m36s
Test / ShareFS (push) Successful in 3m41s
Test / Hpkg (push) Successful in 4m19s
Test / Sandbox (race detector) (push) Successful in 4m52s
Test / Hakurei (race detector) (push) Successful in 5m52s
Test / Flake checks (push) Successful in 1m53s
This moves all cache I/O code to Cache. Artifact now only contains methods for constructing their actual contents. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -5,6 +5,8 @@ import (
|
||||
"bytes"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
"reflect"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/container/check"
|
||||
@@ -19,41 +22,131 @@ import (
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
// A stubArtifact implements [Artifact] with hardcoded kind and identifier.
|
||||
type stubArtifact struct {
|
||||
kind pkg.Kind
|
||||
id pkg.ID
|
||||
// overrideIdent overrides the ID method of [Artifact].
|
||||
type overrideIdent struct {
|
||||
id pkg.ID
|
||||
pkg.Artifact
|
||||
}
|
||||
|
||||
func (a stubArtifact) Kind() pkg.Kind { return a.kind }
|
||||
func (a stubArtifact) ID() pkg.ID { return a.id }
|
||||
func (a stubArtifact) Hash() (pkg.Checksum, error) { panic("unreachable") }
|
||||
func (a stubArtifact) Pathname() (*check.Absolute, error) { panic("unreachable") }
|
||||
func (a overrideIdent) ID() pkg.ID { return a.id }
|
||||
|
||||
// overrideIdentFile overrides the ID method of [File].
|
||||
type overrideIdentFile struct {
|
||||
id pkg.ID
|
||||
pkg.File
|
||||
}
|
||||
|
||||
func (a overrideIdentFile) ID() pkg.ID { return a.id }
|
||||
|
||||
// A knownIdentArtifact implements [pkg.KnownIdent] and [Artifact]
|
||||
type knownIdentArtifact interface {
|
||||
pkg.KnownIdent
|
||||
pkg.Artifact
|
||||
}
|
||||
|
||||
// A knownIdentFile implements [pkg.KnownIdent] and [File]
|
||||
type knownIdentFile interface {
|
||||
pkg.KnownIdent
|
||||
pkg.File
|
||||
}
|
||||
|
||||
// overrideChecksum overrides the Checksum method of [Artifact].
|
||||
type overrideChecksum struct {
|
||||
checksum pkg.Checksum
|
||||
knownIdentArtifact
|
||||
}
|
||||
|
||||
func (a overrideChecksum) Checksum() pkg.Checksum { return a.checksum }
|
||||
|
||||
// overrideChecksumFile overrides the Checksum method of [File].
|
||||
type overrideChecksumFile struct {
|
||||
checksum pkg.Checksum
|
||||
knownIdentFile
|
||||
}
|
||||
|
||||
func (a overrideChecksumFile) Checksum() pkg.Checksum { return a.checksum }
|
||||
|
||||
// A stubArtifact implements [Artifact] with hardcoded behaviour.
|
||||
type stubArtifact struct {
|
||||
kind pkg.Kind
|
||||
params []byte
|
||||
deps []pkg.Artifact
|
||||
|
||||
cure func(work *check.Absolute, loadData pkg.CacheDataFunc) error
|
||||
}
|
||||
|
||||
func (a stubArtifact) Kind() pkg.Kind { return a.kind }
|
||||
func (a stubArtifact) Params() []byte { return a.params }
|
||||
func (a stubArtifact) Dependencies() []pkg.Artifact { return a.deps }
|
||||
|
||||
func (a stubArtifact) Cure(
|
||||
work *check.Absolute,
|
||||
loadData pkg.CacheDataFunc,
|
||||
) error {
|
||||
return a.cure(work, loadData)
|
||||
}
|
||||
|
||||
// A stubFile implements [File] with hardcoded behaviour.
|
||||
type stubFile struct {
|
||||
data []byte
|
||||
err error
|
||||
|
||||
stubArtifact
|
||||
}
|
||||
|
||||
func (a stubFile) Data() ([]byte, error) { return a.data, a.err }
|
||||
|
||||
// newStubFile returns an implementation of [pkg.File] with hardcoded behaviour.
|
||||
func newStubFile(
|
||||
kind pkg.Kind,
|
||||
id pkg.ID,
|
||||
sum *pkg.Checksum,
|
||||
data []byte,
|
||||
err error,
|
||||
) pkg.File {
|
||||
f := overrideIdentFile{id, stubFile{data, err, stubArtifact{
|
||||
kind,
|
||||
nil,
|
||||
nil,
|
||||
func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
||||
panic("unreachable")
|
||||
},
|
||||
}}}
|
||||
if sum == nil {
|
||||
return f
|
||||
} else {
|
||||
return overrideChecksumFile{*sum, f}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
kind pkg.Kind
|
||||
params []byte
|
||||
deps []pkg.Artifact
|
||||
want pkg.ID
|
||||
name string
|
||||
a pkg.Artifact
|
||||
want pkg.ID
|
||||
}{
|
||||
{"tar", pkg.KindTar, []byte{
|
||||
pkg.TarGzip, 0, 0, 0, 0, 0, 0, 0,
|
||||
}, []pkg.Artifact{
|
||||
stubArtifact{pkg.KindHTTP, pkg.ID{}},
|
||||
}, pkg.MustDecode("HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY")},
|
||||
{"tar", stubArtifact{
|
||||
pkg.KindTar,
|
||||
[]byte{pkg.TarGzip, 0, 0, 0, 0, 0, 0, 0},
|
||||
[]pkg.Artifact{
|
||||
overrideIdent{pkg.ID{}, stubArtifact{}},
|
||||
},
|
||||
nil,
|
||||
}, pkg.MustDecode(
|
||||
"HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY",
|
||||
)},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := tc.kind.Ident(tc.params, tc.deps...); got != tc.want {
|
||||
if got := pkg.Ident(tc.a); got != tc.want {
|
||||
t.Errorf("Ident: %s, want %s",
|
||||
pkg.Encode(got),
|
||||
pkg.Encode(tc.want))
|
||||
pkg.Encode(tc.want),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -118,6 +211,37 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
||||
}
|
||||
}
|
||||
|
||||
// A cureStep contains an [Artifact] to be cured, and the expected outcome.
|
||||
type cureStep struct {
|
||||
name string
|
||||
|
||||
a pkg.Artifact
|
||||
|
||||
pathname *check.Absolute
|
||||
checksum pkg.Checksum
|
||||
err error
|
||||
}
|
||||
|
||||
// cureMany cures many artifacts against a [Cache] and checks their outcomes.
|
||||
func cureMany(t *testing.T, c *pkg.Cache, steps []cureStep) {
|
||||
for _, step := range steps {
|
||||
t.Log("cure step:", step.name)
|
||||
if pathname, checksum, err := c.Cure(step.a); !reflect.DeepEqual(err, step.err) {
|
||||
t.Fatalf("Cure: error = %v, want %v", err, step.err)
|
||||
} else if !pathname.Is(step.pathname) {
|
||||
t.Fatalf("Cure: pathname = %q, want %q", pathname, step.pathname)
|
||||
} else if checksum != step.checksum {
|
||||
t.Fatalf("Cure: checksum = %s, want %s", pkg.Encode(checksum), pkg.Encode(step.checksum))
|
||||
} else {
|
||||
v := any(err)
|
||||
if err == nil {
|
||||
v = pathname
|
||||
}
|
||||
t.Log(pkg.Encode(checksum)+":", v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -134,21 +258,9 @@ func TestCache(t *testing.T) {
|
||||
return (pkg.Checksum)(h.Sum(nil))
|
||||
}()
|
||||
|
||||
testdataChecksumString := pkg.Encode(testdataChecksum)
|
||||
|
||||
testCases := []cacheTestCase{
|
||||
{"file", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
wantErrNonexistent := &os.PathError{
|
||||
Op: "open",
|
||||
Path: base.Append(
|
||||
"identifier",
|
||||
testdataChecksumString,
|
||||
).String(),
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
if _, _, err := c.LoadFile(testdataChecksum); !reflect.DeepEqual(err, wantErrNonexistent) {
|
||||
t.Fatalf("LoadFile: error = %#v, want %#v", err, wantErrNonexistent)
|
||||
}
|
||||
c.SetStrict(true)
|
||||
|
||||
identifier := (pkg.ID)(bytes.Repeat([]byte{
|
||||
0x75, 0xe6, 0x9d, 0x6d, 0xe7, 0x9f,
|
||||
@@ -165,154 +277,109 @@ func TestCache(t *testing.T) {
|
||||
"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe",
|
||||
)
|
||||
|
||||
// initial store
|
||||
if pathname, err := c.StoreFile(
|
||||
identifier,
|
||||
[]byte(testdata),
|
||||
&testdataChecksum,
|
||||
true,
|
||||
); err != nil {
|
||||
t.Fatalf("StoreFile: error = %v", err)
|
||||
} else if !pathname.Is(wantPathname) {
|
||||
t.Fatalf("StoreFile: pathname = %q, want %q", pathname, wantPathname)
|
||||
}
|
||||
cureMany(t, c, []cureStep{
|
||||
{"initial file", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
identifier,
|
||||
&testdataChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), wantPathname, testdataChecksum, nil},
|
||||
|
||||
// load or store, identical content
|
||||
if pathname, data, store, err := c.LoadOrStoreFile(identifier0, func() ([]byte, error) {
|
||||
return []byte(testdata), nil
|
||||
}, &testdataChecksum, true); err != nil {
|
||||
t.Fatalf("LoadOrStoreFile: error = %v", err)
|
||||
} else if !pathname.Is(wantPathname0) {
|
||||
t.Fatalf("LoadOrStoreFile: pathname = %q, want %q", pathname, wantPathname0)
|
||||
} else if string(data) != testdata {
|
||||
t.Fatalf("LoadOrStoreFile: data = %x, want %x", data, testdata)
|
||||
} else if !store {
|
||||
t.Fatal("LoadOrStoreFile did not store nonpresent entry")
|
||||
}
|
||||
{"identical content", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
identifier0,
|
||||
&testdataChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), wantPathname0, testdataChecksum, nil},
|
||||
|
||||
// load or store, existing entry
|
||||
if pathname, data, store, err := c.LoadOrStoreFile(identifier, func() ([]byte, error) {
|
||||
return []byte(testdata), nil
|
||||
}, &testdataChecksum, true); err != nil {
|
||||
t.Fatalf("LoadOrStoreFile: error = %v", err)
|
||||
} else if !pathname.Is(wantPathname) {
|
||||
t.Fatalf("LoadOrStoreFile: pathname = %q, want %q", pathname, wantPathname)
|
||||
} else if string(data) != testdata {
|
||||
t.Fatalf("LoadOrStoreFile: data = %x, want %x", data, testdata)
|
||||
} else if store {
|
||||
t.Fatal("LoadOrStoreFile stored over present entry")
|
||||
}
|
||||
{"existing entry", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
identifier,
|
||||
&testdataChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), wantPathname, testdataChecksum, nil},
|
||||
|
||||
// load, existing entry
|
||||
if pathname, data, err := c.LoadFile(identifier0); err != nil {
|
||||
t.Fatalf("LoadFile: error = %v", err)
|
||||
} else if !pathname.Is(wantPathname0) {
|
||||
t.Fatalf("LoadFile: pathname = %q, want %q", pathname, wantPathname0)
|
||||
} else if string(data) != testdata {
|
||||
t.Fatalf("LoadFile: data = %x, want %x", data, testdata)
|
||||
}
|
||||
{"checksum mismatch", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
pkg.ID{0xff, 0},
|
||||
new(pkg.Checksum),
|
||||
[]byte(testdata), nil,
|
||||
), nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
||||
Got: testdataChecksum,
|
||||
}},
|
||||
|
||||
// checksum mismatch
|
||||
wantErrChecksum := &pkg.ChecksumMismatchError{
|
||||
Got: testdataChecksum,
|
||||
}
|
||||
if _, err := c.StoreFile(
|
||||
testdataChecksum,
|
||||
[]byte(testdata),
|
||||
new(pkg.Checksum),
|
||||
true,
|
||||
); !reflect.DeepEqual(err, wantErrChecksum) {
|
||||
t.Fatalf("StoreFile: error = %#v, want %#v", err, wantErrChecksum)
|
||||
}
|
||||
|
||||
// verify failed store
|
||||
if _, _, err := c.LoadFile(testdataChecksum); !reflect.DeepEqual(err, wantErrNonexistent) {
|
||||
t.Fatalf("LoadFile: error = %#v, want %#v", err, wantErrNonexistent)
|
||||
}
|
||||
|
||||
// store, same identifier
|
||||
wantPathnameF := base.Append(
|
||||
"identifier",
|
||||
testdataChecksumString,
|
||||
)
|
||||
if pathname, err := c.StoreFile(
|
||||
testdataChecksum,
|
||||
[]byte(testdata),
|
||||
&testdataChecksum,
|
||||
true,
|
||||
); err != nil {
|
||||
t.Fatalf("StoreFile: error = %v", err)
|
||||
} else if !pathname.Is(wantPathnameF) {
|
||||
t.Fatalf("StoreFile: pathname = %q, want %q", pathname, wantPathnameF)
|
||||
}
|
||||
|
||||
// load, same identifier
|
||||
if pathname, data, err := c.LoadFile(testdataChecksum); err != nil {
|
||||
t.Fatalf("LoadFile: error = %v", err)
|
||||
} else if !pathname.Is(wantPathnameF) {
|
||||
t.Fatalf("LoadFile: pathname = %q, want %q", pathname, wantPathnameF)
|
||||
} else if string(data) != testdata {
|
||||
t.Fatalf("LoadFile: data = %x, want %x", data, testdata)
|
||||
}
|
||||
|
||||
// store without validation
|
||||
wantChecksum := pkg.Checksum{
|
||||
0xbe, 0xc0, 0x21, 0xb4, 0xf3, 0x68,
|
||||
0xe3, 0x06, 0x91, 0x34, 0xe0, 0x12,
|
||||
0xc2, 0xb4, 0x30, 0x70, 0x83, 0xd3,
|
||||
0xa9, 0xbd, 0xd2, 0x06, 0xe2, 0x4e,
|
||||
0x5f, 0x0d, 0x86, 0xe1, 0x3d, 0x66,
|
||||
0x36, 0x65, 0x59, 0x33, 0xec, 0x2b,
|
||||
0x41, 0x34, 0x65, 0x96, 0x68, 0x17,
|
||||
0xa9, 0xc2, 0x08, 0xa1, 0x17, 0x17,
|
||||
}
|
||||
var gotChecksum pkg.Checksum
|
||||
wantPathnameG := base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(wantChecksum),
|
||||
)
|
||||
if pathname, err := c.StoreFile(
|
||||
wantChecksum,
|
||||
[]byte{0},
|
||||
&gotChecksum,
|
||||
false,
|
||||
); err != nil {
|
||||
t.Fatalf("StoreFile: error = %#v", err)
|
||||
} else if !pathname.Is(wantPathnameG) {
|
||||
t.Fatalf("StoreFile: pathname = %q, want %q", pathname, wantPathnameG)
|
||||
} else if gotChecksum != wantChecksum {
|
||||
t.Fatalf("StoreFile: buf = %x, want %x", gotChecksum, wantChecksum)
|
||||
}
|
||||
|
||||
// makeData passthrough
|
||||
var zeroIdent pkg.ID
|
||||
wantErrPassthrough := stub.UniqueError(0xcafe)
|
||||
if _, _, _, err := c.LoadOrStoreFile(zeroIdent, func() ([]byte, error) {
|
||||
return nil, wantErrPassthrough
|
||||
}, new(pkg.Checksum), true); !reflect.DeepEqual(err, wantErrPassthrough) {
|
||||
t.Fatalf("LoadOrStoreFile: error = %#v, want %#v", err, wantErrPassthrough)
|
||||
}
|
||||
|
||||
// verify failed store
|
||||
wantErrNonexistentZero := &os.PathError{
|
||||
Op: "open",
|
||||
Path: base.Append(
|
||||
{"store without validation", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
pkg.MustDecode("vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX"),
|
||||
nil,
|
||||
[]byte{0}, nil,
|
||||
), base.Append(
|
||||
"identifier",
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
).String(),
|
||||
Err: syscall.ENOENT,
|
||||
}
|
||||
if _, _, err := c.LoadFile(zeroIdent); !reflect.DeepEqual(err, wantErrNonexistentZero) {
|
||||
t.Fatalf("LoadFile: error = %#v, want %#v", err, wantErrNonexistentZero)
|
||||
"vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX",
|
||||
), pkg.Checksum{
|
||||
0xbe, 0xc0, 0x21, 0xb4, 0xf3, 0x68,
|
||||
0xe3, 0x06, 0x91, 0x34, 0xe0, 0x12,
|
||||
0xc2, 0xb4, 0x30, 0x70, 0x83, 0xd3,
|
||||
0xa9, 0xbd, 0xd2, 0x06, 0xe2, 0x4e,
|
||||
0x5f, 0x0d, 0x86, 0xe1, 0x3d, 0x66,
|
||||
0x36, 0x65, 0x59, 0x33, 0xec, 0x2b,
|
||||
0x41, 0x34, 0x65, 0x96, 0x68, 0x17,
|
||||
0xa9, 0xc2, 0x08, 0xa1, 0x17, 0x17,
|
||||
}, nil},
|
||||
|
||||
{"error passthrough", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
pkg.ID{0xff, 1},
|
||||
nil,
|
||||
nil, stub.UniqueError(0xcafe),
|
||||
), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||
|
||||
{"error caching", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
pkg.ID{0xff, 1},
|
||||
nil,
|
||||
nil, nil,
|
||||
), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||
|
||||
{"cache hit bad type", overrideChecksum{testdataChecksum, overrideIdent{pkg.ID{0xff, 2}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
}}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
||||
0400,
|
||||
)},
|
||||
})
|
||||
|
||||
if c0, err := pkg.New(base); err != nil {
|
||||
t.Fatalf("New: error = %v", err)
|
||||
} else {
|
||||
cureMany(t, c0, []cureStep{
|
||||
{"cache hit ident", overrideIdent{
|
||||
id: identifier,
|
||||
}, wantPathname, testdataChecksum, nil},
|
||||
|
||||
{"cache miss checksum match", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
testdataChecksum,
|
||||
nil,
|
||||
[]byte(testdata),
|
||||
nil,
|
||||
), base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(testdataChecksum),
|
||||
), testdataChecksum, nil},
|
||||
})
|
||||
}
|
||||
}, pkg.MustDecode("St9rlE-mGZ5gXwiv_hzQ_B8bZP-UUvSNmf4nHUZzCMOumb6hKnheZSe0dmnuc4Q2")},
|
||||
|
||||
{"directory", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
id := pkg.KindTar.Ident(
|
||||
binary.LittleEndian.AppendUint64(nil, pkg.TarGzip),
|
||||
stubArtifact{pkg.KindHTTP, testdataChecksum},
|
||||
overrideIdent{testdataChecksum, stubArtifact{}},
|
||||
)
|
||||
makeSample := func(work *check.Absolute) error {
|
||||
makeSample := func(work *check.Absolute, _ pkg.CacheDataFunc) error {
|
||||
if err := os.Mkdir(work.String(), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(
|
||||
work.Append("check").String(),
|
||||
[]byte{0, 0},
|
||||
@@ -344,59 +411,22 @@ func TestCache(t *testing.T) {
|
||||
pkg.Encode(id),
|
||||
)
|
||||
|
||||
if pathname, store, err := c.Store(
|
||||
id,
|
||||
makeSample,
|
||||
&wantChecksum,
|
||||
true,
|
||||
); err != nil {
|
||||
t.Fatalf("Store: error = %v", err)
|
||||
} else if !store {
|
||||
t.Fatal("Store did not store nonpresent entry")
|
||||
} else if !pathname.Is(wantPathname) {
|
||||
t.Fatalf("Store: pathname = %q, want %q", pathname, wantPathname)
|
||||
}
|
||||
|
||||
// check lookup
|
||||
if pathname, store, err := c.Store(
|
||||
id,
|
||||
nil,
|
||||
&wantChecksum,
|
||||
true,
|
||||
); err != nil {
|
||||
t.Fatalf("Store: error = %v", err)
|
||||
} else if store {
|
||||
t.Fatal("Store stored over present entry")
|
||||
} else if !pathname.Is(wantPathname) {
|
||||
t.Fatalf("Store: pathname = %q, want %q", pathname, wantPathname)
|
||||
}
|
||||
|
||||
// check exist
|
||||
id0 := pkg.KindTar.Ident(
|
||||
binary.LittleEndian.AppendUint64(nil, pkg.TarGzip),
|
||||
stubArtifact{pkg.KindHTTP, pkg.ID{}},
|
||||
overrideIdent{pkg.ID{}, stubArtifact{}},
|
||||
)
|
||||
wantPathname0 := base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(id0),
|
||||
)
|
||||
if pathname, store, err := c.Store(
|
||||
id0,
|
||||
makeSample,
|
||||
&wantChecksum,
|
||||
true,
|
||||
); err != nil {
|
||||
t.Fatalf("Store: error = %v", err)
|
||||
} else if !store {
|
||||
t.Fatal("Store did not store nonpresent entry")
|
||||
} else if !pathname.Is(wantPathname0) {
|
||||
t.Fatalf("Store: pathname = %q, want %q", pathname, wantPathname0)
|
||||
}
|
||||
|
||||
var wantErrMakeGarbage error
|
||||
makeGarbage := func(work *check.Absolute) error {
|
||||
makeGarbage := func(work *check.Absolute, wantErr error) error {
|
||||
if err := os.Mkdir(work.String(), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mode := fs.FileMode(0)
|
||||
if wantErrMakeGarbage == nil {
|
||||
if wantErr == nil {
|
||||
mode = 0500
|
||||
}
|
||||
|
||||
@@ -426,38 +456,144 @@ func TestCache(t *testing.T) {
|
||||
return err
|
||||
}
|
||||
|
||||
return wantErrMakeGarbage
|
||||
return wantErr
|
||||
}
|
||||
|
||||
// check makeArtifact fault
|
||||
wantErrMakeGarbage = stub.UniqueError(0xcafe)
|
||||
if _, store, err := c.Store(
|
||||
pkg.ID{},
|
||||
makeGarbage,
|
||||
nil,
|
||||
false,
|
||||
); !reflect.DeepEqual(err, wantErrMakeGarbage) {
|
||||
t.Fatalf("Store: error = %#v, want %#v", err, wantErrMakeGarbage)
|
||||
} else if !store {
|
||||
t.Fatal("Store did not store nonpresent entry")
|
||||
}
|
||||
cureMany(t, c, []cureStep{
|
||||
{"initial directory", overrideChecksum{wantChecksum, overrideIdent{id, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: makeSample,
|
||||
}}}, wantPathname, wantChecksum, nil},
|
||||
|
||||
// checksum mismatch
|
||||
wantErrMakeGarbage = nil
|
||||
wantErrMismatch := &pkg.ChecksumMismatchError{
|
||||
Got: pkg.MustDecode("GbjlYMcHQANdfwL6qNGopBF99IscPTvCy95HSH1_kIF3eKjFDSLP0_iUUT0z8hiw"),
|
||||
}
|
||||
if _, store, err := c.Store(
|
||||
pkg.ID{},
|
||||
makeGarbage,
|
||||
new(pkg.Checksum),
|
||||
true,
|
||||
); !reflect.DeepEqual(err, wantErrMismatch) {
|
||||
t.Fatalf("Store: error = %v, want %v", err, wantErrMismatch)
|
||||
} else if !store {
|
||||
t.Fatal("Store did not store nonpresent entry")
|
||||
}
|
||||
{"identical identifier", overrideChecksum{wantChecksum, overrideIdent{id, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
}}}, wantPathname, wantChecksum, nil},
|
||||
|
||||
{"identical checksum", overrideIdent{id0, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: makeSample,
|
||||
}}, wantPathname0, wantChecksum, nil},
|
||||
|
||||
{"cure fault", overrideIdent{pkg.ID{0xff, 0}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, _ pkg.CacheDataFunc) error {
|
||||
return makeGarbage(work, stub.UniqueError(0xcafe))
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||
|
||||
{"checksum mismatch", overrideChecksum{pkg.Checksum{}, overrideIdent{pkg.ID{0xff, 1}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, _ pkg.CacheDataFunc) error {
|
||||
return makeGarbage(work, nil)
|
||||
},
|
||||
}}}, nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
||||
Got: pkg.MustDecode(
|
||||
"GbjlYMcHQANdfwL6qNGopBF99IscPTvCy95HSH1_kIF3eKjFDSLP0_iUUT0z8hiw",
|
||||
),
|
||||
}},
|
||||
|
||||
{"cache hit bad type", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
pkg.ID{0xff, 2},
|
||||
&wantChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
||||
fs.ModeDir | 0500,
|
||||
)},
|
||||
|
||||
{"loadData directory", overrideIdent{pkg.ID{0xff, 3}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
||||
_, err := loadData(overrideChecksumFile{checksum: wantChecksum})
|
||||
return err
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, &os.PathError{
|
||||
Op: "read",
|
||||
Path: base.Append(
|
||||
"checksum",
|
||||
pkg.Encode(wantChecksum),
|
||||
).String(),
|
||||
Err: syscall.EISDIR,
|
||||
}},
|
||||
|
||||
{"no output", overrideIdent{pkg.ID{0xff, 4}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
||||
return nil
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, pkg.NoOutputError{}},
|
||||
|
||||
{"file output", overrideIdent{pkg.ID{0xff, 5}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
||||
return os.WriteFile(work.String(), []byte{0}, 0400)
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
||||
|
||||
{"symlink output", overrideIdent{pkg.ID{0xff, 6}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
||||
return os.Symlink(work.String(), work.String())
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
||||
fs.ModeSymlink | 0777,
|
||||
)},
|
||||
})
|
||||
}, pkg.MustDecode("8OP6YxJAdRrhV2WSBt1BPD7oC_n2Qh7JqUMyVMoGvjDX83bDqq2hgVMNcdiBH_64")},
|
||||
|
||||
{"pending", nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
c.SetStrict(true)
|
||||
|
||||
wantErr := stub.UniqueError(0xcafe)
|
||||
n, ready := make(chan struct{}), make(chan struct{})
|
||||
go func() {
|
||||
if _, _, err := c.Cure(overrideIdent{pkg.ID{0xff}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
||||
close(ready)
|
||||
<-n
|
||||
return wantErr
|
||||
},
|
||||
}}); !reflect.DeepEqual(err, wantErr) {
|
||||
panic(fmt.Sprintf("Cure: error = %v, want %v", err, wantErr))
|
||||
}
|
||||
}()
|
||||
|
||||
<-ready
|
||||
go func() {
|
||||
if _, _, err := c.Cure(overrideIdent{pkg.ID{0xff}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
}}); !reflect.DeepEqual(err, wantErr) {
|
||||
panic(fmt.Sprintf("Cure: error = %v, want %v", err, wantErr))
|
||||
}
|
||||
}()
|
||||
|
||||
// check cache activity while a cure is blocking
|
||||
cureMany(t, c, []cureStep{
|
||||
{"error passthrough", newStubFile(
|
||||
pkg.KindHTTP,
|
||||
pkg.ID{0xff, 1},
|
||||
nil,
|
||||
nil, stub.UniqueError(0xbad),
|
||||
), nil, pkg.Checksum{}, stub.UniqueError(0xbad)},
|
||||
|
||||
{"file output", overrideIdent{pkg.ID{0xff, 2}, stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(work *check.Absolute, loadData pkg.CacheDataFunc) error {
|
||||
return os.WriteFile(work.String(), []byte{0}, 0400)
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
||||
})
|
||||
|
||||
identPendingVal := reflect.ValueOf(c).Elem().FieldByName("identPending")
|
||||
identPending := reflect.NewAt(
|
||||
identPendingVal.Type(),
|
||||
unsafe.Pointer(identPendingVal.UnsafeAddr()),
|
||||
).Elem().Interface().(map[pkg.ID]<-chan struct{})
|
||||
notify := identPending[pkg.ID{0xff}]
|
||||
go close(n)
|
||||
<-notify
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
||||
}
|
||||
checkWithCache(t, testCases)
|
||||
}
|
||||
@@ -484,6 +620,14 @@ func TestErrors(t *testing.T) {
|
||||
{"DisallowedTypeflagError", pkg.DisallowedTypeflagError(
|
||||
tar.TypeChar,
|
||||
), "disallowed typeflag '3'"},
|
||||
|
||||
{"InvalidFileModeError", pkg.InvalidFileModeError(
|
||||
fs.ModeSymlink | 0777,
|
||||
), "artifact did not produce a regular file or directory"},
|
||||
|
||||
{"NoOutputError", pkg.NoOutputError{
|
||||
// empty struct
|
||||
}, "artifact cured successfully but did not produce any output"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user