| /* | |
| * | |
| * Copyright 2017 gRPC authors. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| * | |
| */ | |
| package bufconn | |
| import ( | |
| "fmt" | |
| "io" | |
| "net" | |
| "reflect" | |
| "testing" | |
| "time" | |
| "google.golang.org/grpc/internal/grpctest" | |
| ) | |
| type s struct { | |
| grpctest.Tester | |
| } | |
| func Test(t *testing.T) { | |
| grpctest.RunSubTests(t, s{}) | |
| } | |
| func testRW(r io.Reader, w io.Writer) error { | |
| for i := 0; i < 20; i++ { | |
| d := make([]byte, i) | |
| for j := 0; j < i; j++ { | |
| d[j] = byte(i - j) | |
| } | |
| var rn int | |
| var rerr error | |
| b := make([]byte, i) | |
| done := make(chan struct{}) | |
| go func() { | |
| for rn < len(b) && rerr == nil { | |
| var x int | |
| x, rerr = r.Read(b[rn:]) | |
| rn += x | |
| } | |
| close(done) | |
| }() | |
| wn, werr := w.Write(d) | |
| if wn != i || werr != nil { | |
| return fmt.Errorf("%v: w.Write(%v) = %v, %v; want %v, nil", i, d, wn, werr, i) | |
| } | |
| select { | |
| case <-done: | |
| case <-time.After(500 * time.Millisecond): | |
| return fmt.Errorf("%v: r.Read never returned", i) | |
| } | |
| if rn != i || rerr != nil { | |
| return fmt.Errorf("%v: r.Read = %v, %v; want %v, nil", i, rn, rerr, i) | |
| } | |
| if !reflect.DeepEqual(b, d) { | |
| return fmt.Errorf("%v: r.Read read %v; want %v", i, b, d) | |
| } | |
| } | |
| return nil | |
| } | |
| func (s) TestPipe(t *testing.T) { | |
| p := newPipe(10) | |
| if err := testRW(p, p); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| func (s) TestPipeClose(t *testing.T) { | |
| p := newPipe(10) | |
| p.Close() | |
| if _, err := p.Write(nil); err != io.ErrClosedPipe { | |
| t.Fatalf("p.Write = _, %v; want _, %v", err, io.ErrClosedPipe) | |
| } | |
| if _, err := p.Read(nil); err != io.ErrClosedPipe { | |
| t.Fatalf("p.Read = _, %v; want _, %v", err, io.ErrClosedPipe) | |
| } | |
| } | |
| func (s) TestConn(t *testing.T) { | |
| p1, p2 := newPipe(10), newPipe(10) | |
| c1, c2 := &conn{p1, p2}, &conn{p2, p1} | |
| if err := testRW(c1, c2); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := testRW(c2, c1); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| func (s) TestConnCloseWithData(t *testing.T) { | |
| lis := Listen(7) | |
| errChan := make(chan error, 1) | |
| var lisConn net.Conn | |
| go func() { | |
| var err error | |
| if lisConn, err = lis.Accept(); err != nil { | |
| errChan <- err | |
| } | |
| close(errChan) | |
| }() | |
| dialConn, err := lis.Dial() | |
| if err != nil { | |
| t.Fatalf("Dial error: %v", err) | |
| } | |
| if err := <-errChan; err != nil { | |
| t.Fatalf("Listen error: %v", err) | |
| } | |
| // Write some data on both sides of the connection. | |
| n, err := dialConn.Write([]byte("hello")) | |
| if n != 5 || err != nil { | |
| t.Fatalf("dialConn.Write([]byte{\"hello\"}) = %v, %v; want 5, <nil>", n, err) | |
| } | |
| n, err = lisConn.Write([]byte("hello")) | |
| if n != 5 || err != nil { | |
| t.Fatalf("lisConn.Write([]byte{\"hello\"}) = %v, %v; want 5, <nil>", n, err) | |
| } | |
| // Close dial-side; writes from either side should fail. | |
| dialConn.Close() | |
| if _, err := lisConn.Write([]byte("hello")); err != io.ErrClosedPipe { | |
| t.Fatalf("lisConn.Write() = _, <nil>; want _, <non-nil>") | |
| } | |
| if _, err := dialConn.Write([]byte("hello")); err != io.ErrClosedPipe { | |
| t.Fatalf("dialConn.Write() = _, <nil>; want _, <non-nil>") | |
| } | |
| // Read from both sides; reads on lisConn should work, but dialConn should | |
| // fail. | |
| buf := make([]byte, 6) | |
| if _, err := dialConn.Read(buf); err != io.ErrClosedPipe { | |
| t.Fatalf("dialConn.Read(buf) = %v, %v; want _, io.ErrClosedPipe", n, err) | |
| } | |
| n, err = lisConn.Read(buf) | |
| if n != 5 || err != nil { | |
| t.Fatalf("lisConn.Read(buf) = %v, %v; want 5, <nil>", n, err) | |
| } | |
| } | |
| func (s) TestListener(t *testing.T) { | |
| l := Listen(7) | |
| var s net.Conn | |
| var serr error | |
| done := make(chan struct{}) | |
| go func() { | |
| s, serr = l.Accept() | |
| close(done) | |
| }() | |
| c, cerr := l.Dial() | |
| <-done | |
| if cerr != nil || serr != nil { | |
| t.Fatalf("cerr = %v, serr = %v; want nil, nil", cerr, serr) | |
| } | |
| if err := testRW(c, s); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := testRW(s, c); err != nil { | |
| t.Fatal(err) | |
| } | |
| } | |
| func (s) TestCloseWhileDialing(t *testing.T) { | |
| l := Listen(7) | |
| var c net.Conn | |
| var err error | |
| done := make(chan struct{}) | |
| go func() { | |
| c, err = l.Dial() | |
| close(done) | |
| }() | |
| l.Close() | |
| <-done | |
| if c != nil || err != errClosed { | |
| t.Fatalf("c, err = %v, %v; want nil, %v", c, err, errClosed) | |
| } | |
| } | |
| func (s) TestCloseWhileAccepting(t *testing.T) { | |
| l := Listen(7) | |
| var c net.Conn | |
| var err error | |
| done := make(chan struct{}) | |
| go func() { | |
| c, err = l.Accept() | |
| close(done) | |
| }() | |
| l.Close() | |
| <-done | |
| if c != nil || err != errClosed { | |
| t.Fatalf("c, err = %v, %v; want nil, %v", c, err, errClosed) | |
| } | |
| } | |
| func (s) TestDeadline(t *testing.T) { | |
| sig := make(chan error, 2) | |
| blockingWrite := func(conn net.Conn) { | |
| _, err := conn.Write([]byte("0123456789")) | |
| sig <- err | |
| } | |
| blockingRead := func(conn net.Conn) { | |
| _, err := conn.Read(make([]byte, 10)) | |
| sig <- err | |
| } | |
| p1, p2 := newPipe(5), newPipe(5) | |
| c1, c2 := &conn{p1, p1}, &conn{p2, p2} | |
| defer c1.Close() | |
| defer c2.Close() | |
| // Test with deadline | |
| c1.SetWriteDeadline(time.Now()) | |
| go blockingWrite(c1) | |
| select { | |
| case <-time.After(100 * time.Millisecond): | |
| t.Fatalf("Write timeout timed out, c = %v", c1) | |
| case err := <-sig: | |
| if netErr, ok := err.(net.Error); ok { | |
| if !netErr.Timeout() { | |
| t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, netErr) | |
| } | |
| } else { | |
| t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, err) | |
| } | |
| } | |
| c2.SetReadDeadline(time.Now()) | |
| go blockingRead(c2) | |
| select { | |
| case <-time.After(100 * time.Millisecond): | |
| t.Fatalf("Read timeout timed out, c = %v", c2) | |
| case err := <-sig: | |
| if netErr, ok := err.(net.Error); ok { | |
| if !netErr.Timeout() { | |
| t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, netErr) | |
| } | |
| } else { | |
| t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, err) | |
| } | |
| } | |
| // Test timing out pending reads/writes | |
| c1.SetWriteDeadline(time.Time{}) | |
| c2.SetReadDeadline(time.Time{}) | |
| go blockingWrite(c1) | |
| select { | |
| case <-time.After(100 * time.Millisecond): | |
| case err := <-sig: | |
| t.Fatalf("Write returned before timeout, err = %v", err) | |
| } | |
| c1.SetWriteDeadline(time.Now()) | |
| select { | |
| case <-time.After(100 * time.Millisecond): | |
| t.Fatalf("Write timeout timed out, c = %v", c1) | |
| case err := <-sig: | |
| if netErr, ok := err.(net.Error); ok { | |
| if !netErr.Timeout() { | |
| t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, netErr) | |
| } | |
| } else { | |
| t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, err) | |
| } | |
| } | |
| go blockingRead(c2) | |
| select { | |
| case <-time.After(100 * time.Millisecond): | |
| case err := <-sig: | |
| t.Fatalf("Read returned before timeout, err = %v", err) | |
| } | |
| c2.SetReadDeadline(time.Now()) | |
| select { | |
| case <-time.After(100 * time.Millisecond): | |
| t.Fatalf("Read timeout timed out, c = %v", c2) | |
| case err := <-sig: | |
| if netErr, ok := err.(net.Error); ok { | |
| if !netErr.Timeout() { | |
| t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, netErr) | |
| } | |
| } else { | |
| t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, err) | |
| } | |
| } | |
| // Test non-blocking read/write | |
| c1, c2 = &conn{p1, p2}, &conn{p2, p1} | |
| c1.SetWriteDeadline(time.Now().Add(10 * time.Second)) | |
| c2.SetReadDeadline(time.Now().Add(10 * time.Second)) | |
| // Not blocking here | |
| go blockingWrite(c1) | |
| go blockingRead(c2) | |
| // Read response from both routines | |
| for i := 0; i < 2; i++ { | |
| select { | |
| case <-time.After(100 * time.Millisecond): | |
| t.Fatalf("Read/Write timed out, c1 = %v, c2 = %v", c1, c2) | |
| case err := <-sig: | |
| if err != nil { | |
| t.Fatalf("Read/Write failed to complete, c1 = %v, c2 = %v, err = %v", c1, c2, err) | |
| } | |
| } | |
| } | |
| } |