From d5a6d70bc4c86dc2c8b7158aba2a2b63dd99e95d Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Tue, 20 Aug 2024 09:01:46 +0200 Subject: [PATCH] restructuring test code by introducing a testsupport package Making it easy 6to start a porof server in tests. --- cmd/converge/converge.go | 3 +- pkg/comms/agentserver_test.go | 4 +- {cmd/converge => pkg/support/pprof}/pprof.go | 4 +- .../iowrappers => testsupport}/bitpipe.go | 8 +- .../channelreadwritecloser.go | 2 +- .../channelreadwriter_test.go | 125 +++++++++--------- .../inmemoryconnection.go | 2 +- pkg/testsupport/utils.go | 57 ++++++++ 8 files changed, 133 insertions(+), 72 deletions(-) rename {cmd/converge => pkg/support/pprof}/pprof.go (90%) rename pkg/{support/iowrappers => testsupport}/bitpipe.go (71%) rename pkg/{support/iowrappers => testsupport}/channelreadwritecloser.go (98%) rename pkg/{support/iowrappers => testsupport}/channelreadwriter_test.go (51%) rename pkg/{support/iowrappers => testsupport}/inmemoryconnection.go (97%) create mode 100644 pkg/testsupport/utils.go diff --git a/cmd/converge/converge.go b/cmd/converge/converge.go index f23d921..8b79f59 100644 --- a/cmd/converge/converge.go +++ b/cmd/converge/converge.go @@ -7,6 +7,7 @@ import ( "git.wamblee.org/converge/pkg/server/matchmaker" "git.wamblee.org/converge/pkg/server/prometheus" "git.wamblee.org/converge/pkg/server/ui" + pprof2 "git.wamblee.org/converge/pkg/support/pprof" "git.wamblee.org/converge/pkg/support/websocketutil" "log" "net" @@ -143,7 +144,7 @@ func main() { context := HttpContext{mux: http.NewServeMux(), path: contextpath} if pprof { - registerPprof(context.mux, "/debug/pprof") + pprof2.RegisterPprof(context.mux, "/debug/pprof") } setupWebUI(context, registrationService, clientService, sessionService, staticdir, downloaddir) // Catch all for the web UI diff --git a/pkg/comms/agentserver_test.go b/pkg/comms/agentserver_test.go index 83b72f0..3fbc8b2 100644 --- a/pkg/comms/agentserver_test.go +++ b/pkg/comms/agentserver_test.go @@ -2,7 +2,7 @@ package comms import ( "context" - "git.wamblee.org/converge/pkg/support/iowrappers" + "git.wamblee.org/converge/pkg/testsupport" "github.com/stretchr/testify/suite" "log" "sync" @@ -24,7 +24,7 @@ func TestAgentServerTestSuite(t *testing.T) { } func (suite *AgentServerTestSuite) TestNewCommChannel() { - bitpipe := iowrappers.NewInmemoryConnection(context.Background(), "inmemory") + bitpipe := testsupport.NewInmemoryConnection(context.Background(), "inmemory") agentConnection := bitpipe.Front() serverConnection := bitpipe.Back() requires := suite.Require() diff --git a/cmd/converge/pprof.go b/pkg/support/pprof/pprof.go similarity index 90% rename from cmd/converge/pprof.go rename to pkg/support/pprof/pprof.go index 9ae45c9..db9014e 100644 --- a/cmd/converge/pprof.go +++ b/pkg/support/pprof/pprof.go @@ -1,11 +1,11 @@ -package main +package pprof import ( "net/http" "net/http/pprof" ) -func registerPprof(mux *http.ServeMux, contextPath string) { +func RegisterPprof(mux *http.ServeMux, contextPath string) { mux.HandleFunc(contextPath+"/", pprof.Index) mux.HandleFunc(contextPath+"/cmdline", pprof.Cmdline) mux.HandleFunc(contextPath+"/profile", pprof.Profile) diff --git a/pkg/support/iowrappers/bitpipe.go b/pkg/testsupport/bitpipe.go similarity index 71% rename from pkg/support/iowrappers/bitpipe.go rename to pkg/testsupport/bitpipe.go index da38201..0fd85f7 100644 --- a/pkg/support/iowrappers/bitpipe.go +++ b/pkg/testsupport/bitpipe.go @@ -1,4 +1,6 @@ -package iowrappers +package testsupport + +import "git.wamblee.org/converge/pkg/support/iowrappers" type dummyRemoteAddr string @@ -14,6 +16,6 @@ func (r dummyRemoteAddr) String() string { // code under test reads the other side. type BitPipe interface { - Front() ReadWriteAddrCloser - Back() ReadWriteAddrCloser + Front() iowrappers.ReadWriteAddrCloser + Back() iowrappers.ReadWriteAddrCloser } diff --git a/pkg/support/iowrappers/channelreadwritecloser.go b/pkg/testsupport/channelreadwritecloser.go similarity index 98% rename from pkg/support/iowrappers/channelreadwritecloser.go rename to pkg/testsupport/channelreadwritecloser.go index 11b312f..c6d0079 100644 --- a/pkg/support/iowrappers/channelreadwritecloser.go +++ b/pkg/testsupport/channelreadwritecloser.go @@ -1,4 +1,4 @@ -package iowrappers +package testsupport import ( "context" diff --git a/pkg/support/iowrappers/channelreadwriter_test.go b/pkg/testsupport/channelreadwriter_test.go similarity index 51% rename from pkg/support/iowrappers/channelreadwriter_test.go rename to pkg/testsupport/channelreadwriter_test.go index cc68f88..c3d8a32 100644 --- a/pkg/support/iowrappers/channelreadwriter_test.go +++ b/pkg/testsupport/channelreadwriter_test.go @@ -1,11 +1,11 @@ -package iowrappers +package testsupport import ( "context" "github.com/stretchr/testify/suite" "log" + "net/http" "strings" - "sync" "testing" "time" ) @@ -13,6 +13,7 @@ import ( type ChannelReadWriterTestSuite struct { suite.Suite + pprofServer *http.Server ctx context.Context cancelFunc context.CancelFunc toChannel chan<- []byte @@ -24,48 +25,38 @@ func TestChannelReadWriterSuite(t *testing.T) { suite.Run(t, &ChannelReadWriterTestSuite{}) } -func (suite *ChannelReadWriterTestSuite) createChannel() { +func (s *ChannelReadWriterTestSuite) createChannel() { toChannel := make(chan []byte) fromChannel := make(chan []byte) - suite.toChannel = toChannel - suite.fromChannel = fromChannel + s.toChannel = toChannel + s.fromChannel = fromChannel ctx, cancelFunc := context.WithCancel(context.Background()) ctx, timeoutCancelFunc := context.WithTimeout(ctx, 10*time.Second) - suite.ctx = ctx - suite.cancelFunc = func() { + s.ctx = ctx + s.cancelFunc = func() { timeoutCancelFunc() cancelFunc() } - suite.conn = NewChannelReadWriter(ctx, toChannel, fromChannel) + s.conn = NewChannelReadWriter(ctx, toChannel, fromChannel) } -func (suite *ChannelReadWriterTestSuite) SetupTest() { - suite.createChannel() +func (s *ChannelReadWriterTestSuite) SetupSuite() { + s.pprofServer = startPprof("") } -func (suite *ChannelReadWriterTestSuite) TearDownTest() { - suite.cancelFunc() +func (s *ChannelReadWriterTestSuite) TearDownSuite() { + stopPprof(s.ctx, s.pprofServer) } -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 (s *ChannelReadWriterTestSuite) SetupTest() { + s.createChannel() } -func (suite *ChannelReadWriterTestSuite) Test_SuccessfulCommunication() { +func (s *ChannelReadWriterTestSuite) TearDownTest() { + s.cancelFunc() +} + +func (s *ChannelReadWriterTestSuite) Test_SuccessfulCommunication() { tests := []struct { name string data []string @@ -107,45 +98,55 @@ func (suite *ChannelReadWriterTestSuite) Test_SuccessfulCommunication() { } 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 - }, - ) - }) + s.Run(test.name, s.runSuccessfulCommunicationTest(test)) } } -func (suite *ChannelReadWriterTestSuite) Test_Close() { - suite.FailNow("todo") +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 (suite *ChannelReadWriterTestSuite) Test_CloseTwice() { - suite.FailNow("todo") +func (s *ChannelReadWriterTestSuite) Test_Close() { + s.FailNow("todo") } -func (suite *ChannelReadWriterTestSuite) Test_ContextCanceled() { - suite.FailNow("todo") +func (s *ChannelReadWriterTestSuite) Test_CloseTwice() { + s.FailNow("todo") +} + +func (s *ChannelReadWriterTestSuite) Test_ContextCanceled() { + s.FailNow("todo") } diff --git a/pkg/support/iowrappers/inmemoryconnection.go b/pkg/testsupport/inmemoryconnection.go similarity index 97% rename from pkg/support/iowrappers/inmemoryconnection.go rename to pkg/testsupport/inmemoryconnection.go index 011f70b..502d819 100644 --- a/pkg/support/iowrappers/inmemoryconnection.go +++ b/pkg/testsupport/inmemoryconnection.go @@ -1,4 +1,4 @@ -package iowrappers +package testsupport import "context" diff --git a/pkg/testsupport/utils.go b/pkg/testsupport/utils.go new file mode 100644 index 0000000..b3eccb0 --- /dev/null +++ b/pkg/testsupport/utils.go @@ -0,0 +1,57 @@ +package testsupport + +import ( + "context" + "git.wamblee.org/converge/pkg/support/pprof" + "github.com/stretchr/testify/suite" + "log" + "net/http" + _ "runtime/pprof" + "sync" +) + +type TestFunction func() any + +func runAndWait(suite *suite.Suite, functions ...TestFunction) []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 startPprof(port string) *http.Server { + if port == "" { + port = ":9000" + } + mux := http.NewServeMux() + pprof.RegisterPprof(mux, "/debug/pprof") + srv := http.Server{ + Addr: port, + Handler: mux, + } + go func() { + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + log.Fatalf("Could not start pprof listener: %v", err) + } + log.Println("Test pprof server started") + }() + return &srv +} + +func stopPprof(ctx context.Context, server *http.Server) { + err := server.Shutdown(ctx) + if err != nil { + log.Println("Error shutting down test pprof server") + return + } + log.Println("Test pprof server stopped") +}