package testsupport import ( "context" "github.com/stretchr/testify/suite" "log" "net/http" "strings" "testing" "time" ) type ChannelReadWriterTestSuite struct { suite.Suite pprofServer *http.Server ctx context.Context cancelFunc context.CancelFunc toChannel chan<- []byte fromChannel <-chan []byte conn *ChannelReadWriter } func TestChannelReadWriterSuite(t *testing.T) { suite.Run(t, &ChannelReadWriterTestSuite{}) } func (s *ChannelReadWriterTestSuite) createChannel() { toChannel := make(chan []byte) fromChannel := make(chan []byte) s.toChannel = toChannel s.fromChannel = fromChannel ctx, cancelFunc := context.WithCancel(context.Background()) ctx, timeoutCancelFunc := context.WithTimeout(ctx, 10*time.Second) s.ctx = ctx s.cancelFunc = func() { timeoutCancelFunc() cancelFunc() } s.conn = NewChannelReadWriter(ctx, toChannel, fromChannel) } func (s *ChannelReadWriterTestSuite) SetupSuite() { s.pprofServer = startPprof("") } func (s *ChannelReadWriterTestSuite) TearDownSuite() { stopPprof(s.ctx, s.pprofServer) } func (s *ChannelReadWriterTestSuite) SetupTest() { s.createChannel() } func (s *ChannelReadWriterTestSuite) TearDownTest() { s.cancelFunc() } func (s *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 { s.Run(test.name, s.runSuccessfulCommunicationTest(test)) } } func (s *ChannelReadWriterTestSuite) runSuccessfulCommunicationTest(test struct { name string data []string chunkSizes []int chunks []string }) func() { return func() { runAndWait( &s.Suite, func() any { for _, d := range test.data { select { case <-s.ctx.Done(): s.FailNow("deadline reached") log.Println("Write deadline exceeded") case s.toChannel <- []byte(d): } } return nil }, func() any { remainder := strings.Join(test.data, "") for i, chunkSize := range test.chunkSizes { buf := make([]byte, chunkSize) n, err := s.conn.Read(buf) s.Nil(err) s.Equal(n, len(test.chunks[i])) s.Equal([]byte(remainder[:n]), buf[:n]) remainder = remainder[n:] } return nil }, ) } } func (s *ChannelReadWriterTestSuite) Test_Close() { s.FailNow("todo") } func (s *ChannelReadWriterTestSuite) Test_CloseTwice() { s.FailNow("todo") } func (s *ChannelReadWriterTestSuite) Test_ContextCanceled() { s.FailNow("todo") }