1
0
mirror of https://github.com/golang/go synced 2024-11-22 11:54:50 -07:00
go/src/net/writev_test.go
Bryan C. Mills a815078683 net: enable most tests on wasip1 and js
To get them to pass, implement more fake syscalls.
To make those syscalls easier to reason about, replace
the use of sync.Cond with selectable channels.

Fixes #59718.
Fixes #50216.

Change-Id: I135a6656f5c48f0e5c43dc4d4bcbdb48ee5535d2
Reviewed-on: https://go-review.googlesource.com/c/go/+/526117
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Achille Roussel <achille.roussel@gmail.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2023-09-18 17:20:52 +00:00

229 lines
5.0 KiB
Go

// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package net
import (
"bytes"
"fmt"
"internal/poll"
"io"
"reflect"
"runtime"
"sync"
"testing"
)
func TestBuffers_read(t *testing.T) {
const story = "once upon a time in Gopherland ... "
buffers := Buffers{
[]byte("once "),
[]byte("upon "),
[]byte("a "),
[]byte("time "),
[]byte("in "),
[]byte("Gopherland ... "),
}
got, err := io.ReadAll(&buffers)
if err != nil {
t.Fatal(err)
}
if string(got) != story {
t.Errorf("read %q; want %q", got, story)
}
if len(buffers) != 0 {
t.Errorf("len(buffers) = %d; want 0", len(buffers))
}
}
func TestBuffers_consume(t *testing.T) {
tests := []struct {
in Buffers
consume int64
want Buffers
}{
{
in: Buffers{[]byte("foo"), []byte("bar")},
consume: 0,
want: Buffers{[]byte("foo"), []byte("bar")},
},
{
in: Buffers{[]byte("foo"), []byte("bar")},
consume: 2,
want: Buffers{[]byte("o"), []byte("bar")},
},
{
in: Buffers{[]byte("foo"), []byte("bar")},
consume: 3,
want: Buffers{[]byte("bar")},
},
{
in: Buffers{[]byte("foo"), []byte("bar")},
consume: 4,
want: Buffers{[]byte("ar")},
},
{
in: Buffers{nil, nil, nil, []byte("bar")},
consume: 1,
want: Buffers{[]byte("ar")},
},
{
in: Buffers{nil, nil, nil, []byte("foo")},
consume: 0,
want: Buffers{[]byte("foo")},
},
{
in: Buffers{nil, nil, nil},
consume: 0,
want: Buffers{},
},
}
for i, tt := range tests {
in := tt.in
in.consume(tt.consume)
if !reflect.DeepEqual(in, tt.want) {
t.Errorf("%d. after consume(%d) = %+v, want %+v", i, tt.consume, in, tt.want)
}
}
}
func TestBuffers_WriteTo(t *testing.T) {
for _, name := range []string{"WriteTo", "Copy"} {
for _, size := range []int{0, 10, 1023, 1024, 1025} {
t.Run(fmt.Sprintf("%s/%d", name, size), func(t *testing.T) {
testBuffer_writeTo(t, size, name == "Copy")
})
}
}
}
func testBuffer_writeTo(t *testing.T, chunks int, useCopy bool) {
oldHook := poll.TestHookDidWritev
defer func() { poll.TestHookDidWritev = oldHook }()
var writeLog struct {
sync.Mutex
log []int
}
poll.TestHookDidWritev = func(size int) {
writeLog.Lock()
writeLog.log = append(writeLog.log, size)
writeLog.Unlock()
}
var want bytes.Buffer
for i := 0; i < chunks; i++ {
want.WriteByte(byte(i))
}
withTCPConnPair(t, func(c *TCPConn) error {
buffers := make(Buffers, chunks)
for i := range buffers {
buffers[i] = want.Bytes()[i : i+1]
}
var n int64
var err error
if useCopy {
n, err = io.Copy(c, &buffers)
} else {
n, err = buffers.WriteTo(c)
}
if err != nil {
return err
}
if len(buffers) != 0 {
return fmt.Errorf("len(buffers) = %d; want 0", len(buffers))
}
if n != int64(want.Len()) {
return fmt.Errorf("Buffers.WriteTo returned %d; want %d", n, want.Len())
}
return nil
}, func(c *TCPConn) error {
all, err := io.ReadAll(c)
if !bytes.Equal(all, want.Bytes()) || err != nil {
return fmt.Errorf("client read %q, %v; want %q, nil", all, err, want.Bytes())
}
writeLog.Lock() // no need to unlock
var gotSum int
for _, v := range writeLog.log {
gotSum += v
}
var wantSum int
switch runtime.GOOS {
case "aix", "android", "darwin", "ios", "dragonfly", "freebsd", "illumos", "linux", "netbsd", "openbsd", "solaris":
var wantMinCalls int
wantSum = want.Len()
v := chunks
for v > 0 {
wantMinCalls++
v -= 1024
}
if len(writeLog.log) < wantMinCalls {
t.Errorf("write calls = %v < wanted min %v", len(writeLog.log), wantMinCalls)
}
case "windows":
var wantCalls int
wantSum = want.Len()
if wantSum > 0 {
wantCalls = 1 // windows will always do 1 syscall, unless sending empty buffer
}
if len(writeLog.log) != wantCalls {
t.Errorf("write calls = %v; want %v", len(writeLog.log), wantCalls)
}
}
if gotSum != wantSum {
t.Errorf("writev call sum = %v; want %v", gotSum, wantSum)
}
return nil
})
}
func TestWritevError(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skipf("skipping the test: windows does not have problem sending large chunks of data")
}
ln := newLocalListener(t, "tcp")
ch := make(chan Conn, 1)
defer func() {
ln.Close()
for c := range ch {
c.Close()
}
}()
go func() {
defer close(ch)
c, err := ln.Accept()
if err != nil {
t.Error(err)
return
}
ch <- c
}()
c1, err := Dial("tcp", ln.Addr().String())
if err != nil {
t.Fatal(err)
}
defer c1.Close()
c2 := <-ch
if c2 == nil {
t.Fatal("no server side connection")
}
c2.Close()
// 1 GB of data should be enough to notice the connection is gone.
// Just a few bytes is not enough.
// Arrange to reuse the same 1 MB buffer so that we don't allocate much.
buf := make([]byte, 1<<20)
buffers := make(Buffers, 1<<10)
for i := range buffers {
buffers[i] = buf
}
if _, err := buffers.WriteTo(c1); err == nil {
t.Fatal("Buffers.WriteTo(closed conn) succeeded, want error")
}
}