diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 83024e6..040e287 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -59,3 +59,24 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.txt flags: unittests + + test-os: + name: Test (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest] + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Install Go + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version: stable + + # Catches platform-specific test flakiness (timer resolution, scheduler + # behaviour) that the Linux-only coverage job cannot see; see #92. + - name: Test + run: go test -mod=mod -shuffle=on -race -timeout 120s . diff --git a/delay_test.go b/delay_test.go index c02b684..6b29220 100644 --- a/delay_test.go +++ b/delay_test.go @@ -114,16 +114,19 @@ func testDelayGetWait(t *testing.T) { due := time.Now().Add(20 * time.Millisecond) delayQueue := queue.NewDelay([]delayed{{ID: 1, At: due}}, delayedDeadline) - start := time.Now() got := delayQueue.GetWait() - elapsed := time.Since(start) + // Compare against the deadline itself rather than an elapsed duration + // measured from a second, later clock reading: any time spent between + // the two readings would make elapsed fall short of 20ms even though + // GetWait woke on time. + returned := time.Now() if got.ID != 1 { t.Fatalf("got id=%d want 1", got.ID) } - if elapsed < 20*time.Millisecond { - t.Fatalf("GetWait returned before deadline: %s", elapsed) + if returned.Before(due) { + t.Fatalf("GetWait returned %s before deadline", due.Sub(returned)) } }) diff --git a/example_blocking_test.go b/example_blocking_test.go index 66ff3d5..5d36b11 100644 --- a/example_blocking_test.go +++ b/example_blocking_test.go @@ -2,7 +2,6 @@ package queue_test import ( "fmt" - "time" "github.com/adrianbrad/queue" ) @@ -18,6 +17,14 @@ func ExampleBlocking() { size := blockingQueue.Size() fmt.Println("Size:", size) + head, err := blockingQueue.Peek() + if err != nil { + fmt.Println("Peek err:", err) + return + } + + fmt.Println("Peeked elem:", head) + empty := blockingQueue.IsEmpty() fmt.Println("Empty before clear:", empty) @@ -27,53 +34,33 @@ func ExampleBlocking() { empty = blockingQueue.IsEmpty() fmt.Println("Empty after clear:", empty) - var ( - elem int - after time.Duration - ) - - done := make(chan struct{}) + // Get does not block: on an empty queue it returns an error. + if _, err := blockingQueue.Get(); err != nil { + fmt.Println("Get err:", err) + } - start := time.Now() + received := make(chan int) - // this function waits for a new element to be available in the queue. + // GetWait blocks until an element is available, so it is called from + // another goroutine; it returns only once Offer below has run. go func() { - defer close(done) - - elem = blockingQueue.GetWait() - after = time.Since(start) + received <- blockingQueue.GetWait() }() - time.Sleep(time.Millisecond) - - // insert a new element into the queue. if err := blockingQueue.Offer(4); err != nil { fmt.Println("Offer err:", err) return } - nextElem, err := blockingQueue.Peek() - if err != nil { - fmt.Println("Peek err:", err) - return - } - - fmt.Println("Peeked elem:", nextElem) - - <-done - - fmt.Printf( - "Elem %d received after %s", - elem, - after.Round(time.Millisecond), - ) + fmt.Println("GetWait received:", <-received) // Output: // Contains 3: true // Size: 3 + // Peeked elem: 1 // Empty before clear: false // Clear: [1 2 3] // Empty after clear: true - // Peeked elem: 4 - // Elem 4 received after 1ms + // Get err: no elements available in the queue + // GetWait received: 4 }