diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bac03d..b8df42d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/spago.dhall b/spago.dhall index 299e7f5..f07a003 100644 --- a/spago.dhall +++ b/spago.dhall @@ -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" ] } diff --git a/test/BoundedQueue.purs b/test/BoundedQueue.purs index 6c7b906..0e93395 100644 --- a/test/BoundedQueue.purs +++ b/test/BoundedQueue.purs @@ -5,14 +5,15 @@ 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))) @@ -20,79 +21,79 @@ 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 @@ -100,70 +101,70 @@ boundedQueueSyncSuite = do 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) diff --git a/test/Main.purs b/test/Main.purs index 6387da8..49f2dac 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -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 diff --git a/test/Queue.purs b/test/Queue.purs index e5b8cd0..69c7707 100644 --- a/test/Queue.purs +++ b/test/Queue.purs @@ -4,12 +4,13 @@ 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))) @@ -17,10 +18,10 @@ 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 @@ -28,33 +29,33 @@ queueSuite = do 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) diff --git a/test/Util.purs b/test/Util.purs new file mode 100644 index 0000000..3fb1b99 --- /dev/null +++ b/test/Util.purs @@ -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) + +-----------------------------------------------------------------