From b88e50e03ae5afca1009f64b1dc8f8b7cb515d4f Mon Sep 17 00:00:00 2001 From: Daniel Hu Date: Fri, 11 Aug 2023 09:53:49 +0800 Subject: [PATCH] Refactor tests using Ginkgo and Gomega This commit refactors all the tests in the gopool package to use the Ginkgo testing framework and the Gomega assertion library. The tests are now more readable and maintainable. The go.mod file has also been updated to include the new dependencies. Signed-off-by: Daniel Hu --- go.mod | 18 ++- go.sum | 41 ++++++ gopool.go | 6 +- gopool_benchmark_test.go | 68 ++++++++++ gopool_suite_test.go | 13 ++ gopool_test.go | 265 ++++++++++++++++----------------------- 6 files changed, 248 insertions(+), 163 deletions(-) create mode 100644 gopool_benchmark_test.go create mode 100644 gopool_suite_test.go diff --git a/go.mod b/go.mod index ab6c01e..9b4fe6b 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,20 @@ module github.com/devchat-ai/gopool go 1.20 -require github.com/daniel-hutao/spinlock v0.1.0 +require ( + github.com/daniel-hutao/spinlock v0.1.0 + github.com/onsi/ginkgo/v2 v2.11.0 + github.com/onsi/gomega v1.27.10 +) + +require ( + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + golang.org/x/net v0.12.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + golang.org/x/tools v0.9.3 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index a4ec759..688336a 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,43 @@ +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/daniel-hutao/spinlock v0.1.0 h1:qk6v2L6mJLUmxzq1eJ5xUIlCh4q0wM+26Qy/KfH5c3U= github.com/daniel-hutao/spinlock v0.1.0/go.mod h1:KkIAx91Qk/GDks3LcKXTwOJ7CS03ApkAHU4X4nrlwto= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gopool.go b/gopool.go index ecaf035..4681dea 100644 --- a/gopool.go +++ b/gopool.go @@ -202,9 +202,9 @@ func max(a, b int) int { // Running returns the number of workers that are currently working. func (p *goPool) Running() int { - p.lock.Lock() - defer p.lock.Unlock() - return len(p.workers) - len(p.workerStack) + p.lock.Lock() + defer p.lock.Unlock() + return len(p.workers) - len(p.workerStack) } // GetWorkerCount returns the number of workers in the pool. diff --git a/gopool_benchmark_test.go b/gopool_benchmark_test.go new file mode 100644 index 0000000..41c3a58 --- /dev/null +++ b/gopool_benchmark_test.go @@ -0,0 +1,68 @@ +package gopool + +import ( + "sync" + "testing" + "time" + + "github.com/daniel-hutao/spinlock" +) + +func BenchmarkGoPoolWithMutex(b *testing.B) { + var wg sync.WaitGroup + var taskNum = int(1e6) + pool := NewGoPool(1e4, WithLock(new(sync.Mutex))) + defer pool.Release() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + wg.Add(taskNum) + for num := 0; num < taskNum; num++ { + pool.AddTask(func() (interface{}, error) { + time.Sleep(10 * time.Millisecond) + wg.Done() + return nil, nil + }) + } + wg.Wait() + } + b.StopTimer() +} + +func BenchmarkGoPoolWithSpinLock(b *testing.B) { + var wg sync.WaitGroup + var taskNum = int(1e6) + pool := NewGoPool(1e4, WithLock(new(spinlock.SpinLock))) + defer pool.Release() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + wg.Add(taskNum) + for num := 0; num < taskNum; num++ { + pool.AddTask(func() (interface{}, error) { + time.Sleep(10 * time.Millisecond) + wg.Done() + return nil, nil + }) + } + wg.Wait() + } + b.StopTimer() +} + +func BenchmarkGoroutines(b *testing.B) { + var wg sync.WaitGroup + var taskNum = int(1e6) + + for i := 0; i < b.N; i++ { + wg.Add(taskNum) + for num := 0; num < taskNum; num++ { + go func() (interface{}, error) { + time.Sleep(10 * time.Millisecond) + wg.Done() + return nil, nil + }() + } + wg.Wait() + } +} diff --git a/gopool_suite_test.go b/gopool_suite_test.go new file mode 100644 index 0000000..375cad3 --- /dev/null +++ b/gopool_suite_test.go @@ -0,0 +1,13 @@ +package gopool_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestGopool(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Gopool Suite") +} diff --git a/gopool_test.go b/gopool_test.go index 36dacc9..6b9fc58 100644 --- a/gopool_test.go +++ b/gopool_test.go @@ -1,185 +1,132 @@ -package gopool +package gopool_test import ( "errors" "sync" "sync/atomic" - "testing" "time" "github.com/daniel-hutao/spinlock" + "github.com/devchat-ai/gopool" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) -func TestGoPoolWithMutex(t *testing.T) { - pool := NewGoPool(100, WithLock(new(sync.Mutex))) - defer pool.Release() - for i := 0; i < 1000; i++ { - pool.AddTask(func() (interface{}, error) { - time.Sleep(10 * time.Millisecond) - return nil, nil +var _ = Describe("Gopool", func() { + Describe("With Mutex", func() { + It("should work correctly", func() { + pool := gopool.NewGoPool(100, gopool.WithLock(new(sync.Mutex))) + defer pool.Release() + for i := 0; i < 1000; i++ { + pool.AddTask(func() (interface{}, error) { + time.Sleep(10 * time.Millisecond) + return nil, nil + }) + } + pool.Wait() }) - } - pool.Wait() -} - -func TestGoPoolWithSpinLock(t *testing.T) { - pool := NewGoPool(100, WithLock(new(spinlock.SpinLock))) - defer pool.Release() - for i := 0; i < 1000; i++ { - pool.AddTask(func() (interface{}, error) { - time.Sleep(10 * time.Millisecond) - return nil, nil + }) + + Describe("With SpinLock", func() { + It("should work correctly", func() { + pool := gopool.NewGoPool(100, gopool.WithLock(new(spinlock.SpinLock))) + defer pool.Release() + for i := 0; i < 1000; i++ { + pool.AddTask(func() (interface{}, error) { + time.Sleep(10 * time.Millisecond) + return nil, nil + }) + } + pool.Wait() }) - } - pool.Wait() -} - -func BenchmarkGoPoolWithMutex(b *testing.B) { - var wg sync.WaitGroup - var taskNum = int(1e6) - pool := NewGoPool(1e4, WithLock(new(sync.Mutex))) - defer pool.Release() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - wg.Add(taskNum) - for num := 0; num < taskNum; num++ { - pool.AddTask(func() (interface{}, error) { - time.Sleep(10 * time.Millisecond) - wg.Done() - return nil, nil - }) - } - wg.Wait() - } - b.StopTimer() -} - -func BenchmarkGoPoolWithSpinLock(b *testing.B) { - var wg sync.WaitGroup - var taskNum = int(1e6) - pool := NewGoPool(1e4, WithLock(new(spinlock.SpinLock))) - defer pool.Release() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - wg.Add(taskNum) - for num := 0; num < taskNum; num++ { - pool.AddTask(func() (interface{}, error) { - time.Sleep(10 * time.Millisecond) - wg.Done() - return nil, nil - }) - } - wg.Wait() - } - b.StopTimer() -} - -func BenchmarkGoroutines(b *testing.B) { - var wg sync.WaitGroup - var taskNum = int(1e6) - - for i := 0; i < b.N; i++ { - wg.Add(taskNum) - for num := 0; num < taskNum; num++ { - go func() (interface{}, error) { - time.Sleep(10 * time.Millisecond) - wg.Done() - return nil, nil - }() - } - wg.Wait() - } -} - -func TestGoPoolWithError(t *testing.T) { - var errTaskError = errors.New("task error") - pool := NewGoPool(100, WithErrorCallback(func(err error) { - if err != errTaskError { - t.Errorf("Expected error %v, but got %v", errTaskError, err) - } - })) - defer pool.Release() - - for i := 0; i < 1000; i++ { - pool.AddTask(func() (interface{}, error) { - return nil, errTaskError + }) + + Describe("With Error", func() { + It("should work correctly", func() { + var errTaskError = errors.New("task error") + pool := gopool.NewGoPool(100, gopool.WithErrorCallback(func(err error) { + Expect(err).To(Equal(errTaskError)) + })) + defer pool.Release() + + for i := 0; i < 1000; i++ { + pool.AddTask(func() (interface{}, error) { + return nil, errTaskError + }) + } + pool.Wait() }) - } - pool.Wait() -} - -func TestGoPoolWithResult(t *testing.T) { - var expectedResult = "task result" - pool := NewGoPool(100, WithResultCallback(func(result interface{}) { - if result != expectedResult { - t.Errorf("Expected result %v, but got %v", expectedResult, result) - } - })) - defer pool.Release() - - for i := 0; i < 1000; i++ { - pool.AddTask(func() (interface{}, error) { - return expectedResult, nil + }) + + Describe("With Result", func() { + It("should work correctly", func() { + var expectedResult = "task result" + pool := gopool.NewGoPool(100, gopool.WithResultCallback(func(result interface{}) { + Expect(result).To(Equal(expectedResult)) + })) + defer pool.Release() + + for i := 0; i < 1000; i++ { + pool.AddTask(func() (interface{}, error) { + return expectedResult, nil + }) + } + pool.Wait() }) - } - pool.Wait() -} - -func TestGoPoolWithRetry(t *testing.T) { - var retryCount = int32(3) - var taskError = errors.New("task error") - var taskRunCount int32 = 0 - - pool := NewGoPool(100, WithRetryCount(int(retryCount))) - defer pool.Release() - - pool.AddTask(func() (interface{}, error) { - atomic.AddInt32(&taskRunCount, 1) - if taskRunCount <= retryCount { - return nil, taskError - } - return nil, nil }) - pool.Wait() + Describe("With Retry", func() { + It("should work correctly", func() { + var retryCount = int32(3) + var taskError = errors.New("task error") + var taskRunCount int32 = 0 - if atomic.LoadInt32(&taskRunCount) != retryCount+1 { - t.Errorf("Expected task to run %v times, but it ran %v times", retryCount+1, taskRunCount) - } -} + pool := gopool.NewGoPool(100, gopool.WithRetryCount(int(retryCount))) + defer pool.Release() -func TestGoPoolWithTimeout(t *testing.T) { - var taskRun int32 + pool.AddTask(func() (interface{}, error) { + atomic.AddInt32(&taskRunCount, 1) + if taskRunCount <= retryCount { + return nil, taskError + } + return nil, nil + }) - pool := NewGoPool(100, WithTimeout(100*time.Millisecond), WithErrorCallback(func(err error) { - if err.Error() != "task timed out" { - t.Errorf("Expected error 'task timed out', but got %v", err) - } - atomic.StoreInt32(&taskRun, 1) - })) - defer pool.Release() + pool.Wait() - pool.AddTask(func() (interface{}, error) { - time.Sleep(200 * time.Millisecond) - return nil, nil + Expect(atomic.LoadInt32(&taskRunCount)).To(Equal(retryCount + 1)) + }) }) - pool.Wait() + Describe("With Timeout", func() { + It("should work correctly", func() { + var taskRun int32 - if atomic.LoadInt32(&taskRun) == 0 { - t.Errorf("Expected task to run and timeout, but it did not run") - } -} + pool := gopool.NewGoPool(100, gopool.WithTimeout(100*time.Millisecond), gopool.WithErrorCallback(func(err error) { + Expect(err.Error()).To(Equal("task timed out")) + atomic.StoreInt32(&taskRun, 1) + })) + defer pool.Release() -func TestGoPoolWithMinWorkers(t *testing.T) { - var minWorkers = 50 + pool.AddTask(func() (interface{}, error) { + time.Sleep(200 * time.Millisecond) + return nil, nil + }) - pool := NewGoPool(100, WithMinWorkers(minWorkers)) - defer pool.Release() + pool.Wait() - if pool.GetWorkerCount() != minWorkers { - t.Errorf("Expected worker count to be %v, but got %v", minWorkers, pool.GetWorkerCount()) - } -} + Expect(atomic.LoadInt32(&taskRun)).To(Equal(int32(1))) + }) + }) + + Describe("With MinWorkers", func() { + It("should work correctly", func() { + var minWorkers = 50 + + pool := gopool.NewGoPool(100, gopool.WithMinWorkers(minWorkers)) + defer pool.Release() + + Expect(pool.GetWorkerCount()).To(Equal(minWorkers)) + }) + }) +})