hedgehog-extras-0.10.1.0: test/Hedgehog/Extras/Test/WorkspaceSpec.hs
{-# LANGUAGE BangPatterns #-}
module Hedgehog.Extras.Test.WorkspaceSpec where
import Control.Applicative
import Control.Exception (throwIO)
import Control.Monad.IO.Class
import Data.Bool
import Data.Either
import Data.Function (($))
import Data.IORef
import Data.Maybe
import Data.Semigroup ((<>))
import GHC.Err (error)
import Hedgehog
import Hedgehog.Extras.Test.Base
import Hedgehog.Extras.Test.Unit
import System.IO.Error (userError)
import Text.Show
import qualified System.Directory as IO
import qualified Hedgehog.Internal.Property as H
-- | Test that workspace directories are removed on successful completion when keepWorkspace=False
tasty_workspace_removed_on_success_on_keepWorkspace_False :: UnitIO ()
tasty_workspace_removed_on_success_on_keepWorkspace_False = do
workspacePath <- liftIO $ newIORef Nothing
-- This workspace operation will succeed
workspaceWithConfig CleanupOnSuccess "test-success" $ \ws -> do
liftIO $ writeIORef workspacePath (Just ws)
-- Successful operation - no failing assertions
True === True
-- After successful completion, workspace should be removed
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing -> do
H.failWith Nothing "Expected workspace path to be recorded, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
annotate $ "Workspace path: " <> path
annotate $ "Directory exists after success: " <> show exists
-- On success, directory should be removed
exists === False
-- | Test that workspace directories are preserved when assertion fails (keepWorkspace=False)
tasty_workspace_kept_on_assertion_on_keepWorkspace_False :: UnitIO ()
tasty_workspace_kept_on_assertion_on_keepWorkspace_False = do
-- This test will intentionally trigger an assertion failure to verify
-- that the workspace directory is preserved for debugging
workspacePath <- liftIO $ newIORef Nothing
result <- tryAssertion $ workspaceWithConfig CleanupOnSuccess "test-failure" $ \ws -> do
-- Store the workspace path so we can check it later
liftIO $ writeIORef workspacePath (Just ws)
-- Intentionally trigger assertion failure
False === True
case result of
Left _ -> do
-- Assertion failed as expected, now check if workspace was correctly preserved
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing -> do
H.failWith Nothing "Expected workspace path to be recorded after failed assertion, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
-- Correct behavior: directory is preserved on assertion failure for debugging
annotate $ "Workspace path: " <> path
annotate $ "Directory exists: " <> show exists
-- Directory should be preserved when assertion fails
exists === True
-- Clean up manually since assertion failed and we preserved it
liftIO $ IO.removeDirectoryRecursive path
Right _ -> do
-- Assertion unexpectedly passed, this shouldn't happen
annotate "Test was supposed to fail but didn't"
failure
-- | Test that workspace directories are preserved when pure exception occurs (keepWorkspace=False)
tasty_workspace_kept_on_pure_exception_on_keepWorkspace_False :: UnitIO ()
tasty_workspace_kept_on_pure_exception_on_keepWorkspace_False = do
workspacePath <- liftIO $ newIORef Nothing
result <- tryAssertion $ workspaceWithConfig CleanupOnSuccess "test-pure-exception" $ \ws -> do
-- Store the workspace path so we can check it later
liftIO $ writeIORef workspacePath (Just ws)
-- Force evaluation of pure code that throws an exception
let !_ = error "Pure code exception in workspace" :: ()
pure ()
case result of
Left _ -> do
-- Exception was thrown as expected, now check if workspace was correctly preserved
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing ->
H.failWith Nothing "Expected workspace path to be recorded after pure exception, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
annotate $ "Workspace path after pure exception: " <> path
annotate $ "Directory exists after pure exception: " <> show exists
-- Correct behavior: directory is preserved when pure code throws exception
exists === True
-- Clean up manually since exception occurred and we preserved it
liftIO $ IO.removeDirectoryRecursive path
Right _ -> do
-- Pure code unexpectedly didn't throw exception
annotate "Pure code was supposed to throw exception but didn't"
failure
-- | Test that workspace directories are preserved when IO exception occurs (keepWorkspace=False)
tasty_workspace_kept_on_io_exception_on_keepWorkspace_False :: UnitIO ()
tasty_workspace_kept_on_io_exception_on_keepWorkspace_False = do
workspacePath <- liftIO $ newIORef Nothing
result <- tryAssertion $ workspaceWithConfig CleanupOnSuccess "test-io-exception" $ \ws -> do
-- Store the workspace path so we can check it later
liftIO $ writeIORef workspacePath (Just ws)
-- Throw an IO exception within the workspace
liftIO $ throwIO (userError "IO exception in workspace")
case result of
Left _ -> do
-- Exception was thrown as expected, now check if workspace was correctly preserved
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing ->
H.failWith Nothing "Expected workspace path to be recorded after IO exception, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
annotate $ "Workspace path after IO exception: " <> path
annotate $ "Directory exists after IO exception: " <> show exists
-- Correct behavior: directory is preserved when IO exception is thrown
exists === True
-- Clean up manually since exception occurred and we preserved it
liftIO $ IO.removeDirectoryRecursive path
Right _ -> do
-- IO operation unexpectedly didn't throw exception
annotate "IO operation was supposed to throw exception but didn't"
failure
-- | Test that workspace directories are preserved on successful completion when keepWorkspace=True
tasty_workspace_kept_on_success_on_keepWorkspace_True :: UnitIO ()
tasty_workspace_kept_on_success_on_keepWorkspace_True = do
workspacePath <- liftIO $ newIORef Nothing
-- This workspace operation will succeed
workspaceWithConfig PreserveWorkspace "test-keep-success" $ \ws -> do
liftIO $ writeIORef workspacePath (Just ws)
-- Successful operation - no failing assertions
True === True
-- After successful completion, workspace should still be preserved due to keepWorkspace=True
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing -> do
H.failWith Nothing "Expected workspace path to be recorded with keepWorkspace=True, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
annotate $ "Workspace path with keepWorkspace=True (success): " <> path
annotate $ "Directory exists after success with keepWorkspace=True: " <> show exists
-- With keepWorkspace=True, directory should be preserved even on success
exists === True
-- Clean up manually since we kept it
liftIO $ IO.removeDirectoryRecursive path
-- | Test that workspace directories are preserved when assertion fails (keepWorkspace=True)
tasty_workspace_kept_on_assertion_on_keepWorkspace_True :: UnitIO ()
tasty_workspace_kept_on_assertion_on_keepWorkspace_True = do
workspacePath <- liftIO $ newIORef Nothing
result <- tryAssertion $ workspaceWithConfig PreserveWorkspace "test-keep" $ \ws -> do
liftIO $ writeIORef workspacePath (Just ws)
-- This assertion will fail, but workspace should be kept due to keepWorkspace=True
False === True
case result of
Left _ -> do
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing -> do
H.failWith Nothing "Expected workspace path to be recorded with keepWorkspace=True after failed assertion, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
annotate $ "Workspace path: " <> path
annotate $ "Directory exists with keepWorkspace=True: " <> show exists
-- With keepWorkspace=True, directory should always be preserved
exists === True
-- Clean up manually since we kept it
liftIO $ IO.removeDirectoryRecursive path
Right _ -> do
annotate "Test was supposed to fail but didn't"
failure
-- | Test that workspace directories are preserved when pure exception occurs (keepWorkspace=True)
tasty_workspace_kept_on_pure_exception_on_keepWorkspace_True :: UnitIO ()
tasty_workspace_kept_on_pure_exception_on_keepWorkspace_True = do
workspacePath <- liftIO $ newIORef Nothing
result <- tryAssertion $ workspaceWithConfig PreserveWorkspace "test-keep-pure-exception" $ \ws -> do
-- Store the workspace path so we can check it later
liftIO $ writeIORef workspacePath (Just ws)
-- Force evaluation of pure code that throws an exception
let !_ = error "Pure code exception with keepWorkspace=True" :: ()
pure ()
case result of
Left _ -> do
-- Exception was thrown as expected, now check if workspace was correctly preserved
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing -> do
H.failWith Nothing "Expected workspace path to be recorded with keepWorkspace=True after pure exception, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
annotate $ "Workspace path after pure exception with keepWorkspace=True: " <> path
annotate $ "Directory exists after pure exception with keepWorkspace=True: " <> show exists
-- With keepWorkspace=True, directory should always be preserved
exists === True
-- Clean up manually since we kept it
liftIO $ IO.removeDirectoryRecursive path
Right _ -> do
-- Pure code unexpectedly didn't throw exception
annotate "Pure code was supposed to throw exception but didn't"
failure
-- | Test that workspace directories are preserved when IO exception occurs (keepWorkspace=True)
tasty_workspace_kept_on_io_exception_on_keepWorkspace_True :: UnitIO ()
tasty_workspace_kept_on_io_exception_on_keepWorkspace_True = do
workspacePath <- liftIO $ newIORef Nothing
result <- tryAssertion $ workspaceWithConfig PreserveWorkspace "test-keep-io-exception" $ \ws -> do
-- Store the workspace path so we can check it later
liftIO $ writeIORef workspacePath (Just ws)
-- Throw an IO exception within the workspace
liftIO $ throwIO (userError "IO exception with keepWorkspace=True")
case result of
Left _ -> do
-- Exception was thrown as expected, now check if workspace was correctly preserved
maybePath <- liftIO $ readIORef workspacePath
case maybePath of
Nothing -> do
H.failWith Nothing "Expected workspace path to be recorded with keepWorkspace=True after IO exception, but got Nothing"
Just path -> do
exists <- liftIO $ IO.doesDirectoryExist path
annotate $ "Workspace path after IO exception with keepWorkspace=True: " <> path
annotate $ "Directory exists after IO exception with keepWorkspace=True: " <> show exists
-- With keepWorkspace=True, directory should always be preserved
exists === True
-- Clean up manually since we kept it
liftIO $ IO.removeDirectoryRecursive path
Right _ -> do
-- IO operation unexpectedly didn't throw exception
annotate "IO operation was supposed to throw exception but didn't"
failure