Skip to content

Replace test-unit with assert and ReaderT #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Bugfixes:
Other improvements:
- Changed default branch to `main` from `master`
- Updated to comply with Contributors library guidelines by adding new issue and pull request templates, updating documentation, and migrating to Spago for local development and CI
- Replaced `test-unit` dependency with `assert` and `ReaderT` wrapper (#11)

## [v1.1.0](https://github.com/purescript-contrib/purescript-concurrent-queues/releases/tag/v1.1.0) - 2018-10-22

Expand Down
2 changes: 1 addition & 1 deletion spago.dhall
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{ name = "concurrent-queues"
, dependencies =
[ "aff", "avar", "console", "effect", "psci-support", "test-unit" ]
[ "aff", "assert", "avar", "console", "effect", "psci-support" ]
, packages = ./packages.dhall
, sources = [ "src/**/*.purs", "test/**/*.purs" ]
}
109 changes: 55 additions & 54 deletions test/BoundedQueue.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,165 +5,166 @@ import Prelude
import Concurrent.BoundedQueue as BQ
import Concurrent.BoundedQueue.Sync as BQS
import Control.Alt ((<|>))
import Control.Monad.Reader.Class (class MonadReader)
import Data.Either (Either(..), isLeft, isRight)
import Data.Int (toNumber)
import Data.Maybe (Maybe(..), isNothing)
import Data.Tuple (Tuple(..))
import Effect.Aff (Aff, Milliseconds(..), delay, forkAff, parallel, sequential)
import Effect.Class (liftEffect)
import Test.Unit (TestSuite, suite, test)
import Test.Unit.Assert as Assert
import Effect.Aff.Class (class MonadAff, liftAff)
import Test.Util (suite, test, shouldEqual, assert, assertFalse)

race ∷ ∀ a b. Aff a → Aff b → Aff (Either a b)
race a b = sequential ((parallel (map Left a)) <|> (parallel (map Right b)))

delayMs ∷ Int → Aff Unit
delayMs = delay <<< Milliseconds <<< toNumber

boundedQueueSuite ∷ TestSuite
boundedQueueSuite ∷ forall m. MonadReader Int m ⇒ MonadAff m ⇒ m Unit
boundedQueueSuite = do
suite "Simple operations" do
test "inserting and popping elements" do
test "inserting and popping elements" $ liftAff do
q ← BQ.new 2
BQ.write q 1
BQ.write q 2
r1 ← BQ.read q
r2 ← BQ.read q
Assert.equal r1 1
Assert.equal r2 2
r1 `shouldEqual` 1
r2 `shouldEqual` 2
suite "Blocking and unblocking" do
test "writing more than the allowed capacity blocks" do
test "writing more than the allowed capacity blocks" $ liftAff do
q ← BQ.new 1
BQ.write q 1
r ← race (delayMs 50) (BQ.write q 2)
Assert.assert "Not blocked" (isLeft r)
test "reading unblocks writes blocked on missing capacity" do
assert "Not blocked" (isLeft r)
test "reading unblocks writes blocked on missing capacity" $ liftAff do
q ← BQ.new 1
BQ.write q 1
_ ← forkAff (delayMs 20 *> (BQ.read q))
r ← race (delayMs 50) (BQ.write q 2)
Assert.assert "Blocked too long" (isRight r)
assert "Blocked too long" (isRight r)
suite "isEmpty" do
test "an empty queue is empty" do
test "an empty queue is empty" $ liftAff do
q ← BQ.new 1
r ← BQ.isEmpty q
Assert.assert "" r
test "an empty queue with blocked readers is empty" do
assert "" r
test "an empty queue with blocked readers is empty" $ liftAff do
q ← BQ.new 1
_ ← forkAff (BQ.read q)
r ← BQ.isEmpty q
Assert.assert "" r
assert "" r
suite "tryRead blocking and unblocking" do
test "tryRead is non-blocking for empty queue" do
test "tryRead is non-blocking for empty queue" $ liftAff do
q ← BQ.new 1
r ← BQ.tryRead q
Assert.assert "Should've been Nothing" (isNothing r)
test "tryRead reads from a non-empty queue" do
assert "Should've been Nothing" (isNothing r)
test "tryRead reads from a non-empty queue" $ liftAff do
q ← BQ.new 1
BQ.write q 1
r1 ← BQ.tryRead q
r2 ← BQ.tryRead q
Assert.equal r1 (Just 1)
Assert.assert "Should've been Nothing" (isNothing r2)
test "tryRead blocks when there are consumers blocked on the queue" do
r1 `shouldEqual` (Just 1)
assert "Should've been Nothing" (isNothing r2)
test "tryRead blocks when there are consumers blocked on the queue" $ liftAff do
q ← BQ.new 1
_ ← forkAff (BQ.read q)
r ← race (delayMs 20) (BQ.tryRead q)
Assert.assert "Should've been Left" (isLeft r)
assert "Should've been Left" (isLeft r)
suite "tryWrite blocking and unblocking" do
test "tryWrite is non-blocking for full queue" do
test "tryWrite is non-blocking for full queue" $ liftAff do
q ← BQ.new 1
BQ.write q 1
r ← BQ.tryWrite q 2
Assert.assertFalse "Write should've failed" r
test "tryWrite writes to a non-full queue" do
assertFalse "Write should've failed" r
test "tryWrite writes to a non-full queue" $ liftAff do
q ← BQ.new 1
rw ← BQ.tryWrite q 1
r ← BQ.read q
Assert.assert "tryWrite should've succeeded" rw
Assert.equal r 1
test "tryWrite blocks when there are writers blocked on the queue" do
assert "tryWrite should've succeeded" rw
r `shouldEqual` 1
test "tryWrite blocks when there are writers blocked on the queue" $ liftAff do
q ← BQ.new 1
BQ.write q 1
_ ← forkAff (BQ.write q 2)
r ← race (delayMs 20) (BQ.tryWrite q 2)
Assert.assert "Should've been Left" (isLeft r)
assert "Should've been Left" (isLeft r)

boundedQueueSyncSuite :: TestSuite
boundedQueueSyncSuite :: forall m. MonadReader Int m ⇒ MonadAff m ⇒ m Unit
boundedQueueSyncSuite = do
suite "(Sync) Simple operations" do
test "(Sync) inserting and popping elements" do
test "(Sync) inserting and popping elements" $ liftAff do
Tuple r1 r2 ← liftEffect do
q ← liftEffect (BQS.new 2)
_ ← BQS.tryWrite q 1
_ ← BQS.tryWrite q 2
r1 ← BQS.tryRead q
r2 ← BQS.tryRead q
pure (Tuple r1 r2)
Assert.equal r1 (Just 1)
Assert.equal r2 (Just 2)
r1 `shouldEqual` (Just 1)
r2 `shouldEqual` (Just 2)
suite "(Sync) Blocking and unblocking" do
test "(Sync) writing more than the allowed capacity blocks" do
test "(Sync) writing more than the allowed capacity blocks" $ liftAff do
q ← liftEffect (BQS.new 1)
BQ.write q 1
r ← race (delayMs 50) (BQ.write q 2)
Assert.assert "Not blocked" (isLeft r)
test "(Sync) reading unblocks writes blocked on missing capacity" do
assert "Not blocked" (isLeft r)
test "(Sync) reading unblocks writes blocked on missing capacity" $ liftAff do
q ← liftEffect (BQS.new 1)
BQ.write q 1
_ ← forkAff (delayMs 20 *> (BQ.read q))
r ← race (delayMs 50) (BQ.write q 2)
Assert.assert "Blocked too long" (isRight r)
assert "Blocked too long" (isRight r)
suite "(Sync) isEmpty" do
test "(Sync) an empty queue is empty" do
test "(Sync) an empty queue is empty" $ liftAff do
r ← liftEffect do
q ← BQS.new 1
BQS.isEmpty q
Assert.assert "" r
test "(Sync) an empty queue with blocked readers is empty" do
assert "" r
test "(Sync) an empty queue with blocked readers is empty" $ liftAff do
q ← liftEffect (BQS.new 1)
_ ← forkAff (BQ.read q)
r ← liftEffect (BQS.isEmpty q)
Assert.assert "" r
assert "" r
suite "(Sync) tryRead blocking and unblocking" do
test "(Sync) tryRead is non-blocking for empty queue" do
test "(Sync) tryRead is non-blocking for empty queue" $ liftAff do
r ← liftEffect do
q ← BQS.new 1
BQS.tryRead q
Assert.assert "Should've been Nothing" (isNothing r)
test "(Sync) tryRead reads from a non-empty queue" do
assert "Should've been Nothing" (isNothing r)
test "(Sync) tryRead reads from a non-empty queue" $ liftAff do
q ← liftEffect (BQS.new 1)
BQ.write q 1
Tuple r1 r2 ← liftEffect do
r1 ← BQS.tryRead q
r2 ← BQS.tryRead q
pure (Tuple r1 r2)
Assert.equal r1 (Just 1)
Assert.assert "Should've been Nothing" (isNothing r2)
r1 `shouldEqual` (Just 1)
assert "Should've been Nothing" (isNothing r2)
test ("(Sync) tryRead does not block when there are consumers blocked on "
<> "the queue") do
<> "the queue") $ liftAff do
q ← liftEffect (BQS.new 1)
_ ← forkAff (BQ.read q)
r ← race (delayMs 20) (liftEffect (BQS.tryRead q))
Assert.assert "Should've been Right" (isRight r)
assert "Should've been Right" (isRight r)
suite "(Sync) tryWrite blocking and unblocking" do
test "(Sync) tryWrite is non-blocking for full queue" do
test "(Sync) tryWrite is non-blocking for full queue" $ liftAff do
q ← liftEffect (BQS.new 1)
BQ.write q 1
r ← liftEffect (BQS.tryWrite q 2)
Assert.assertFalse "Write should've failed" r
test "(Sync) tryWrite writes to a non-full queue" do
assertFalse "Write should've failed" r
test "(Sync) tryWrite writes to a non-full queue" $ liftAff do
Tuple q rw ← liftEffect do
q ← BQS.new 1
rw ← BQS.tryWrite q 1
pure (Tuple q rw)
r ← BQ.read q
Assert.assert "tryWrite should've succeeded" rw
Assert.equal r 1
assert "tryWrite should've succeeded" rw
r `shouldEqual` 1
test ("(Sync) tryWrite does not block when there are writers blocked on " <>
"the queue") do
"the queue") $ liftAff do
q ← liftEffect (BQS.new 1)
BQ.write q 1
_ ← forkAff (BQ.write q 2)
r ← race (delayMs 20) (liftEffect (BQS.tryWrite q 2))
Assert.assert "Should've been Right" (isRight r)
assert "Should've been Right" (isRight r)
7 changes: 4 additions & 3 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ module Test.Main where

