19
clientMsg = "Hi server!\n"
20
serverMsg = "Hi there, client!\n"
21
fileTemplate = "62DA0493-99A1-4327-B5A8-6C4E4466C3FC.txt"
24
// TestBadDial tests that if you dial something other than a valid pipe path, that you get back a
25
// PipeError and that you don't accidently create a file on disk (since dial uses OpenFile)
26
func TestBadDial(t *testing.T) {
27
fn := filepath.Join("C:\\", fileTemplate)
28
ns := []string{fn, "http://www.google.com", "somethingbadhere"}
29
for _, n := range ns {
31
if _, ok := err.(PipeError); !ok {
32
t.Errorf("Dialing '%s' did not result in correct error! Expected PipeError, got '%v'",
36
t.Errorf("Dialing '%s' returned non-nil connection", n)
38
if b, _ := exists(n); b {
39
t.Errorf("Dialing '%s' incorrectly created file on disk", n)
44
// TestDialExistingFile tests that if you dial with the name of an existing file,
45
// that you don't accidentally open the file (since dial uses OpenFile)
46
func TestDialExistingFile(t *testing.T) {
47
tempdir := os.TempDir()
48
fn := filepath.Join(tempdir, fileTemplate)
49
if f, err := os.Create(fn); err != nil {
50
t.Fatalf("Unexpected error creating file '%s': '%v'", fn, err)
52
// we don't actually need to write to the file, just need it to exist
57
if _, ok := err.(PipeError); !ok {
58
t.Errorf("Dialing '%s' did not result in error! Expected PipeError, got '%v'", fn, err)
61
t.Errorf("Dialing '%s' returned non-nil connection", fn)
65
// TestBadListen tests that if you listen on a bad address, that we get back a PipeError
66
func TestBadListen(t *testing.T) {
67
addrs := []string{"not a valid pipe address", `\\127.0.0.1\pipe\TestBadListen`}
68
for _, address := range addrs {
69
ln, err := Listen(address)
70
if _, ok := err.(PipeError); !ok {
71
t.Errorf("Listening on '%s' did not result in correct error! Expected PipeError, got '%v'",
75
t.Errorf("Listening on '%s' returned non-nil listener.", address)
80
// TestDoubleListen makes sure we can't listen to the same address twice.
81
func TestDoubleListen(t *testing.T) {
82
address := `\\.\pipe\TestDoubleListen`
83
ln1, err := Listen(address)
85
t.Fatalf("Listen(%q): %v", address, err)
89
ln2, err := Listen(address)
92
t.Fatalf("second Listen on %q succeeded.", address)
96
// TestPipeConnected tests whether we correctly handle clients connecting
97
// and then closing the connection between creating and connecting the
98
// pipe on the server side.
99
func TestPipeConnected(t *testing.T) {
100
address := `\\.\pipe\TestPipeConnected`
101
ln, err := Listen(address)
103
t.Fatalf("Listen(%q): %v", address, err)
107
// Create a client connection and close it immediately.
108
clientConn, err := Dial(address)
110
t.Fatalf("Error from dial: %v", err)
116
// Now create a real connection and send some data.
117
clientConn, err := Dial(address)
119
t.Fatalf("Error from dial: %v", err)
121
if _, err := clientConn.Write([]byte(content)); err != nil {
122
t.Fatalf("Error writing to pipe: %v", err)
127
serverConn, err := ln.Accept()
129
t.Fatalf("Error from accept: %v", err)
131
result, err := ioutil.ReadAll(serverConn)
133
t.Fatalf("Error from ReadAll: %v", err)
135
if string(result) != content {
136
t.Fatalf("Got %s, expected: %s", string(result), content)
141
// TestListenCloseListen tests whether Close() actually closes a named pipe properly.
142
func TestListenCloseListen(t *testing.T) {
143
address := `\\.\pipe\TestListenCloseListen`
144
ln1, err := Listen(address)
146
t.Fatalf("Listen(%q): %v", address, err)
150
ln2, err := Listen(address)
152
t.Fatalf("second Listen on %q failed.", address)
157
// TestCloseFileHandles tests that all PipeListener handles are actualy closed after
159
func TestCloseFileHandles(t *testing.T) {
160
address := `\\.\pipe\TestCloseFileHandles`
161
ln, err := Listen(address)
163
t.Fatalf("Error listening on %q: %v", address, err)
166
server := rpc.NewServer()
167
service := &RPCService{}
168
server.Register(service)
171
conn, err := ln.Accept()
173
// Ignore errors produced by a closed listener.
174
if err != ErrClosed {
175
t.Errorf("ln.Accept(): %v", err.Error())
179
go server.ServeConn(conn)
182
conn, err := Dial(address)
184
t.Fatalf("Error dialing %q: %v", address, err)
186
client := rpc.NewClient(conn)
190
if err = client.Call("RPCService.GetResponse", req, &resp); err != nil {
191
t.Fatalf("Error calling RPCService.GetResponse: %v", err)
194
t.Fatalf("Unexpected result (expected: %q, got: %q)", req, resp)
198
if ln.acceptHandle != 0 {
199
t.Fatalf("Failed to close acceptHandle")
201
if ln.acceptOverlapped.HEvent != 0 {
202
t.Fatalf("Failed to close acceptOverlapped handle")
206
// TestCancelListen tests whether Accept() can be cancelled by closing the listener.
207
func TestCancelAccept(t *testing.T) {
208
address := `\\.\pipe\TestCancelListener`
209
ln, err := Listen(address)
211
t.Fatalf("Listen(%q): %v", address, err)
214
cancelled := make(chan struct{})
215
started := make(chan struct{})
218
conn, _ := ln.Accept()
220
t.Fatalf("Unexpected incoming connection: %v", conn)
223
cancelled <- struct{}{}
226
// Close listener after 20ms. This should give the go routine enough time to be actually
227
// waiting for incoming connections inside ln.Accept().
228
time.AfterFunc(20*time.Millisecond, func() {
229
if err := ln.Close(); err != nil {
230
t.Fatalf("Error closing listener: %v", err)
233
// Any Close() should abort the ln.Accept() call within 100ms.
234
// We fail with a timeout otherwise, to avoid blocking forever on a failing test.
235
timeout := time.After(100 * time.Millisecond)
238
// This is what should happen.
240
t.Fatal("Timeout trying to cancel accept.")
244
// Test that PipeConn's read deadline works correctly
245
func TestReadDeadline(t *testing.T) {
246
address := `\\.\pipe\TestReadDeadline`
247
var wg sync.WaitGroup
250
go listenAndWait(address, wg, t)
253
c, err := Dial(address)
255
t.Fatalf("Error dialing into pipe: %v", err)
258
t.Fatal("Unexpected nil connection from Dial")
261
deadline := time.Now().Add(time.Millisecond * 50)
262
c.SetReadDeadline(deadline)
263
msg, err := bufio.NewReader(c).ReadString('\n')
266
t.Errorf("Pipe read timeout returned a non-empty message: %s", msg)
269
t.Error("Pipe read timeout returned nil error")
271
pe, ok := err.(PipeError)
273
t.Errorf("Got wrong error returned, expected PipeError, got '%t'", err)
276
t.Error("Pipe read timeout didn't return an error indicating the timeout")
279
checkDeadline(deadline, end, t)
282
// listenAndWait simply sets up a pipe listener that does nothing and closes after the waitgroup
284
func listenAndWait(address string, wg sync.WaitGroup, t *testing.T) {
285
ln, err := Listen(address)
287
t.Fatalf("Error starting to listen on pipe: %v", err)
290
t.Fatal("Got unexpected nil listener")
292
conn, err := ln.Accept()
294
t.Fatalf("Error accepting connection: %v", err)
297
t.Fatal("Got unexpected nil connection")
300
// don't read or write anything
304
// TestWriteDeadline tests that PipeConn's write deadline works correctly
305
func TestWriteDeadline(t *testing.T) {
306
address := `\\.\pipe\TestWriteDeadline`
307
var wg sync.WaitGroup
310
go listenAndWait(address, wg, t)
312
c, err := Dial(address)
314
t.Fatalf("Error dialing into pipe: %v", err)
317
t.Fatal("Unexpected nil connection from Dial")
320
// windows pipes have a buffer, so even if we don't read from the pipe,
321
// the write may succeed anyway, so we have to write a whole bunch to
323
deadline := time.Now().Add(time.Millisecond * 50)
324
c.SetWriteDeadline(deadline)
325
buffer := make([]byte, 1<<16)
326
if _, err = io.ReadFull(rand.Reader, buffer); err != nil {
327
t.Fatalf("Couldn't generate random buffer: %v", err)
329
_, err = c.Write(buffer)
334
t.Error("Pipe write timeout returned nil error")
336
pe, ok := err.(PipeError)
338
t.Errorf("Got wrong error returned, expected PipeError, got '%t'", err)
341
t.Error("Pipe write timeout didn't return an error indicating the timeout")
344
checkDeadline(deadline, end, t)
347
// TestDialTimeout tests that the DialTimeout function will actually timeout correctly
348
func TestDialTimeout(t *testing.T) {
349
timeout := time.Millisecond * 150
350
deadline := time.Now().Add(timeout)
351
c, err := DialTimeout(`\\.\pipe\TestDialTimeout`, timeout)
354
t.Errorf("DialTimeout returned non-nil connection: %v", c)
357
t.Error("DialTimeout returned nil error after timeout")
359
pe, ok := err.(PipeError)
361
t.Errorf("Got wrong error returned, expected PipeError, got '%t'", err)
364
t.Error("Dial timeout didn't return an error indicating the timeout")
367
checkDeadline(deadline, end, t)
370
// TestDialNoTimeout tests that the DialTimeout function will properly wait for the pipe and
371
// connect when it is available
372
func TestDialNoTimeout(t *testing.T) {
373
timeout := time.Millisecond * 500
374
address := `\\.\pipe\TestDialNoTimeout`
376
<-time.After(50 * time.Millisecond)
377
listenAndClose(address, t)
380
deadline := time.Now().Add(timeout)
381
c, err := DialTimeout(address, timeout)
385
t.Error("DialTimeout returned unexpected nil connection")
388
t.Error("DialTimeout returned unexpected non-nil error: ", err)
390
if end.After(deadline) {
391
t.Fatalf("Ended %v after deadline", end.Sub(deadline))
395
// TestDial tests that you can dial before a pipe is available,
396
// and that it'll pick up the pipe once it's ready
397
func TestDial(t *testing.T) {
398
address := `\\.\pipe\TestDial`
399
var wg sync.WaitGroup
403
conn, err := Dial(address)
405
t.Fatalf("Got unexpected error from Dial: %v", err)
408
t.Fatal("Got unexpected nil connection from Dial")
410
if err := conn.Close(); err != nil {
411
t.Fatalf("Got unexpected error from conection.Close(): %v", err)
416
<-time.After(50 * time.Millisecond)
417
listenAndClose(address, t)
420
type RPCService struct{}
422
func (s *RPCService) GetResponse(request string, response *string) error {
427
// TestGoRPC tests that you can run go RPC over the pipe,
428
// and that overlapping bi-directional communication is working
429
// (write while a blocking read is in progress).
430
func TestGoRPC(t *testing.T) {
431
address := `\\.\pipe\TestRPC`
432
ln, err := Listen(address)
434
t.Fatalf("Error listening on %q: %v", address, err)
436
waitExit := make(chan struct{})
443
server := rpc.NewServer()
444
server.Register(&RPCService{})
446
conn, err := ln.Accept()
448
// Ignore errors produced by a closed listener.
449
if err != ErrClosed {
450
t.Errorf("ln.Accept(): %v", err.Error())
454
go server.ServeConn(conn)
458
conn, err := Dial(address)
460
t.Fatalf("Error dialing %q: %v", address, err)
462
client := rpc.NewClient(conn)
466
if err = client.Call("RPCService.GetResponse", req, &resp); err != nil {
467
t.Fatalf("Error calling RPCService.GetResponse: %v", err)
470
t.Fatalf("Unexpected result (expected: %q, got: %q)", req, resp)
474
// listenAndClose is a helper method to just listen on a pipe and close as soon as someone connects.
475
func listenAndClose(address string, t *testing.T) {
476
ln, err := Listen(address)
478
t.Fatalf("Got unexpected error from Listen: %v", err)
481
t.Fatal("Got unexpected nil listener from Listen")
483
conn, err := ln.Accept()
485
t.Fatalf("Got unexpected error from Accept: %v", err)
488
t.Fatal("Got unexpected nil connection from Accept")
490
if err := conn.Close(); err != nil {
491
t.Fatalf("Got unexpected error from conection.Close(): %v", err)
495
// TestCommonUseCase is a full run-through of the most common use case, where you create a listener
496
// and then dial into it with several clients in succession
497
func TestCommonUseCase(t *testing.T) {
498
addrs := []string{`\\.\pipe\TestCommonUseCase`, `\\127.0.0.1\pipe\TestCommonUseCase`}
499
// always listen on the . version, since IP won't work for listening
500
ln, err := Listen(addrs[0])
502
t.Fatalf("Listen(%q) failed: %v", addrs[0], err)
506
for _, address := range addrs {
510
wg := sync.WaitGroup{}
512
for x := 0; x < clients; x++ {
514
go startClient(address, &wg, convos, t)
517
go startServer(ln, convos, t)
522
case <-time.After(time.Second):
523
t.Fatal("Failed to finish after a reasonable timeout")
528
// wait simply waits on the waitgroup and closes the returned channel when done.
529
func wait(wg *sync.WaitGroup) <-chan struct{} {
530
done := make(chan struct{})
538
// startServer accepts connections and spawns goroutines to handle them
539
func startServer(ln *PipeListener, iter int, t *testing.T) {
541
conn, err := ln.Accept()
542
if err == ErrClosed {
546
t.Fatalf("Error accepting connection: %v", err)
548
go handleConnection(conn, iter, t)
552
// handleConnection is the goroutine that handles connections on the server side
553
// it expects to read a message and then write a message, convos times, before exiting.
554
func handleConnection(conn net.Conn, convos int, t *testing.T) {
555
r := bufio.NewReader(conn)
556
for x := 0; x < convos; x++ {
557
msg, err := r.ReadString('\n')
559
t.Fatalf("Error reading from server connection: %v", err)
561
if msg != clientMsg {
562
t.Fatalf("Read incorrect message from client. Expected '%s', got '%s'", clientMsg, msg)
565
if _, err := fmt.Fprint(conn, serverMsg); err != nil {
566
t.Fatalf("Error on server writing to pipe: %v", err)
569
if err := conn.Close(); err != nil {
570
t.Fatalf("Error closing server side of connection: %v", err)
574
// startClient waits on a pipe at the given address. It expects to write a message and then
575
// read a message from the pipe, convos times, and then sends a message on the done
577
func startClient(address string, wg *sync.WaitGroup, convos int, t *testing.T) {
579
c := make(chan *PipeConn)
580
go asyncdial(address, c, t)
585
case <-time.After(time.Second):
586
// Yes this is a long timeout, but sometimes it really does take a long time.
587
t.Fatalf("Client timed out waiting for dial to resolve")
589
r := bufio.NewReader(conn)
590
for x := 0; x < convos; x++ {
591
if _, err := fmt.Fprint(conn, clientMsg); err != nil {
592
t.Fatalf("Error on client writing to pipe: %v", err)
595
msg, err := r.ReadString('\n')
597
t.Fatalf("Error reading from client connection: %v", err)
599
if msg != serverMsg {
600
t.Fatalf("Read incorrect message from server. Expected '%s', got '%s'", serverMsg, msg)
604
if err := conn.Close(); err != nil {
605
t.Fatalf("Error closing client side of pipe %v", err)
609
// asyncdial is a helper that dials and returns the connection on the given channel.
610
// this is useful for being able to give dial a timeout
611
func asyncdial(address string, c chan *PipeConn, t *testing.T) {
612
conn, err := Dial(address)
614
t.Fatalf("Error from dial: %v", err)
619
// exists is a simple helper function to detect if a file exists on disk
620
func exists(path string) (bool, error) {
621
_, err := os.Stat(path)
625
if os.IsNotExist(err) {
631
func checkDeadline(deadline, end time.Time, t *testing.T) {
632
if end.Before(deadline) {
633
t.Fatalf("Ended %v before deadline", deadline.Sub(end))
635
diff := end.Sub(deadline)
637
// we need a huge fudge factor here because Windows has really poor
638
// resolution for timeouts, and in practice, the timeout can be 400ms or
639
// more after the expected timeout.
640
if diff > 500*time.Millisecond {
641
t.Fatalf("Ended significantly (%v) after deadline", diff)