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.108852b4worked 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.
80 lines
2.3 KiB
Go
80 lines
2.3 KiB
Go
package proc
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/yetone/magpie/internal/testenv"
|
|
)
|
|
|
|
// A CLI is asked its version once per binary: one that fails to run (macOS
|
|
// stopping it as malware, #864) isn't started again until it changes, one
|
|
// cut off by the timeout is, and an npm CLI's is read from its package
|
|
// without running it.
|
|
func TestVersionRunsOnce(t *testing.T) {
|
|
if runtime.GOOS == "windows" {
|
|
t.Skip("shell scripts")
|
|
}
|
|
dir := t.TempDir()
|
|
runs := filepath.Join(dir, "runs")
|
|
bin := filepath.Join(dir, "codex")
|
|
write := func(body string) {
|
|
testenv.Program(t, bin, "#!/bin/sh\necho x >> '"+runs+"'\n"+body)
|
|
}
|
|
count := func() int {
|
|
b, _ := os.ReadFile(runs)
|
|
return strings.Count(string(b), "x")
|
|
}
|
|
write("exit 9\n")
|
|
for range 3 {
|
|
if v := Version(bin); v != "" {
|
|
t.Fatalf("a failing CLI said %q", v)
|
|
}
|
|
}
|
|
if n := count(); n != 1 {
|
|
t.Fatalf("a failing CLI was run %d times", n)
|
|
}
|
|
// changed (reinstalled), it is asked again
|
|
write("echo codex-cli 0.161.1\n")
|
|
os.Chtimes(bin, time.Now().Add(time.Minute), time.Now().Add(time.Minute))
|
|
if v := Version(bin); v != "codex-cli 0.161.1" {
|
|
t.Fatalf("got %q", v)
|
|
}
|
|
if n := count(); n != 2 {
|
|
t.Fatalf("run %d times", n)
|
|
}
|
|
|
|
// a timeout is asked again
|
|
other := filepath.Join(dir, "slow")
|
|
testenv.Program(t, other, "#!/bin/sh\n")
|
|
old := runVersion
|
|
t.Cleanup(func() { runVersion = old })
|
|
asked := 0
|
|
runVersion = func(string) (string, error) { asked++; return "", context.DeadlineExceeded }
|
|
Version(other)
|
|
Version(other)
|
|
if asked != 2 {
|
|
t.Fatalf("a timed-out CLI was asked %d times", asked)
|
|
}
|
|
|
|
// an npm CLI: the package says it, nothing is run
|
|
asked = 0
|
|
pkg := filepath.Join(dir, "lib", "node_modules", "@openai", "codex")
|
|
os.MkdirAll(filepath.Join(pkg, "bin"), 0o755)
|
|
os.WriteFile(filepath.Join(pkg, "package.json"), []byte(`{"name":"@openai/codex","version":"0.162.0"}`), 0o644)
|
|
os.WriteFile(filepath.Join(pkg, "bin", "codex.js"), []byte("#!/usr/bin/env node\n"), 0o755)
|
|
link := filepath.Join(dir, "bin", "codex")
|
|
os.MkdirAll(filepath.Dir(link), 0o755)
|
|
if err := os.Symlink(filepath.Join(pkg, "bin", "codex.js"), link); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if v := Version(link); v != "0.162.0" || asked != 0 {
|
|
t.Fatalf("npm CLI: %q, run %d times", v, asked)
|
|
}
|
|
}
|