import Prelude

import Control.Monad.Reader.Trans (runReaderT)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Test.BoundedQueue (boundedQueueSuite, boundedQueueSyncSuite)
import Test.Queue (queueSuite)
import Test.Unit (suite)
import Test.Unit.Main (runTest)
import Test.Util (suite)

main ∷ Effect Unit
main = runTest do
main = launchAff_ $ flip runReaderT 0 do
suite "Queue" queueSuite
suite "BoundedQueue" boundedQueueSuite
suite "(Sync) BoundedQueue" boundedQueueSyncSuite
37 changes: 19 additions & 18 deletions test/Queue.purs
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,58 @@ import Prelude

import Concurrent.Queue as Q
import Control.Alt ((<|>))
import Control.Monad.Reader.Class (class MonadReader)
import Data.Either (Either(..), isLeft, isRight)
import Data.Int (toNumber)
import Data.Maybe (Maybe(..), isNothing)
import Effect.Aff (Aff, Milliseconds(..), delay, forkAff, parallel, sequential)
import Test.Unit (TestSuite, suite, test)
import Test.Unit.Assert as Assert
import Effect.Aff.Class (class MonadAff, liftAff)
import Test.Util (suite, test, shouldEqual, assert)

race ∷ ∀ a b. Aff a → Aff b → Aff (Either a b)
race a b = sequential ((parallel (map Left a)) <|> (parallel (map Right b)))

