111 lines
4.1 KiB
Go
111 lines
4.1 KiB
Go
package buildstep
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/forgekey"
|
|
)
|
|
|
|
// overlayOf answers the upper directory a command line gives the cache c, "" when it gives none.
|
|
func overlayOf(argv []string, c string) string {
|
|
for i := 0; i+3 < len(argv); i++ {
|
|
if argv[i] == "--overlay" && argv[i+3] == c {
|
|
return argv[i+1]
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// 1921/S2: with a cache layer, a command reads the ask's kept layer above the shared cache and writes into a layer on
|
|
// disk of its own, never into the shared cache and never into a throwaway tmpfs.
|
|
func TestACommandWithACacheLayerReadsTheKeptLayerAndWritesItsOwn(t *testing.T) {
|
|
fixedSystem(t)
|
|
iso := Isolation{Workspace: "/w", Caches: []string{"/cache/gomod", "/cache/gobuild"}, CacheLayer: "/w/cachelayer"}
|
|
argv, err := iso.Command("go", "vet")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for i, c := range iso.Caches {
|
|
if !holds(argv, "--overlay-src", c, "--overlay-src", keptLayer(iso.CacheLayer, i), "--overlay") {
|
|
t.Errorf("the cache %s is not read beneath the ask's kept layer: %v", c, argv)
|
|
}
|
|
if up := overlayOf(argv, c); up == "" || filepath.Dir(filepath.Dir(filepath.Dir(up))) != "/w/cachelayer" {
|
|
t.Errorf("the cache %s is written into %q, not a layer of the ask's own: %v", c, up, argv)
|
|
}
|
|
if holds(argv, "--tmp-overlay", c) || holds(argv, "--bind", c, c) {
|
|
t.Errorf("the cache %s is thrown away or written: %v", c, argv)
|
|
}
|
|
}
|
|
if _, err := (Isolation{Workspace: "/w", CacheLayer: "cachelayer"}).Command("go"); err == nil {
|
|
t.Error("a relative cache layer was taken")
|
|
}
|
|
}
|
|
|
|
// 1921/S2: what one command wrote into the caches is kept for the ask's later commands, the shared cache stays
|
|
// unchanged, two commands at once write into layers of their own (overlayfs refuses an upper layer two mounts share),
|
|
// and a command's own layer is gone when it ends.
|
|
func TestWhatOneCommandCompiledIsReadByTheNextOfTheSameAsk(t *testing.T) {
|
|
fixedSystem(t)
|
|
root := t.TempDir()
|
|
shared := filepath.Join(root, "gobuild")
|
|
if err := os.MkdirAll(shared, 0o700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
layer := filepath.Join(root, "ws", "cachelayer")
|
|
var mu sync.Mutex
|
|
uppers := map[string]bool{}
|
|
gate := make(chan struct{})
|
|
var started sync.WaitGroup
|
|
started.Add(2)
|
|
inner := func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
|
|
up := overlayOf(args, shared)
|
|
mu.Lock()
|
|
uppers[up] = true
|
|
mu.Unlock()
|
|
if holds(args, "--", "go", "build") {
|
|
started.Done()
|
|
<-gate // both commands are running before either ends
|
|
os.MkdirAll(filepath.Join(up, "ab"), 0o700)
|
|
os.WriteFile(filepath.Join(up, "ab", args[len(args)-1]+"-d"), []byte("compiled"), 0o600)
|
|
}
|
|
return forgekey.Ran{}
|
|
}
|
|
run := Isolated(inner, Isolation{Workspace: filepath.Join(root, "ws"), Caches: []string{shared}, CacheLayer: layer})
|
|
var wg sync.WaitGroup
|
|
for _, pkg := range []string{"one", "two"} {
|
|
wg.Add(1)
|
|
go func() { defer wg.Done(); run(context.Background(), nil, "go", "build", pkg) }()
|
|
}
|
|
started.Wait()
|
|
close(gate)
|
|
wg.Wait()
|
|
if len(uppers) != 2 {
|
|
t.Fatalf("two commands at once wrote into %d layers, want 2 of their own: %v", len(uppers), uppers)
|
|
}
|
|
for _, pkg := range []string{"one", "two"} {
|
|
if b, err := os.ReadFile(filepath.Join(keptLayer(layer, 0), "ab", pkg+"-d")); err != nil || string(b) != "compiled" {
|
|
t.Errorf("what the build of %s compiled is not kept for the ask: %q %v", pkg, b, err)
|
|
}
|
|
}
|
|
for up := range uppers {
|
|
if _, err := os.Stat(up); !os.IsNotExist(err) {
|
|
t.Errorf("the command's own layer %s outlived it: %v", up, err)
|
|
}
|
|
}
|
|
var next []string
|
|
Isolated(func(ctx context.Context, env []string, name string, args ...string) forgekey.Ran {
|
|
next = args
|
|
return forgekey.Ran{}
|
|
}, Isolation{Workspace: filepath.Join(root, "ws"), Caches: []string{shared}, CacheLayer: layer})(context.Background(), nil, "go", "test")
|
|
if !holds(next, "--overlay-src", shared, "--overlay-src", keptLayer(layer, 0)) {
|
|
t.Errorf("the next command does not read the kept layer: %v", next)
|
|
}
|
|
if entries, _ := os.ReadDir(shared); len(entries) != 0 {
|
|
t.Errorf("the shared cache was written: %v", entries)
|
|
}
|
|
}
|