5
5
"io"
6
6
"os/exec"
7
7
"strings"
8
+ "syscall"
8
9
"testing"
9
10
"time"
10
11
@@ -16,6 +17,9 @@ func runCmd(cmds []string, t *testing.T) {
16
17
// -t enables fallthrough so we can see the output
17
18
cmd := exec .Command ("go" , cmds ... )
18
19
20
+ // Set process group for clean shutdown
21
+ cmd .SysProcAttr = & syscall.SysProcAttr {Setpgid : true }
22
+
19
23
// Get stdin pipe
20
24
stdin , err := cmd .StdinPipe ()
21
25
assert .NoError (t , err )
@@ -30,6 +34,7 @@ func runCmd(cmds []string, t *testing.T) {
30
34
31
35
// Create a channel to collect output lines
32
36
outputChan := make (chan string )
37
+ done := make (chan bool )
33
38
34
39
// Start goroutine to read stdout
35
40
go func () {
@@ -41,7 +46,10 @@ func runCmd(cmds []string, t *testing.T) {
41
46
return
42
47
}
43
48
if err != nil {
44
- t .Errorf ("Error reading stdout: %v" , err )
49
+ // Ignore pipe errors since we're killing the process
50
+ if ! strings .Contains (err .Error (), "pipe" ) {
51
+ t .Errorf ("Error reading stdout: %v" , err )
52
+ }
45
53
close (outputChan )
46
54
return
47
55
}
@@ -67,25 +75,52 @@ func runCmd(cmds []string, t *testing.T) {
67
75
assert .NoError (t , err )
68
76
}
69
77
78
+ // Close stdin to signal we're done writing
79
+ stdin .Close ()
80
+
70
81
// Collect output with timeout
71
82
receivedLines := make ([]string , 0 )
72
83
timeout := time .After (5 * time .Second )
73
84
74
- for i := 0 ; i < len (testLines ); i ++ {
75
- select {
76
- case line , ok := <- outputChan :
77
- if ! ok {
78
- t .Fatal ("Output channel closed before receiving all expected lines" )
85
+ go func () {
86
+ for i := 0 ; i < len (testLines ); i ++ {
87
+ select {
88
+ case line , ok := <- outputChan :
89
+ if ! ok {
90
+ return
91
+ }
92
+ receivedLines = append (receivedLines , line )
93
+ if len (receivedLines ) == len (testLines ) {
94
+ done <- true
95
+ return
96
+ }
97
+ case <- timeout :
98
+ done <- true
99
+ return
79
100
}
80
- receivedLines = append (receivedLines , line )
81
- case <- timeout :
82
- t .Fatal ("Timeout waiting for output" )
83
101
}
102
+ }()
103
+
104
+ <- done
105
+
106
+ // Kill the process group to ensure clean shutdown
107
+ pgid , err := syscall .Getpgid (cmd .Process .Pid )
108
+ if err == nil {
109
+ syscall .Kill (- pgid , syscall .SIGTERM )
84
110
}
85
111
86
- // Kill the process since we're done testing
87
- if err := cmd .Process .Kill (); err != nil {
88
- t .Errorf ("Failed to kill process: %v" , err )
112
+ // Wait with timeout to avoid hanging
113
+ waitChan := make (chan error )
114
+ go func () {
115
+ waitChan <- cmd .Wait ()
116
+ }()
117
+
118
+ select {
119
+ case <- waitChan :
120
+ // Process exited
121
+ case <- time .After (2 * time .Second ):
122
+ // Force kill if it didn't exit cleanly
123
+ cmd .Process .Kill ()
89
124
}
90
125
91
126
// Verify output matches input
0 commit comments