delayMs ∷ Int → Aff Unit
delayMs = delay <<< Milliseconds <<< toNumber

queueSuite ∷ TestSuite
queueSuite ∷ forall m. MonadReader Int m ⇒ MonadAff m ⇒ m Unit
queueSuite = do
suite "Simple operations" do
test "inserting and popping elements" do
test "inserting and popping elements" $ liftAff do
q ← Q.new
Q.write q 1
Q.write q 2
r1 ← Q.read q
r2 ← Q.read q
Q.write q 3
r3 ← Q.read q
Assert.equal r1 1
Assert.equal r2 2
Assert.equal r3 3
r1 `shouldEqual` 1
r2 `shouldEqual` 2
r3 `shouldEqual` 3
suite "Blocking and unblocking" do
test "reading from an empty Queue blocks" do
test "reading from an empty Queue blocks" $ liftAff do
q ← Q.new
r ← race (delayMs 50) (Q.read q)
Assert.assert "Not blocked" (isLeft r)
test "writing unblocks reads" do
assert "Not blocked" (isLeft r)
test "writing unblocks reads" $ liftAff do
q ← Q.new
_ ← forkAff (delayMs 20 *> (Q.write q 1))
r ← race (delayMs 50) (Q.read q)
Assert.assert "Blocked too long" (isRight r)
assert "Blocked too long" (isRight r)
suite "tryRead blocking and unblocking" do
test "tryRead is non-blocking for empty queue" do
test "tryRead is non-blocking for empty queue" $ liftAff do
q ← Q.new
r ← Q.tryRead q
Assert.assert "Should've been Nothing" (isNothing r)
test "tryRead reads from a non-empty queue" do
assert "Should've been Nothing" (isNothing r)
test "tryRead reads from a non-empty queue" $ liftAff do
q ← Q.new
Q.write q 1
r1 ← Q.tryRead q
r2 ← Q.tryRead q
Assert.equal r1 (Just 1)
Assert.assert "Should've been Nothing" (isNothing r2)
test "tryRead blocks when there are consumers blocked on the queue" do
r1 `shouldEqual` (Just 1)
assert "Should've been Nothing" (isNothing r2)
test "tryRead blocks when there are consumers blocked on the queue" $ liftAff do
q ← Q.new
_ ← forkAff (Q.read q)
r ← race (delayMs 20) (Q.tryRead q)
Assert.assert "Should've been Left" (isLeft r)
assert "Should've been Left" (isLeft r)
38 changes: 38 additions & 0 deletions test/Util.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
module Test.Util where

