152 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package iowrappers
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"github.com/stretchr/testify/suite"
 | |
| 	"log"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| type ChannelReadWriterTestSuite struct {
 | |
| 	suite.Suite
 | |
| 
 | |
| 	ctx         context.Context
 | |
| 	cancelFunc  context.CancelFunc
 | |
| 	toChannel   chan<- []byte
 | |
| 	fromChannel <-chan []byte
 | |
| 	conn        *ChannelReadWriter
 | |
| }
 | |
| 
 | |
| func TestChannelReadWriterSuite(t *testing.T) {
 | |
| 	suite.Run(t, &ChannelReadWriterTestSuite{})
 | |
| }
 | |
| 
 | |
| func (suite *ChannelReadWriterTestSuite) createChannel() {
 | |
| 	toChannel := make(chan []byte)
 | |
| 	fromChannel := make(chan []byte)
 | |
| 	suite.toChannel = toChannel
 | |
| 	suite.fromChannel = fromChannel
 | |
| 	ctx, cancelFunc := context.WithCancel(context.Background())
 | |
| 	ctx, timeoutCancelFunc := context.WithTimeout(ctx, 10*time.Second)
 | |
| 	suite.ctx = ctx
 | |
| 	suite.cancelFunc = func() {
 | |
| 		timeoutCancelFunc()
 | |
| 		cancelFunc()
 | |
| 	}
 | |
| 	suite.conn = NewChannelReadWriter(ctx, toChannel, fromChannel)
 | |
| }
 | |
| 
 | |
| func (suite *ChannelReadWriterTestSuite) SetupTest() {
 | |
| 	suite.createChannel()
 | |
| }
 | |
| 
 | |
| func (suite *ChannelReadWriterTestSuite) TearDownTest() {
 | |
| 	suite.cancelFunc()
 | |
| }
 | |
| 
 | |
| type TestFunc func() any
 | |
| 
 | |
| func (suite *ChannelReadWriterTestSuite) runAndWait(functions ...TestFunc) []any {
 | |
| 	wg := sync.WaitGroup{}
 | |
| 	wg.Add(len(functions))
 | |
| 	res := make([]any, len(functions))
 | |
| 	for i, function := range functions {
 | |
| 		go func() {
 | |
| 			defer func() {
 | |
| 				wg.Done()
 | |
| 			}()
 | |
| 			res[i] = function()
 | |
| 		}()
 | |
| 	}
 | |
| 	wg.Wait()
 | |
| 	return res
 | |
| }
 | |
| 
 | |
| func (suite *ChannelReadWriterTestSuite) Test_SuccessfulCommunication() {
 | |
| 	tests := []struct {
 | |
| 		name       string
 | |
| 		data       []string
 | |
| 		chunkSizes []int
 | |
| 		chunks     []string
 | |
| 	}{
 | |
| 		{
 | |
| 			name:       "buffer_large_enough",
 | |
| 			data:       []string{"hello"},
 | |
| 			chunkSizes: []int{10},
 | |
| 			chunks:     []string{"hello"},
 | |
| 		},
 | |
| 		{
 | |
| 			name:       "two_reads_required",
 | |
| 			data:       []string{"hello"},
 | |
| 			chunkSizes: []int{3, 10},
 | |
| 			chunks:     []string{"hel", "lo"},
 | |
| 		},
 | |
| 		{
 | |
| 			name:       "many_reads_required",
 | |
| 			data:       []string{"hello"},
 | |
| 			chunkSizes: []int{1, 1, 1, 1, 1},
 | |
| 			chunks:     []string{"h", "e", "l", "l", "o"},
 | |
| 		},
 | |
| 		{
 | |
| 			name:       "buffer_large_enough_multiple_writes",
 | |
| 			data:       []string{"hel", "lo"},
 | |
| 			chunkSizes: []int{3, 2},
 | |
| 			chunks:     []string{"hel", "lo"},
 | |
| 		},
 | |
| 		{ // NOTE: no intelligence in the reader to fill up the read buffer when it is not full
 | |
| 			// therefore, the second read will have only 1 char since the first channel read returned
 | |
| 			// 3 of which 2 where returned in the first read call to the ChannelReadWriter.
 | |
| 			name:       "buffer_too_small_multiple_writes",
 | |
| 			data:       []string{"hel", "lo"},
 | |
| 			chunkSizes: []int{2, 2, 2},
 | |
| 			chunks:     []string{"he", "l", "lo"},
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, test := range tests {
 | |
| 		suite.Run(test.name, func() {
 | |
| 			suite.runAndWait(
 | |
| 				func() any {
 | |
| 					for _, d := range test.data {
 | |
| 						select {
 | |
| 						case <-suite.ctx.Done():
 | |
| 							suite.FailNow("deadline reached")
 | |
| 							log.Println("Write deadline exceeded")
 | |
| 						case suite.toChannel <- []byte(d):
 | |
| 						}
 | |
| 					}
 | |
| 					return nil
 | |
| 				},
 | |
| 				func() any {
 | |
| 					remainder := strings.Join(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_Close() {
 | |
| 	suite.FailNow("todo")
 | |
| }
 | |
| 
 | |
| func (suite *ChannelReadWriterTestSuite) Test_CloseTwice() {
 | |
| 	suite.FailNow("todo")
 | |
| }
 | |
| 
 | |
| func (suite *ChannelReadWriterTestSuite) Test_ContextCanceled() {
 | |
| 	suite.FailNow("todo")
 | |
| }
 |