From 07e7b17ae8a073feda2288cf38e8d865975e3a89 Mon Sep 17 00:00:00 2001 From: Winni Neessen Date: Mon, 23 Sep 2024 11:45:22 +0200 Subject: [PATCH] Add tests for ConnPool close and concurrency issues This commit introduces two new tests: `TestConnPool_Close` and `TestConnPool_Concurrency`. The former ensures the proper closing of connection pool resources, while the latter checks for concurrency issues by creating and closing multiple connections in parallel. --- connpool_test.go | 100 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/connpool_test.go b/connpool_test.go index d03062f..4450e8c 100644 --- a/connpool_test.go +++ b/connpool_test.go @@ -255,6 +255,106 @@ func TestPoolConn_MarkUnusable(t *testing.T) { } } +func TestConnPool_Close(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + serverPort := TestServerPortBase + 15 + featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" + go func() { + if err := simpleSMTPServer(ctx, featureSet, true, serverPort); err != nil { + t.Errorf("failed to start test server: %s", err) + return + } + }() + time.Sleep(time.Millisecond * 300) + + pool, err := newConnPool(serverPort) + if err != nil { + t.Errorf("failed to create connection pool: %s", err) + } + pool.Close() + + castPool := pool.(*connPool) + + if castPool.conns != nil { + t.Error("closing pool failed: conns channel should be nil") + } + if castPool.dialCtxFunc != nil { + t.Error("closing pool failed: dialCtxFunc should be nil") + } + if castPool.dialContext != nil { + t.Error("closing pool failed: dialContext should be nil") + } + if castPool.dialAddress != "" { + t.Error("closing pool failed: dialAddress should be empty") + } + if castPool.dialNetwork != "" { + t.Error("closing pool failed: dialNetwork should be empty") + } + + conn, err := pool.Get() + if err == nil { + t.Errorf("closing pool failed: getting new connection should return an error") + } + if conn != nil { + t.Errorf("closing pool failed: getting new connection should return a nil-connection") + } + if pool.Size() != 0 { + t.Errorf("closing pool failed: pool size should be 0, but got: %d", pool.Size()) + } +} + +func TestConnPool_Concurrency(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + serverPort := TestServerPortBase + 16 + featureSet := "250-AUTH PLAIN\r\n250-8BITMIME\r\n250-DSN\r\n250 SMTPUTF8" + go func() { + if err := simpleSMTPServer(ctx, featureSet, true, serverPort); err != nil { + t.Errorf("failed to start test server: %s", err) + return + } + }() + time.Sleep(time.Millisecond * 300) + + pool, err := newConnPool(serverPort) + if err != nil { + t.Errorf("failed to create connection pool: %s", err) + } + defer pool.Close() + pipe := make(chan net.Conn) + + getWg := sync.WaitGroup{} + closeWg := sync.WaitGroup{} + for i := 0; i < 30; i++ { + getWg.Add(1) + closeWg.Add(1) + go func() { + conn, err := pool.Get() + if err != nil { + t.Errorf("failed to get new connection from pool: %s", err) + } + pipe <- conn + getWg.Done() + }() + + go func() { + conn := <-pipe + if conn == nil { + return + } + if err = conn.Close(); err != nil { + t.Errorf("failed to close connection: %s", err) + } + closeWg.Done() + }() + getWg.Wait() + closeWg.Wait() + } +} + func newConnPool(port int) (Pool, error) { netDialer := net.Dialer{} return NewConnPool(context.Background(), 5, 30, netDialer.DialContext, "tcp",