Skip to content

Commit f24101a

Browse files
committed
Get windows tests working with conpty
1 parent 826566b commit f24101a

File tree

5 files changed

+37
-223
lines changed

5 files changed

+37
-223
lines changed

expect/conpty/conpty.go

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ type ConPty struct {
1919
hpCon windows.Handle
2020
pipeFdIn windows.Handle
2121
pipeFdOut windows.Handle
22+
pipe3 windows.Handle
23+
pipe4 windows.Handle
2224
consoleSize uintptr
23-
inPipe *os.File
24-
outPipe *os.File
25+
outputR *os.File
26+
outputW *os.File
27+
inputR *os.File
28+
inputW *os.File
29+
closed bool
2530
}
2631

2732
// New returns a new ConPty pseudo terminal device
@@ -35,38 +40,45 @@ func New(columns int16, rows int16) (*ConPty, error) {
3540

3641
// Close closes the pseudo-terminal and cleans up all attached resources
3742
func (c *ConPty) Close() error {
43+
if (c.closed) {
44+
return nil
45+
}
46+
3847
err := closePseudoConsole(c.hpCon)
39-
c.inPipe.Close()
40-
c.outPipe.Close()
48+
c.outputR.Close()
49+
c.outputW.Close()
50+
c.inputR.Close()
51+
c.inputW.Close()
52+
c.closed = true
4153
return err
4254
}
4355

4456
// OutPipe returns the output pipe of the pseudo terminal
4557
func (c *ConPty) OutPipe() *os.File {
46-
return c.inPipe
58+
return c.outputR
4759
}
4860

4961
func (c *ConPty) Reader() io.Reader {
50-
return c.outPipe
62+
return c.outputW
5163
}
5264

5365
// InPipe returns input pipe of the pseudo terminal
5466
// Note: It is safer to use the Write method to prevent partially-written VT sequences
5567
// from corrupting the terminal
5668
func (c *ConPty) InPipe() *os.File {
57-
return c.outPipe
69+
return c.inputR
5870
}
5971

6072
func (c *ConPty) WriteString(str string) (int, error) {
61-
return c.inPipe.WriteString(str)
73+
return c.inputW.WriteString(str)
6274
}
6375

6476
func (c *ConPty) createPseudoConsoleAndPipes() error {
6577
// These are the readers/writers for "stdin", but we only need this to
6678
// successfully call CreatePseudoConsole. After, we can throw it away.
6779
var hPipeInW, hPipeInR windows.Handle
6880

69-
// Create the stdin pipe although we never use this.
81+
// Create the stdin pipe
7082
if err := windows.CreatePipe(&hPipeInR, &hPipeInW, nil, 0); err != nil {
7183
return err
7284
}
@@ -81,16 +93,15 @@ func (c *ConPty) createPseudoConsoleAndPipes() error {
8193
return fmt.Errorf("failed to create pseudo console: %d, %v", uintptr(c.hpCon), err)
8294
}
8395

84-
// Close our stdin cause we're never going to use it
85-
if hPipeInR != windows.InvalidHandle {
86-
windows.CloseHandle(hPipeInR)
87-
}
88-
if hPipeInW != windows.InvalidHandle {
89-
windows.CloseHandle(hPipeInW)
90-
}
96+
c.pipe3 = hPipeInR
97+
c.pipe4 = hPipeInW
98+
99+
c.outputR = os.NewFile(uintptr(c.pipeFdIn), "|0")
100+
c.outputW = os.NewFile(uintptr(c.pipeFdOut), "|1")
91101

92-
c.inPipe = os.NewFile(uintptr(c.pipeFdIn), "|0")
93-
c.outPipe = os.NewFile(uintptr(c.pipeFdOut), "|1")
102+
c.inputR = os.NewFile(uintptr(c.pipe3), "|2")
103+
c.inputW = os.NewFile(uintptr(c.pipe4), "|3")
104+
c.closed = false
94105

95106
return nil
96107
}

expect/console.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"io/ioutil"
2222
"log"
2323
"os"
24-
"time"
2524
"unicode/utf8"
2625

2726
"github.com/coder/coder/expect/pty"
@@ -49,7 +48,6 @@ type ConsoleOpts struct {
4948
Closers []io.Closer
5049
ExpectObservers []ExpectObserver
5150
SendObservers []SendObserver
52-
ReadTimeout *time.Duration
5351
}
5452

5553
// ExpectObserver provides an interface for a function callback that will
@@ -114,14 +112,6 @@ func WithSendObserver(observers ...SendObserver) ConsoleOpt {
114112
}
115113
}
116114

