Files
quic-go/integrationtests/self/deadline_test.go
Marten Seemann 691086db7f migrate integration tests away from Ginkgo (#4736)
* use require in benchmark tests

* translate the QLOGDIR test

* translate handshake tests

* translate the handshake RTT tests

* translate the early data test

* translate the MTU tests

* translate the key update test

* translate the stateless reset tests

* translate the packetization test

* translate the close test

* translate the resumption test

* translate the tracer test

* translate the connection ID length test

* translate the RTT tests

* translate the multiplexing tests

* translate the drop tests

* translate the handshake drop tests

* translate the 0-RTT tests

* translate the hotswap test

* translate the stream test

* translate the unidirectional stream test

* translate the timeout tests

* translate the MITM test

* rewrite the datagram tests

* translate the cancellation tests

* translate the deadline tests

* translate the test helpers
2024-12-16 23:43:59 +08:00

241 lines
5.7 KiB
Go

package self_test
import (
"bytes"
"context"
"fmt"
"io"
"net"
"testing"
"time"
"github.com/quic-go/quic-go"
"github.com/stretchr/testify/require"
)
func setupDeadlineTest(t *testing.T) (serverStr, clientStr quic.Stream) {
t.Helper()
server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil))
require.NoError(t, err)
t.Cleanup(func() { server.Close() })
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
conn, err := quic.DialAddr(
ctx,
fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port),
getTLSClientConfig(),
getQuicConfig(nil),
)
require.NoError(t, err)
t.Cleanup(func() { conn.CloseWithError(0, "") })
clientStr, err = conn.OpenStream()
require.NoError(t, err)
_, err = clientStr.Write([]byte{0}) // need to write one byte so the server learns about the stream
require.NoError(t, err)
serverConn, err := server.Accept(ctx)
require.NoError(t, err)
t.Cleanup(func() { serverConn.CloseWithError(0, "") })
serverStr, err = serverConn.AcceptStream(ctx)
require.NoError(t, err)
_, err = serverStr.Read([]byte{0})
require.NoError(t, err)
return serverStr, clientStr
}
func TestReadDeadlineSync(t *testing.T) {
serverStr, clientStr := setupDeadlineTest(t)
const timeout = time.Millisecond
errChan := make(chan error, 1)
go func() {
_, err := serverStr.Write(PRDataLong)
errChan <- err
}()
var bytesRead int
var timeoutCounter int
buf := make([]byte, 1<<10)
data := make([]byte, len(PRDataLong))
clientStr.SetReadDeadline(time.Now().Add(timeout))
for bytesRead < len(PRDataLong) {
n, err := clientStr.Read(buf)
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
timeoutCounter++
clientStr.SetReadDeadline(time.Now().Add(timeout))
} else {
require.NoError(t, err)
}
copy(data[bytesRead:], buf[:n])
bytesRead += n
}
require.Equal(t, PRDataLong, data)
// make sure the test actually worked and Read actually ran into the deadline a few times
t.Logf("ran into deadline %d times", timeoutCounter)
require.GreaterOrEqual(t, timeoutCounter, 10)
select {
case err := <-errChan:
require.NoError(t, err)
case <-time.After(time.Second):
t.Fatal("timeout")
}
}
func TestReadDeadlineAsync(t *testing.T) {
serverStr, clientStr := setupDeadlineTest(t)
const timeout = time.Millisecond
errChan := make(chan error, 1)
go func() {
_, err := serverStr.Write(PRDataLong)
errChan <- err
}()
var bytesRead int
var timeoutCounter int
buf := make([]byte, 1<<10)
data := make([]byte, len(PRDataLong))
received := make(chan struct{})
go func() {
for {
select {
case <-received:
return
default:
time.Sleep(timeout)
}
clientStr.SetReadDeadline(time.Now().Add(timeout))
}
}()
for bytesRead < len(PRDataLong) {
n, err := clientStr.Read(buf)
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
timeoutCounter++
} else {
require.NoError(t, err)
}
copy(data[bytesRead:], buf[:n])
bytesRead += n
}
require.Equal(t, PRDataLong, data)
close(received)
// make sure the test actually worked and Read actually ran into the deadline a few times
t.Logf("ran into deadline %d times", timeoutCounter)
require.GreaterOrEqual(t, timeoutCounter, 10)
select {
case err := <-errChan:
require.NoError(t, err)
case <-time.After(time.Second):
t.Fatal("timeout")
}
}
func TestWriteDeadlineSync(t *testing.T) {
serverStr, clientStr := setupDeadlineTest(t)
const timeout = time.Millisecond
errChan := make(chan error, 1)
go func() {
defer close(errChan)
data, err := io.ReadAll(serverStr)
if err != nil {
errChan <- err
}
if !bytes.Equal(PRDataLong, data) {
errChan <- fmt.Errorf("data mismatch")
}
}()
var bytesWritten int
var timeoutCounter int
clientStr.SetWriteDeadline(time.Now().Add(timeout))
for bytesWritten < len(PRDataLong) {
n, err := clientStr.Write(PRDataLong[bytesWritten:])
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
timeoutCounter++
clientStr.SetWriteDeadline(time.Now().Add(timeout))
} else {
require.NoError(t, err)
}
bytesWritten += n
}
clientStr.Close()
// make sure the test actually worked and Write actually ran into the deadline a few times
t.Logf("ran into deadline %d times", timeoutCounter)
require.GreaterOrEqual(t, timeoutCounter, 10)
select {
case err := <-errChan:
require.NoError(t, err)
case <-time.After(time.Second):
t.Fatal("timeout")
}
}
func TestWriteDeadlineAsync(t *testing.T) {
serverStr, clientStr := setupDeadlineTest(t)
const timeout = time.Millisecond
errChan := make(chan error, 1)
go func() {
defer close(errChan)
data, err := io.ReadAll(serverStr)
if err != nil {
errChan <- err
}
if !bytes.Equal(PRDataLong, data) {
errChan <- fmt.Errorf("data mismatch")
}
}()
clientStr.SetWriteDeadline(time.Now().Add(timeout))
readDone := make(chan struct{})
deadlineDone := make(chan struct{})
go func() {
defer close(deadlineDone)
for {
select {
case <-readDone:
return
default:
time.Sleep(timeout)
}
clientStr.SetWriteDeadline(time.Now().Add(timeout))
}
}()
var bytesWritten int
var timeoutCounter int
clientStr.SetWriteDeadline(time.Now().Add(timeout))
for bytesWritten < len(PRDataLong) {
n, err := clientStr.Write(PRDataLong[bytesWritten:])
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
timeoutCounter++
} else {
require.NoError(t, err)
}
bytesWritten += n
}
clientStr.Close()
close(readDone)
// make sure the test actually worked and Write actually ran into the deadline a few times
t.Logf("ran into deadline %d times", timeoutCounter)
require.GreaterOrEqual(t, timeoutCounter, 10)
select {
case err := <-errChan:
require.NoError(t, err)
case <-time.After(time.Second):
t.Fatal("timeout")
}
}