diff --git a/kitty_tests/atexit.py b/kitty_tests/atexit.py index 3caa4fbb4..45a2033e3 100644 --- a/kitty_tests/atexit.py +++ b/kitty_tests/atexit.py @@ -23,6 +23,11 @@ class Atexit(BaseTest): def tearDown(self): shutil.rmtree(self.tdir) + def test_go_atexit(self): + cp = subprocess.run([kitten_exe(), '__atexit__', 'test'], cwd=self.tdir) + self.ae(cp.returncode, 0) + self.assertFalse(os.listdir(self.tdir)) + def test_atexit(self): def r(action='close'): diff --git a/tools/cmd/atexit/main.go b/tools/cmd/atexit/main.go index 91d62edb8..5fd527655 100644 --- a/tools/cmd/atexit/main.go +++ b/tools/cmd/atexit/main.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/kovidgoyal/kitty/tools/cli" + "github.com/kovidgoyal/kitty/tools/utils" "github.com/kovidgoyal/kitty/tools/utils/shm" ) @@ -33,6 +34,9 @@ func main() (rc int, err error) { rc = 0 for _, line := range lines { if action, rest, found := strings.Cut(line, " "); found { + if !found { + continue + } switch action { case "unlink": if err := os.Remove(rest); err != nil && !os.IsNotExist(err) { @@ -55,6 +59,25 @@ func main() (rc int, err error) { return } +func do_test() (err error) { + if err = os.WriteFile("file", []byte("moose"), 0o600); err != nil { + return + } + if err = utils.AtExitUnlink("file"); err != nil { + return + } + if err = os.Mkdir("dir", 0o700); err != nil { + return + } + if err = utils.AtExitRmtree("dir"); err != nil { + return + } + if err = os.WriteFile("dir/sf", []byte("cat"), 0o600); err != nil { + return + } + return +} + func EntryPoint(root *cli.Command) { root.AddSubCommand(&cli.Command{ Name: "__atexit__", @@ -62,6 +85,13 @@ func EntryPoint(root *cli.Command) { OnlyArgsAllowed: true, Run: func(cmd *cli.Command, args []string) (rc int, err error) { if len(args) != 0 { + if args[0] == "test" { + rc = 0 + if err = do_test(); err != nil { + rc = 1 + } + return + } return 1, fmt.Errorf("Usage: __atexit__") } return main() diff --git a/tools/utils/atexit.go b/tools/utils/atexit.go index 14de67315..b9538b8de 100644 --- a/tools/utils/atexit.go +++ b/tools/utils/atexit.go @@ -1,10 +1,12 @@ package utils import ( + "flag" "fmt" "io" "os" "os/exec" + "path/filepath" "sync" "sync/atomic" ) @@ -18,11 +20,21 @@ type worker struct { var worker_started atomic.Bool +// IsTesting returns true if the code is being run by "go test". +func IsTesting() bool { + return flag.Lookup("test.v") != nil +} + var get_worker = sync.OnceValues(func() (*worker, error) { exe, err := os.Executable() if err != nil { return nil, err } + if IsTesting() { + if exe, err = filepath.Abs("../../kitty/launcher/kitten"); err != nil { + return nil, err + } + } cmd := exec.Command(exe, "__atexit__") cmd.Stdout = nil cmd.Stderr = os.Stderr @@ -32,23 +44,36 @@ var get_worker = sync.OnceValues(func() (*worker, error) { return nil, err } ans.stdin_pipe = si - if err = cmd.Run(); err != nil { + if err = cmd.Start(); err != nil { return nil, err } worker_started.Store(true) return &ans, nil }) -func WaitForAtexitWorkerToFinish() { +func WaitForAtexitWorkerToFinish() error { if worker_started.Load() { if w, err := get_worker(); err == nil { w.stdin_pipe.Close() - _ = w.cmd.Wait() + return w.cmd.Wait() + } else { + return err } } + return nil } func register(prefix, path string) error { + // no atexit cleanup is done as we dont have a good place to run + // WaitForAtexitWorkerToFinish() and anyway we may want to run tests in + // parallel, etc. + if IsTesting() { + return nil + } + path, err := filepath.Abs(path) + if err != nil { + return err + } if w, err := get_worker(); err == nil { _, err = fmt.Fprintln(w.stdin_pipe, prefix+" "+path) return err