117-
// WithDefaultTimeout sets a default read timeout during Expect statements.
118-
func WithDefaultTimeout(timeout time.Duration) ConsoleOpt {
119-
return func(opts *ConsoleOpts) error {
120-
opts.ReadTimeout = &timeout
121-
return nil
122-
}
123-
}
124-
125115
// NewConsole returns a new Console with the given options.
126116
func NewConsole(opts ...ConsoleOpt) (*Console, error) {
127117
options := ConsoleOpts{

expect/expect.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"bytes"
2020
"fmt"
2121
"io"
22-
"time"
2322
"unicode/utf8"
2423
)
2524

@@ -59,11 +58,6 @@ func (c *Console) Expect(opts ...ExpectOpt) (string, error) {
5958
writer := io.MultiWriter(append(c.opts.Stdouts, buf)...)
6059
runeWriter := bufio.NewWriterSize(writer, utf8.UTFMax)
6160

62-
readTimeout := c.opts.ReadTimeout
63-
if options.ReadTimeout != nil {
64-
readTimeout = options.ReadTimeout
65-
}
66-
6761
var matcher Matcher
6862
var err error
6963

@@ -78,13 +72,6 @@ func (c *Console) Expect(opts ...ExpectOpt) (string, error) {
7872
}()
7973

8074
for {
81-
if readTimeout != nil {
82-
err = c.passthroughPipe.SetReadDeadline(time.Now().Add(*readTimeout))
83-
if err != nil {
84-
return buf.String(), err
85-
}
86-
}
87-
8875
var r rune
8976
r, _, err = c.runeReader.ReadRune()
9077
if err != nil {

expect/expect_test.go

Lines changed: 1 addition & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,11 @@ import (
1818
"bufio"
1919
"errors"
2020
"fmt"
21-
"io"
22-
// "io/ioutil"
23-
//"log"
24-
// "os"
25-
// "os/exec"
21+
"io"
2622
"runtime/debug"
2723
"strings"
2824
"sync"
2925
"testing"
30-
"time"
3126
)
3227

3328
var (
@@ -70,7 +65,6 @@ func newTestConsole(t *testing.T, opts ...ConsoleOpt) (*Console, error) {
7065
opts = append([]ConsoleOpt{
7166
expectNoError(t),
7267
sendNoError(t),
73-
WithDefaultTimeout(time.Second),
7468
}, opts...)
7569
return NewTestConsole(t, opts...)
7670
}
@@ -131,14 +125,12 @@ func TestExpectf(t *testing.T) {
131125
c.SendLine("2")
132126
c.Expectf("What is %s backwards?", "Netflix")
133127
c.SendLine("xilfteN")
134-
c.ExpectEOF()
135128
}()
136129

137130
err = Prompt(c.InTty(), c.OutTty())
138131
if err != nil {
139132
t.Errorf("Expected no error but got '%s'", err)
140133
}
141-
testCloser(t, c)
142134
wg.Wait()
143135
}
144136

@@ -159,15 +151,12 @@ func TestExpect(t *testing.T) {
159151
c.SendLine("2")
160152
c.ExpectString("What is Netflix backwards?")
161153
c.SendLine("xilfteN")
162-
//c.ExpectEOF()
163154
}()
164155

165156
err = Prompt(c.InTty(), c.OutTty())
166157
if err != nil {
167158
t.Errorf("Expected no error but got '%s'", err)
168159
}
169-
// close the pts so we can expect EOF
170-
testCloser(t, c)
171160
wg.Wait()
172161
}
173162

@@ -187,174 +176,11 @@ func TestExpectOutput(t *testing.T) {
187176
defer wg.Done()
188177
c.ExpectString("What is 1+1?")
189178
c.SendLine("3")
190-
//c.ExpectEOF()
191179
}()
192180

193181
err = Prompt(c.InTty(), c.OutTty())
194182
if err == nil || err != ErrWrongAnswer {
195183
t.Errorf("Expected error '%s' but got '%s' instead", ErrWrongAnswer, err)
196184
}
197-
testCloser(t, c)
198185
wg.Wait()
199186
}
200-
201-
// TODO: Needs to be updated to work on Windows
202-
// func TestExpectDefaultTimeout(t *testing.T) {
203-
// t.Parallel()
204-
205-
// c, err := NewTestConsole(t, WithDefaultTimeout(0))
206-
// if err != nil {
207-
// t.Errorf("Expected no error but got'%s'", err)
208-
// }
209-
// defer testCloser(t, c)
210-
211-
// var wg sync.WaitGroup
212-
// wg.Add(1)
213-
// go func() {
214-
// defer wg.Done()
215-
// Prompt(c.InTty(), c.OutTty())
216-
// }()
217-
218-
// _, err = c.ExpectString("What is 1+2?")
219-
// if err == nil || !strings.Contains(err.Error(), "i/o timeout") {
220-
// t.Errorf("Expected error to contain 'i/o timeout' but got '%s' instead", err)
221-
// }
222-
223-
// //Close to unblock Prompt and wait for the goroutine to exit.
224-
// c.Close()
225-
// wg.Wait()
226-
// }
227-
228-
// func TestExpectTimeout(t *testing.T) {
229-
// t.Parallel()
230-
231-
// c, err := NewTestConsole(t)
232-
// if err != nil {
233-
// t.Errorf("Expected no error but got'%s'", err)
234-
// }
235-
// defer testCloser(t, c)
236-
237-
// var wg sync.WaitGroup
238-
// wg.Add(1)
239-
// go func() {
240-
// defer wg.Done()
241-
// Prompt(c.InTty(), c.OutTty())
242-
// }()
243-
244-
// _, err = c.Expect(String("What is 1+2?"), WithTimeout(0))
245-
// if err == nil || !strings.Contains(err.Error(), "i/o timeout") {
246-
// t.Errorf("Expected error to contain 'i/o timeout' but got '%s' instead", err)
247-
// }
248-
249-
// //Close to unblock Prompt and wait for the goroutine to exit.
250-
// c.Close()
251-
// wg.Wait()
252-
// }
253-
254-
// func TestExpectDefaultTimeoutOverride(t *testing.T) {
255-
// t.Parallel()
256-
257-
// c, err := newTestConsole(t, WithDefaultTimeout(100*time.Millisecond))
258-
// if err != nil {
259-
// t.Errorf("Expected no error but got'%s'", err)
260-
// }
261-
// defer testCloser(t, c)
262-
263-
// var wg sync.WaitGroup
264-
// wg.Add(1)
265-
// go func() {
266-
// defer wg.Done()
267-
// err = Prompt(c.InTty(), c.OutTty())
268-
// if err != nil {
269-
// t.Errorf("Expected no error but got '%s'", err)
270-
// }
271-
// time.Sleep(200 * time.Millisecond)
272-
// c.Close()
273-
// }()
274-
275-
// c.ExpectString("What is 1+1?")
276-
// c.SendLine("2")
277-
// c.ExpectString("What is Netflix backwards?")
278-
// c.SendLine("xilfteN")
279-
// c.Expect(EOF, PTSClosed, WithTimeout(time.Second))
280-
281-
// wg.Wait()
282-
// }
283-
284-
// func TestEditor(t *testing.T) {
285-
// if _, err := exec.LookPath("vi"); err != nil {
286-
// t.Skip("vi not found in PATH")
287-
// }
288-
// t.Parallel()
289-
290-
// c, err := NewConsole(expectNoError(t), sendNoError(t))
291-
// if err != nil {
292-
// t.Errorf("Expected no error but got '%s'", err)
293-
// }
294-
// defer testCloser(t, c)
295-
296-
// file, err := ioutil.TempFile("", "")
297-
// if err != nil {
298-
// t.Errorf("Expected no error but got '%s'", err)
299-
// }
300-
301-
// cmd := exec.Command("vi", file.Name())
302-
// cmd.Stdin = c.InTty()
303-
// cmd.Stdout = c.OutTty()
304-
// cmd.Stderr = c.OutTty()
305-
306-
// var wg sync.WaitGroup
307-
// wg.Add(1)
308-
// go func() {
309-
// defer wg.Done()
310-
// c.Send("iHello world\x1b")
311-
// c.SendLine(":wq!")
312-
// c.ExpectEOF()
313-
// }()
314-
315-
// err = cmd.Run()
316-
// if err != nil {
317-
// t.Errorf("Expected no error but got '%s'", err)
318-
// }
319-
320-
// testCloser(t, c)
321-
// wg.Wait()
322-
323-
// data, err := ioutil.ReadFile(file.Name())
324-
// if err != nil {
325-
// t.Errorf("Expected no error but got '%s'", err)
326-
// }
327-
// if string(data) != "Hello world\n" {
328-
// t.Errorf("Expected '%s' to equal '%s'", string(data), "Hello world\n")
329-
// }
330-
// }
331-
332-
// func ExampleConsole_echo() {
333-
// c, err := NewConsole(WithStdout(os.Stdout))
334-
// if err != nil {
335-
// log.Fatal(err)
336-
// }
337-
// defer c.Close()
338-
339-
// cmd := exec.Command("echo")
340-
// cmd.Stdin = c.InTty()
341-
// cmd.Stdout = c.OutTty()
342-
// cmd.Stderr = c.OutTty()
343-
344-
// err = cmd.Start()
345-
// if err != nil {
346-
// log.Fatal(err)
347-
// }
348-
349-
// c.Send("Hello world")
350-
// c.ExpectString("Hello world")
351-
// c.Close()
352-
// c.ExpectEOF()
353-
354-
// err = cmd.Wait()
355-
// if err != nil {
356-
// log.Fatal(err)
357-
// }
358-
359-
// Output: Hello world
360-
// }

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy