table testing with go routines only really works well when standard

assertions are ued. With suite.Required() there are issues in getting
the test results correct.

Instead, use standard assertions and use suite.T().Failed() to do
early returns.
This commit is contained in:
Erik Brakkee 2024-08-20 00:52:57 +02:00
parent 3867b0432d
commit 45595a34aa
3 changed files with 63 additions and 29 deletions

View File

@ -13,7 +13,7 @@ generate:
vet: fmt vet: fmt
go vet ./... go vet ./...
test: test: build
go test -count=1 -v ./... go test -count=1 -v ./...
build: generate vet build: generate vet

View File

@ -2,6 +2,7 @@ package iowrappers
import ( import (
"context" "context"
"errors"
"io" "io"
"log" "log"
) )
@ -28,17 +29,16 @@ func NewChannelReadWriter(ctx context.Context, receiver <-chan []byte,
func (rw *ChannelReadWriter) Read(p []byte) (n int, err error) { func (rw *ChannelReadWriter) Read(p []byte) (n int, err error) {
nread := copy(p, rw.readBuf) nread := copy(p, rw.readBuf)
if nread > 0 { if nread > 0 {
log.Printf("Read %v bytes", nread)
rw.readBuf = rw.readBuf[nread:] rw.readBuf = rw.readBuf[nread:]
return nread, nil return nread, nil
} }
select { select {
case <-rw.ctx.Done(): case <-rw.ctx.Done():
log.Println("Context was canceled")
return 0, io.EOF return 0, io.EOF
case data, ok := <-rw.receiver: case data, ok := <-rw.receiver:
if !ok { if !ok {
log.Println("Channel closed")
return 0, io.EOF return 0, io.EOF
} }
nread = copy(p, data) nread = copy(p, data)
@ -48,13 +48,18 @@ func (rw *ChannelReadWriter) Read(p []byte) (n int, err error) {
} }
func (rw *ChannelReadWriter) Write(p []byte) (n int, err error) { func (rw *ChannelReadWriter) Write(p []byte) (n int, err error) {
if rw.closed {
return 0, errors.New("Write on closed channel")
}
select { select {
case <-rw.ctx.Done(): case <-rw.ctx.Done():
rw.Close()
return 0, io.EOF return 0, io.EOF
case rw.sender <- p: case rw.sender <- p:
} }
return len(p), nil return len(p), nil
} }
func (rw *ChannelReadWriter) Close() error { func (rw *ChannelReadWriter) Close() error {
if !rw.closed { if !rw.closed {
close(rw.sender) close(rw.sender)

View File

@ -35,7 +35,6 @@ func (suite *ChannelReadWriterTestSuite) createChannel() {
timeoutCancelFunc() timeoutCancelFunc()
cancelFunc() cancelFunc()
} }
suite.cancelFunc = cancelFunc
suite.conn = NewChannelReadWriter(ctx, toChannel, fromChannel) suite.conn = NewChannelReadWriter(ctx, toChannel, fromChannel)
} }
@ -49,47 +48,77 @@ func (suite *ChannelReadWriterTestSuite) TearDownTest() {
type TestFunc func() any type TestFunc func() any
func runAndWait(functions ...TestFunc) []any { func (suite *ChannelReadWriterTestSuite) runAndWait(functions ...TestFunc) []any {
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(len(functions)) wg.Add(len(functions))
res := make([]any, len(functions)) res := make([]any, len(functions))
for i, function := range functions { for i, function := range functions {
go func() { go func() {
defer func() {
wg.Done()
}()
res[i] = function() res[i] = function()
wg.Done()
}() }()
} }
wg.Wait() wg.Wait()
return res return res
} }
func (suite *ChannelReadWriterTestSuite) Test_SlicesLargeEnough() { func (suite *ChannelReadWriterTestSuite) Test_SuccessfulCommunication() {
requires := suite.Require() tests := []struct {
data := []byte("hello") name string
data string
runAndWait( chunkSizes []int
func() any { chunks []string
suite.toChannel <- data }{
log.Println("data sent") {
return nil name: "buffer_large_enough",
data: "hello",
chunkSizes: []int{10},
chunks: []string{"hello"},
}, },
func() any { {
buf := make([]byte, len(data)*2) name: "two_reads_required",
n, err := suite.conn.Read(buf) data: "hello",
requires.Nil(err) chunkSizes: []int{3, 10},
requires.Equal(n, len(data)) chunks: []string{"hel", "lo"},
requires.Equal(data, buf[:n])
return nil
}, },
) {
} name: "many_reads_required",
data: "hello",
chunkSizes: []int{1, 1, 1, 1, 1},
chunks: []string{"h", "e", "l", "l", "o"},
},
}
func (suite *ChannelReadWriterTestSuite) Test_SliceTooSmallFullReadInTwoParts() { for _, test := range tests {
suite.FailNow("todo") suite.Run(test.name, func() {
} suite.runAndWait(
func() any {
select {
case <-suite.ctx.Done():
suite.FailNow("deadline reached")
log.Println("Write deadline exceeded")
case suite.toChannel <- []byte(test.data):
}
return nil
},
func() any {
remainder := test.data
for i, chunkSize := range test.chunkSizes {
buf := make([]byte, chunkSize)
n, err := suite.conn.Read(buf)
suite.Nil(err)
suite.Equal(n, len(test.chunks[i]))
suite.Equal([]byte(remainder[:n]), buf[:n])
remainder = remainder[n:]
}
return nil
},
)
})
}
func (suite *ChannelReadWriterTestSuite) Test_SliceTooSmallFullREadInManyParts() {
suite.FailNow("todo")
} }
func (suite *ChannelReadWriterTestSuite) Test_Close() { func (suite *ChannelReadWriterTestSuite) Test_Close() {