Repository navigation
Expand file tree
/
Copy pathconsole_unix_test.go
More file actions
87 lines (79 loc) · 2.75 KB
/
Copy pathconsole_unix_test.go
File metadata and controls
87 lines (79 loc) · 2.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//go:build linux || darwin
package main
import (
"bytes"
"errors"
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
"time"
"github.com/jmwri/flockdeck/internal/store"
)
// -detach gives the terminal back. It used to hold the prompt for as long as
// the run lasted, and to end with the terminal, the opposite of its promise.
// Now the command returns once start-up is over, having printed the address
// and how to stop the run, and the run is still there to be stopped.
//
// With -no-window as well it held the prompt for good: only a run with a
// window let the terminal go.
func TestDetachGivesTheTerminalBack(t *testing.T) {
if testing.Short() {
t.Skip("builds the program")
}
exe := filepath.Join(t.TempDir(), "flockdeck")
build := exec.Command("go", "build", "-o", exe, ".")
if out, err := build.CombinedOutput(); err != nil {
t.Fatalf("build: %v\n%s", err, out)
}
for _, flags := range [][]string{{"-detach"}, {"-detach", "-no-window"}} {
t.Run(strings.Join(flags, " "), func(t *testing.T) { detachGivesTheTerminalBack(t, exe, flags) })
}
}
func detachGivesTheTerminalBack(t *testing.T, exe string, flags []string) {
home := t.TempDir()
t.Setenv("HOME", home)
t.Setenv("XDG_CONFIG_HOME", filepath.Join(home, "config"))
t.Setenv("XDG_CACHE_HOME", filepath.Join(home, "cache"))
t.Setenv(updateEnv, "off")
var out bytes.Buffer
cmd := exec.Command(exe, append(append([]string{"-solo"}, flags...), "-shell", "-C", t.TempDir())...)
cmd.Stdout, cmd.Stderr = &out, &out
returned := make(chan error, 1)
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
go func() { returned <- cmd.Wait() }()
var err error
select {
case err = <-returned:
case <-time.After(60 * time.Second):
_ = cmd.Process.Kill()
<-returned
}
// Whatever happened, the run this started does not outlive the test.
if inst, _ := store.LoadInstance(); inst != nil {
t.Cleanup(func() { stopProcess(inst.PID) })
}
if err != nil {
t.Fatalf("flockdeck -detach: %v, having printed %q; want it to return once started", err, out.String())
}
if !strings.Contains(out.String(), "Running detached") {
t.Errorf("flockdeck -detach printed %q, want its address and how to stop it", out.String())
}
quit, err := exec.Command(exe, "-quit").CombinedOutput()
if err != nil || !strings.Contains(string(quit), "stopped") {
t.Errorf("-quit after -detach returned: %v, %q; want the detached run there to stop", err, quit)
}
}
// stopProcess waits up to 20 s for pid to end, and then ends it.
func stopProcess(pid int) {
gone := func() bool { return errors.Is(syscall.Kill(pid, 0), syscall.ESRCH) }
for deadline := time.Now().Add(20 * time.Second); time.Now().Before(deadline); time.Sleep(50 * time.Millisecond) {
if gone() {
return
}
}
_ = syscall.Kill(pid, syscall.SIGKILL)
}