packages feed

teardown-0.0.0.1: test/TestSuite.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where

import Protolude

import Test.Tasty                   (TestTree, defaultMainWithIngredients, testGroup)
import Test.Tasty.HUnit
import Test.Tasty.Ingredients.Rerun (rerunningTests)
import Test.Tasty.Runners           (consoleTestReporter, listingTests)

import Control.Teardown
import Data.IORef       (atomicModifyIORef, newIORef, readIORef)

main :: IO ()
main =
  defaultMainWithIngredients
    [ rerunningTests [listingTests, consoleTestReporter] ]
    tests

tests :: TestTree
tests =
  testGroup "teardown"
  [
    testCase "idempotent execution of teardown action" $ do
      callCountRef <- newIORef (0 :: Int)
      teardownAction <- newTeardown "test cleanup" $
        atomicModifyIORef callCountRef (\a -> (succ a, ()))

      replicateM_ 10 (teardown teardownAction)
      callCount <- readIORef callCountRef
      assertEqual "teardown action got called more than once"
                  1 callCount

  , testCase "failing teardown action does not stop execution" $ do
      teardownAction <- newTeardown "failing teardown" $
        panic "failing teardown"

      result <- teardown teardownAction
      replicateM_ 9 (teardown teardownAction)

      assertBool "result should report an error"
                 (didTeardownFail result)

  , testCase "thread safe idempotent execution of teardown action" $ do
      callCountRef <- newIORef (0 :: Int)
      teardownAction <- newTeardown "test cleanup" $
        atomicModifyIORef callCountRef (\a -> (succ a, ()))

      asyncList <-
        replicateM 10 (async
                        -- each async executes teardown 3 times
                        $ replicateM_ 3
                        $ void $ teardown teardownAction)

      mapM_ wait asyncList
      callCount <- readIORef callCountRef
      assertEqual "teardown action must not be called more than once"
                  1 callCount

  , testCase "concatenated teardown actions keep idempotent guarantees" $ do
      callCountRefs <- replicateM 10 $ newIORef (0 :: Int)
      teardownActions <- forM callCountRefs $ \callCountRef ->
        newTeardown "test cleanup"
                    (atomicModifyIORef callCountRef (\a -> (succ a, ())))

      let
        teardownAction =
          concatTeardown "bigger system" teardownActions

      replicateM_ 10 (teardown teardownAction)

      countRefs <- mapM readIORef callCountRefs
      assertEqual "teardown action must not be called more than once"
                  (replicate 10 1)
                  countRefs

  , testCase "concatenated teardown actions return correct count" $ do
      teardownActions <-
        replicateM 10 (newTeardown "test cleanup" (return ()))

      let
        teardownAction =
          concatTeardown "bigger system" teardownActions

      toredownResult <- teardown teardownAction
      replicateM_ 9 (teardown teardownAction)

      assertEqual "teardown action must not be called more than once"
                  10 (toredownCount toredownResult)

  , testCase "concatenated failed teardown actions return correct count" $ do
      failedTeardownActions <-
        replicateM 5 (newTeardown "test cleanup with failures" (panic "nope"))

      teardownActions <-
        replicateM 5 (newTeardown "test cleanup" (return ()))

      let
        teardownAction =
          concatTeardown "bigger system"
                         (failedTeardownActions <> teardownActions)

      toredownResult <- teardown teardownAction
      replicateM_ 9 (teardown teardownAction)

      assertEqual "teardown action count must be correct"
                  10 (toredownCount toredownResult)

      assertEqual "failed teardown action must be correct"
                  5 (failedToredownCount toredownResult)

  , testCase "dynamic teardown behave similar to vanilla teardown" $ do
      callCountRef <- newIORef (0 :: Int)
      failedTeardownActions <-
        replicateM 5 (newTeardown "test cleanup with failures" (panic "nope"))

      teardownActions <-
        replicateM 5 (newTeardown "test cleanup"
                      (atomicModifyIORef callCountRef (\a -> (succ a, ()))))

      let
        teardownAction =
          newDynTeardown "bigger system" $
            mapM teardown (failedTeardownActions <> teardownActions)

      teardownResult <- teardown teardownAction
      replicateM_ 9 (teardown teardownAction)

      teardownSuccessCount <- readIORef callCountRef

      assertEqual "teardown action count must be correct"
                  10 (toredownCount teardownResult)

      assertEqual "teardown action must be idempotent"
                  5 teardownSuccessCount

  ]