packages feed

teardown-0.1.0.0: 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, modifyIORef, 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" :: IO ())

      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 "teardown tree keeps idempotent guarantees around execution" $ do
      callCountRefs <- replicateM 10 $ newIORef (0 :: Int)

      teardownAction <-
        newTeardown "bigger system" $
          forM callCountRefs $ \callCountRef ->
            newTeardown "test cleanup"
                        (atomicModifyIORef callCountRef (\a -> (succ a, ())))

      replicateM_ 10 (teardown teardownAction)

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

  , testCase "teardown action that returns Teardown list returns correct count" $ do
      failedTeardownActions <-
        replicateM 5 (newTeardown "test cleanup with failures" (panic "nope" :: IO ()))

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

      teardownAction <-
        newTeardown "bigger system"
          (return (failedTeardownActions <> teardownActions) :: IO [Teardown])

      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 "teardown with list of description and actions executes correctly" $ do
      callCountRef <- newIORef (0 :: Int)
      teardownAction <-
          newTeardown "bigger-system"
              [
                ("1" :: Text, modifyIORef callCountRef (+1))
              , ("2", modifyIORef callCountRef (+1))
              , ("3", modifyIORef callCountRef (+1))
              , ("4", modifyIORef callCountRef (+1))
              , ("5", modifyIORef callCountRef (+1))
              , ("6", panic "nope")
              , ("7", panic "nope")
              , ("8", panic "nope")
              , ("9", panic "nope")
              ]


      -- Execute multiple times to assert idempotency
      toredownResult <- teardown teardownAction
      replicateM_ 9 (teardown teardownAction)

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

      assertEqual "failed teardown must be correct"
                  4 (failedToredownCount toredownResult)

      callCount <- readIORef callCountRef
      assertEqual "side-effects were executed despite errors on other teardown operations"
                  5 callCount
  ]