Skip to content

Commit e176b0b

Browse files
committed
Windows doesn't send pty EOF unless wait is called
1 parent ef53e28 commit e176b0b

File tree

4 files changed

+19
-22
lines changed

4 files changed

+19
-22
lines changed

helpers.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"errors"
66
"os"
7-
"os/exec"
87
"strings"
98
"time"
109
)
@@ -23,20 +22,10 @@ type cmdExit struct {
2322
Err error
2423
}
2524

26-
// waitForCmdExit turns process.wait() into a channel so that it can be used within a select{} statement
27-
func waitForCmdExit(cmd *exec.Cmd) chan *cmdExit {
28-
exit := make(chan *cmdExit, 1)
29-
go func() {
30-
err := cmd.Wait()
31-
exit <- &cmdExit{ProcessState: cmd.ProcessState, Err: err}
32-
}()
33-
return exit
34-
}
35-
3625
func waitChan[T any](wait func() T) chan T {
37-
done := make(chan T)
26+
done := make(chan T, 1)
3827
go func() {
39-
done <- wait()
28+
wait()
4029
close(done)
4130
}()
4231
return done

termtest.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ type TermTest struct {
2323
ptmx pty.Pty
2424
outputProducer *outputProducer
2525
listenError chan error
26+
waitError chan error
2627
opts *Opts
2728
}
2829

@@ -78,6 +79,7 @@ func New(cmd *exec.Cmd, opts ...SetOpt) (*TermTest, error) {
7879
cmd: cmd,
7980
outputProducer: newOutputProducer(optv),
8081
listenError: make(chan error, 1),
82+
waitError: make(chan error, 1),
8183
opts: optv,
8284
}
8385

@@ -227,6 +229,7 @@ func (tt *TermTest) start() (rerr error) {
227229
tt.term = vt10x.New(vt10x.WithWriter(ptmx), vt10x.WithSize(tt.opts.Cols, tt.opts.Rows))
228230

229231
// Start listening for output
232+
// We use a waitgroup here to ensure the listener is active before consumers are attached.
230233
wg := &sync.WaitGroup{}
231234
wg.Add(1)
232235
go func() {
@@ -235,6 +238,16 @@ func (tt *TermTest) start() (rerr error) {
235238
err := tt.outputProducer.Listen(tt.ptmx, tt.term)
236239
tt.listenError <- err
237240
}()
241+
242+
go func() {
243+
// We start waiting right away, because on Windows the PTY isn't closed until the process exits, which in turn
244+
// can't happen unless we've told the pty we're ready for it to close.
245+
// This of course isn't ideal, but until the pty library fixes the cross-platform inconsistencies we have to
246+
// work around these limitations.
247+
defer tt.opts.Logger.Printf("waitIndefinitely finished")
248+
tt.waitError <- tt.waitIndefinitely()
249+
}()
250+
238251
wg.Wait()
239252

240253
return nil
@@ -247,13 +260,8 @@ func (tt *TermTest) Wait(timeout time.Duration) (rerr error) {
247260
tt.opts.Logger.Println("wait called")
248261
defer tt.opts.Logger.Println("wait closed")
249262

250-
errc := make(chan error, 1)
251-
go func() {
252-
errc <- tt.WaitIndefinitely()
253-
}()
254-
255263
select {
256-
case err := <-errc:
264+
case err := <-tt.waitError:
257265
// WaitIndefinitely already invokes the expect error handler
258266
return err
259267
case <-time.After(timeout):

termtest_other.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func syscallErrorCode(err error) int {
1212
return -1
1313
}
1414

15-
func (tt *TermTest) WaitIndefinitely() error {
15+
func (tt *TermTest) waitIndefinitely() error {
1616
tt.opts.Logger.Println("WaitIndefinitely called")
1717
defer tt.opts.Logger.Println("WaitIndefinitely closed")
1818

termtest_windows.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ func syscallErrorCode(err error) int {
1616
return 0
1717
}
1818

19-
// WaitIndefinitely on Windows has to work around a Windows PTY bug where the PTY will NEVER exit by itself:
19+
// waitIndefinitely on Windows has to work around a Windows PTY bug where the PTY will NEVER exit by itself:
2020
// https://github.com/photostorm/pty/issues/3
2121
// Instead we wait for the process itself to exit, and after a grace period will shut down the pty.
22-
func (tt *TermTest) WaitIndefinitely() error {
22+
func (tt *TermTest) waitIndefinitely() error {
2323
tt.opts.Logger.Println("WaitIndefinitely called")
2424
defer tt.opts.Logger.Println("WaitIndefinitely closed")
2525

0 commit comments

Comments
 (0)