//go:build windows package winpwn import ( "fmt" "testing" "time" ) // TestNamedPipeServerClient exercises ServePipe/DialPipe end to end: a // goroutine plays the server (CreateNamedPipe + ConnectNamedPipe, both // overlapped), the test goroutine plays the client (CreateFile), and both // sides talk over the resulting *Tube exactly like any other transport. func TestNamedPipeServerClient(t *testing.T) { pipeName := fmt.Sprintf("winpwn_test_%d", time.Now().UnixNano()) serverErr := make(chan error, 1) serverMsg := make(chan []byte, 1) go func() { tube, err := ServePipe(pipeName) if err != nil { serverErr <- err return } defer tube.Close() got, err := tube.RecvLine() if err != nil { serverErr <- err return } serverMsg <- got if err := tube.SendLine([]byte("pong")); err != nil { serverErr <- err return } serverErr <- nil }() // ServePipe's CreateNamedPipe call may not have run yet; retry the // dial briefly rather than racing it with a fixed sleep. var client *Tube var err error for i := 0; i < 100; i++ { client, err = DialPipe(pipeName) if err == nil { break } time.Sleep(20 * time.Millisecond) } if err != nil { t.Fatalf("DialPipe: %v", err) } defer client.Close() if err := client.SendLine([]byte("ping")); err != nil { t.Fatalf("client SendLine: %v", err) } select { case got := <-serverMsg: if string(got) != "ping\n" { t.Errorf("server received %q, want %q", got, "ping\n") } case err := <-serverErr: t.Fatalf("server error: %v", err) case <-time.After(5 * time.Second): t.Fatal("timed out waiting for the server to receive the client's message") } got, err := client.RecvLine() if err != nil { t.Fatalf("client RecvLine: %v", err) } if string(got) != "pong\n" { t.Errorf("client received %q, want %q", got, "pong\n") } if err := <-serverErr; err != nil { t.Fatalf("server goroutine error: %v", err) } }