diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 21c9018..eef6d52 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,9 +3,10 @@ name: Test on: [push, pull_request] jobs: - test: + lint: runs-on: ubuntu-latest continue-on-error: false + timeout-minutes: 5 steps: - name: Install Go @@ -16,10 +17,25 @@ jobs: - name: Checkout Code uses: actions/checkout@v2 - - name: Test - run: go test -count=1 -timeout 60s ./... - - name: Lint uses: golangci/golangci-lint-action@v2 with: - version: v1.29 + version: v1.40.1 + + test: + runs-on: ubuntu-latest + continue-on-error: false + timeout-minutes: 5 + needs: lint + + steps: + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: '^1.15' + + - name: Checkout Code + uses: actions/checkout@v2 + + - name: Test + run: go test -count=1 -timeout 60s ./... diff --git a/.golangci.yml b/.golangci.yml index c013135..59437f3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -13,7 +13,7 @@ linters-settings: lines: 100 statements: 50 gci: - local-prefixes: github.com/golangci/golangci-lint + local-prefixes: github.com/dc0d/spool goconst: min-len: 2 min-occurrences: 2 @@ -34,7 +34,7 @@ linters-settings: gocyclo: min-complexity: 15 goimports: - local-prefixes: github.com/golangci/golangci-lint + local-prefixes: github.com/dc0d/spool golint: min-confidence: 0 gomnd: @@ -83,7 +83,6 @@ linters: - gocyclo - gofmt - goimports - - golint - gomnd - goprintffuncname - gosec @@ -121,6 +120,7 @@ linters: # - testpackage # - revive # - wsl + # - golint issues: # Excluding configuration per-path, per-linter, per-text and per-source diff --git a/worker-pool.go b/worker-pool.go index b5324d6..feab4f0 100644 --- a/worker-pool.go +++ b/worker-pool.go @@ -45,10 +45,10 @@ func (pool Workerpool) Blocking(callback func()) { <-done } -// Nonblocking sends the job to the worker, as long as the mailbox is not full. +// SemiBlocking sends the job to the worker in a non-blocking manner, as long as the mailbox is not full. // After that, it becomes blocking until there is an empty space in the mailbox. -// If the workerpool is stopped, Nonblocking will panic. -func (pool Workerpool) Nonblocking(callback func()) { +// If the workerpool is stopped, SemiBlocking will panic. +func (pool Workerpool) SemiBlocking(callback func()) { pool <- callback } @@ -69,8 +69,12 @@ func (pool Workerpool) start(options growthOptions) { } func (pool Workerpool) worker(options growthOptions) { - workerStarted(pool) - defer workerStopped(pool) + if workerStarted != nil { + workerStarted(pool) + } + if workerStopped != nil { + defer workerStopped(pool) + } var ( absoluteTimeout = options.absoluteTimeout @@ -125,8 +129,8 @@ func execCallback(callback func()) { } var ( - workerStarted = func(pool Workerpool) {} - workerStopped = func(pool Workerpool) {} + workerStarted func(pool Workerpool) + workerStopped func(pool Workerpool) ) // growth options diff --git a/worker-pool_test.go b/worker-pool_test.go index 027f408..2ea8af1 100644 --- a/worker-pool_test.go +++ b/worker-pool_test.go @@ -68,7 +68,7 @@ func Test_Workerpool_Nonblocking_should_just_put_job_in_the_mailbox(t *testing.T wg.Add(n) for i := 0; i < n; i++ { - pool.Nonblocking(func() { + pool.SemiBlocking(func() { defer wg.Done() atomic.AddInt64(&counter, 1) }) @@ -98,40 +98,46 @@ func Test_Workerpool_should_not_stop_because_of_panic(t *testing.T) { func Test_Workerpool_Grow_should_spin_up_at_least_one_new_worker(t *testing.T) { increased := 1 - expectedNumberOfWorkers := increased /* the one extra worker */ + 1 /* the default worker */ pool := Init(9) defer pool.Stop() negativeOrZero := 0 pool.Grow(negativeOrZero) - time.Sleep(time.Millisecond * 50) - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + expectedNumberOfWorkers := increased /* the one extra worker */ + 1 /* the default worker */ + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) } func Test_Workerpool_Grow_should_spin_up_multiple_new_workers(t *testing.T) { increased := 10 - expectedNumberOfWorkers := increased + 1 pool := Init(9) defer pool.Stop() pool.Grow(increased) - time.Sleep(time.Millisecond * 50) - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + expectedNumberOfWorkers := increased + 1 + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) } func Test_Workerpool_Grow_should_stop_extra_workers_with_absolute_timeout(t *testing.T) { increased := 10 - expectedNumberOfWorkers := 1 absoluteTimeout := time.Millisecond * 10 pool := Init(9) defer pool.Stop() pool.Grow(increased, WithAbsoluteTimeout(absoluteTimeout)) - time.Sleep(time.Millisecond * 50) - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + expectedNumberOfWorkers := 1 + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) } func Test_Workerpool_Grow_should_stop_extra_workers_with_idle_timeout_when_there_are_no_more_jobs(t *testing.T) { @@ -145,7 +151,7 @@ func Test_Workerpool_Grow_should_stop_extra_workers_with_idle_timeout_when_there wg := &sync.WaitGroup{} wg.Add(n) for i := 0; i < n; i++ { - go pool.Nonblocking(func() { + go pool.SemiBlocking(func() { defer wg.Done() <-start }) @@ -153,12 +159,10 @@ func Test_Workerpool_Grow_should_stop_extra_workers_with_idle_timeout_when_there pool.Grow(increased, WithIdleTimeout(idleTimeout)) expectedNumberOfWorkers := 1 + increased - for i := 0; i < 3; i++ { - if expectedNumberOfWorkers > getNumberOfWorkers(pool) { - time.Sleep(time.Millisecond * 50) - } - } - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) go func() { for i := 0; i < n; i++ { @@ -168,12 +172,10 @@ func Test_Workerpool_Grow_should_stop_extra_workers_with_idle_timeout_when_there wg.Wait() expectedNumberOfWorkers = 1 - for i := 0; i < 3; i++ { - if expectedNumberOfWorkers < getNumberOfWorkers(pool) { - time.Sleep(time.Millisecond * 50) - } - } - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) } func Test_Workerpool_Grow_should_stop_extra_workers_with_explicit_stop_signal(t *testing.T) { @@ -185,36 +187,36 @@ func Test_Workerpool_Grow_should_stop_extra_workers_with_explicit_stop_signal(t pool.Grow(increased, WithStopSignal(stopSignal)) expectedNumberOfWorkers := 1 + increased - for i := 0; i < 3; i++ { - if expectedNumberOfWorkers > getNumberOfWorkers(pool) { - time.Sleep(time.Millisecond * 50) - } - } - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) close(stopSignal) expectedNumberOfWorkers = 1 - for i := 0; i < 3; i++ { - if expectedNumberOfWorkers < getNumberOfWorkers(pool) { - time.Sleep(time.Millisecond * 50) - } - } - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) } func Test_Workerpool_Grow_should_respawn_after_a_certain_number_of_requests(t *testing.T) { pool := Init(9, WithRespawnAfter(10)) defer pool.Stop() - time.Sleep(time.Millisecond * 50) - assert.Equal(t, 1, getNumberOfStarts(pool)) // one start + expectedNumberOfStarts := 1 // one initial start + assert.Eventually(t, func() bool { + return expectedNumberOfStarts == getNumberOfStarts(pool) + }, time.Millisecond*500, time.Millisecond*50) for i := 0; i < 11; i++ { pool.Blocking(func() {}) } - time.Sleep(time.Millisecond * 50) - assert.Equal(t, 2, getNumberOfStarts(pool)) + expectedNumberOfStarts = 2 + assert.Eventually(t, func() bool { + return expectedNumberOfStarts == getNumberOfStarts(pool) + }, time.Millisecond*500, time.Millisecond*50) } func Test_Workerpool_Grow_should_respawn_after_a_certain_timespan_if_reapawnAfter_is_provided(t *testing.T) { @@ -222,7 +224,8 @@ func Test_Workerpool_Grow_should_respawn_after_a_certain_timespan_if_reapawnAfte defer pool.Stop() time.Sleep(time.Millisecond * 190) - assert.Equal(t, 4, getNumberOfStarts(pool)) + expectedNumberOfStarts := 4 + assert.Equal(t, expectedNumberOfStarts, getNumberOfStarts(pool)) } // @@ -232,7 +235,7 @@ func Test_Workerpool_Stop_should_close_the_pool(t *testing.T) { pool.Stop() assert.Panics(t, func() { - pool.Nonblocking(func() {}) + pool.SemiBlocking(func() {}) }) } @@ -243,26 +246,22 @@ func Test_Workerpool_Stop_should_stop_the_workers(t *testing.T) { pool.Grow(increased) expectedNumberOfWorkers := 1 + increased - for i := 0; i < 3; i++ { - if expectedNumberOfWorkers > getNumberOfWorkers(pool) { - time.Sleep(time.Millisecond * 50) - } - } - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) pool.Stop() assert.Panics(t, func() { - pool.Nonblocking(func() {}) + pool.SemiBlocking(func() {}) }) expectedNumberOfWorkers = 0 - for i := 0; i < 3; i++ { - if expectedNumberOfWorkers < getNumberOfWorkers(pool) { - time.Sleep(time.Millisecond * 50) - } - } - assert.Equal(t, expectedNumberOfWorkers, getNumberOfWorkers(pool)) + assert.Eventuallyf(t, func() bool { + return expectedNumberOfWorkers == getNumberOfWorkers(pool) + }, time.Millisecond*500, time.Millisecond*50, + "expectedNumberOfWorkers: %v, actual: %v", expectedNumberOfWorkers, getNumberOfWorkers(pool)) } // @@ -346,7 +345,7 @@ func ExampleWorkerpool_Blocking() { // 19 } -func ExampleWorkerpool_Nonblocking() { +func ExampleWorkerpool_SemiBlocking() { pool := Init(1) defer pool.Stop() @@ -357,7 +356,7 @@ func ExampleWorkerpool_Nonblocking() { atomic.AddInt64(&state, 19) } - pool.Nonblocking(job) + pool.SemiBlocking(job) <-jobDone fmt.Println(state) @@ -377,7 +376,7 @@ func ExampleWorkerpool_Grow() { wg := &sync.WaitGroup{} wg.Add(n) for i := 0; i < n; i++ { - pool.Nonblocking(func() { defer wg.Done(); atomic.AddInt64(&state, 1) }) + pool.SemiBlocking(func() { defer wg.Done(); atomic.AddInt64(&state, 1) }) } wg.Wait()