import Prelude

import Control.Monad.Reader.Class (class MonadReader, ask, local)
import Data.Monoid (power, guard)
import Effect.Console (log)
import Effect.Class (liftEffect)
import Effect.Class.Console (error)
import Effect.Aff.Class (class MonadAff, liftAff)
import Test.Assert (assertEqual)

-----------------------------------------------------------------

-- Provide similar API to purescript-test-unit to reduce code changes

suite :: forall m. MonadReader Int m ⇒ MonadAff m ⇒ String -> m Unit -> m Unit
suite = test

test :: forall m. MonadReader Int m ⇒ MonadAff m ⇒ String -> m Unit -> m Unit
test msg runTest = do
indentation <- ask
let spacing = guard (indentation > 0) " "
liftEffect $ log $ (power ">>" indentation) <> spacing <> msg
local (_ + 1) runTest

shouldEqual :: forall m a. MonadAff m ⇒ Eq a ⇒ Show a ⇒ a -> a -> m Unit
shouldEqual actual expected =
liftEffect $ assertEqual { actual, expected }

assert :: forall m. MonadAff m => String -> Boolean -> m Unit
assert _ true = pure unit
assert msg false = liftAff $ error msg

assertFalse :: forall m. MonadAff m => String -> Boolean -> m Unit
assertFalse msg b = assert msg (not b)

-----------------------------------------------------------------