Files
yetone 06420a7121 tests: a fake program is a file macOS has already let run (testenv.Program), so a fake's first start no longer eats a test's wait under load
macOS checks a newly written executable before its first exec: 0.27s
alone, 2-5s while other new programs start (a go test ./... starting its
test binaries). The same file again takes 0.009s, and the pass is kept
across a rename and an in-place rewrite. 108852b4 worked round this for one
test with /bin/sh -c; the class was every test that writes a `#!/bin/sh`
fake and times its answer.

testenv.Program(t, path, script) replaces os.WriteFile(path, script, 0o755)
for fakes. On macOS it renames a file that has run once from a pool to path
and writes the script into it in place; at the test's cleanup the file goes
back to the pool (only a path the test created; a stand-in it wrote over
stays). Fakes still end with their tests: nothing about how they are
started or waited on changed. Run (and gateway's TestMain) remove the pool
at the end. TestMain's PATH stand-ins are run once in the background
(testenv.StandIns). Other systems write the file as before.

Converted every `#!/bin/sh` fake written by tests under internal/ (agent,
gateway, gui, library, plugin, proc, provider, update). Windows, binary
and node/bun fakes are unchanged.

Under the same load (3 loops each starting 8 freshly written scripts at a
time), 4 rounds each, before (108852b4 / origin/main) vs after:
- provider TestSavedButSignedOutClaudeServed: 4/4 fail (8.01s) -> 4/4 pass
- provider TestClaudeSignInOverStaleCredentials: 4/4 fail (5.00s) -> 4/4 pass
- provider TestDevinSignedInOnlyInMagpie: 3/4 fail ("CLI's own []") -> 4/4 pass
- gateway TestClaudeRunEndsWhenTheClientLeaves: 4/4 fail -> 4/4 pass
  (and 3/3 fail -> 3/3 pass at a third of that load)
- gateway TestClientGoneRecordedAsCanceled: 4/4 fail -> 4/4 pass
- gui TestProvidersAnswerWhileListsComeIn ("cursor found false" at 3.03s):
  4/4 fail -> 4/4 pass. The same class, not bfe8bcaf's test order.
- proc TestUserPath (login shell's PATH never came, 7.01s): 4/4 fail ->
  4/4 pass
Unloaded, every touched package passes; no pool directory or process is
left after a run.
2026-10-07 03:00:47 +08:00

179 lines
4.8 KiB
Go

package testenv
import (
"bytes"
"fmt"
"os"
"path/filepath"
"runtime"
"strconv"
"sync"
"testing"
)
// Program writes script, a `#!` script, at path as a program a test runs,
// where os.WriteFile(path, script, 0o755) would. On macOS the file is one
// the system has already let run: macOS checks a newly written executable
// before its first exec, a quarter of a second alone and several seconds
// while other new programs start (a go test ./... starting its test
// binaries), which ate the wait of every test that times a fake's answer
// (TestDevinSignedInOnlyInMagpie, TestClaudeRunEndsWhenTheClientLeaves).
// The check is made once a file: one already run keeps its pass when it is
// renamed or written over in place. So Program takes a file that has run
// from a pool, renames it to path and writes the script into it; when the
// test ends, the file goes back to the pool for the next test.
func Program[S ~string | ~[]byte](t testing.TB, path string, script S) {
t.Helper()
_, err := os.Lstat(path)
existed := err == nil
if err := WriteProgram(path, script); err != nil {
t.Fatal(err)
}
// a file the test only wrote over (a stand-in TestMain made) stays,
// as it would have
if !existed {
t.Cleanup(func() { reclaim(path) })
}
}
// WriteProgram is Program for a fake that lasts the whole test binary, made
// from TestMain, or written by a stand-in for the code under test: it is
// not taken back when a test ends.
func WriteProgram[S ~string | ~[]byte](path string, script S) error {
b := []byte(script)
if !bytes.HasPrefix(b, []byte("#!")) {
// a binary written into a file that passed as a script is no test's
return fmt.Errorf("testenv.Program: %s is no #! script", path)
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
if runtime.GOOS != "darwin" {
return os.WriteFile(path, b, 0o755)
}
if !ran(path) {
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
return err
}
if err := warmAt(path); err != nil {
return err
}
}
// written over in place: the file keeps the pass of its first run
f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0)
if err != nil {
return err
}
if _, err := f.Write(b); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
return os.Chmod(path, 0o755)
}
// RemovePrograms deletes the pool, after the tests: Run does it, and a
// TestMain of its own calls it.
func RemovePrograms() {
warming.Wait()
pool.Lock()
defer pool.Unlock()
if pool.dir != "" {
os.RemoveAll(pool.dir)
}
pool.dir, pool.free, pool.ran = "", nil, nil
}
// pool holds the files that have run once: the free ones in dir, and in ran
// each one at a program's path.
var pool struct {
sync.Mutex
dir string
n int
free []string
ran map[string]os.FileInfo
}
// ran says whether path is a file that has run once, put there by
// WriteProgram and still in place.
func ran(path string) bool {
pool.Lock()
fi, ok := pool.ran[path]
pool.Unlock()
now, err := os.Stat(path)
return ok && err == nil && os.SameFile(fi, now)
}
// warmAt puts a file that has run once at path: a free one from the pool,
// or a new one run there now.
func warmAt(path string) error {
pool.Lock()
for len(pool.free) > 0 {
f := pool.free[0]
pool.free = pool.free[1:]
if os.Rename(f, path) == nil {
pool.Unlock()
return record(path)
}
os.Remove(f) // another file system: the pool is no help here
}
pool.Unlock()
if err := os.WriteFile(path, []byte("#!/bin/sh\nexit 0\n"), 0o755); err != nil {
return err
}
// run as a test's code runs it (proc.Command is out of reach: proc's
// own tests use this package), on macOS only, so no console to hide
p, err := os.StartProcess(path, []string{path}, &os.ProcAttr{})
if err != nil {
return fmt.Errorf("testenv.Program: a first run of %s: %v", path, err)
}
if st, err := p.Wait(); err != nil || !st.Success() {
return fmt.Errorf("testenv.Program: a first run of %s: %v %v", path, st, err)
}
return record(path)
}
// record notes path as a file that has run once.
func record(path string) error {
fi, err := os.Stat(path)
if err != nil {
return err
}
pool.Lock()
defer pool.Unlock()
if pool.ran == nil {
pool.ran = map[string]os.FileInfo{}
}
pool.ran[path] = fi
return nil
}
// reclaim takes path back into the pool when it is still the file Program
// put there: one the code under test replaced, or that is gone, is not.
func reclaim(path string) {
if runtime.GOOS != "darwin" {
return
}
pool.Lock()
defer pool.Unlock()
fi, ok := pool.ran[path]
delete(pool.ran, path)
if now, err := os.Stat(path); !ok || err != nil || !os.SameFile(fi, now) {
return
}
if pool.dir == "" {
d, err := os.MkdirTemp("", "magpie-test-programs-")
if err != nil {
return
}
pool.dir = d
}
pool.n++
f := filepath.Join(pool.dir, strconv.Itoa(pool.n))
if os.Rename(path, f) == nil {
pool.free = append(pool.free, f)
}
}