UnkindPartition / tasty

Modern and extensible testing framework for Haskell
640 stars 110 forks source link

Resources not cleaned up with Ctrl+C #354

Open brandonchinn178 opened 1 year ago

brandonchinn178 commented 1 year ago

Minimal repro:

import Control.Concurrent
import Control.Exception
import Test.Tasty
import Test.Tasty.HUnit

main :: IO ()
main = defaultMain $
  testCase "test" $ do
    putStrLn "Sleeping..."
    threadDelay 100000000 `finally`
      ( do
          putStrLn "finally.1"
          threadDelay 1000000
          putStrLn "finally.2"
      )

Observed behaviors:

Ctrl+C ```console test: Sleeping... ^Cfinally.1 foo-test: SignalException 15 ```
Ctrl+C without second threadDelay ```console test: Sleeping... ^Cfinally.1 finally.2 ```
pkill -INT ```console test: Sleeping... finally.1 finally.2 foo> Test suite foo-test failed Test suite failure for package foo-0.1 foo-test: exited with: ExitFailure (-2) Logs printed to console ```
pkill -15 ```console test: Sleeping... finally.1 finally.2 foo-test: SignalException 15 foo> Test suite foo-test failed Test suite failure for package foo-0.1 foo-test: exited with: ExitFailure 1 Logs printed to console ```

This behavior also happens with unliftio's finally, which uses uninterruptibleMask. It also fails to clean up any bracket or withResource wrapping the entire test, e.g. cleanup will never be printed in the following snippet:

main = defaultMain $
  withResource (putStrLn "setup") (\_ -> putStrLn "cleanup") $ \_ ->
    -- same code as above

Changing the second threadDelay to threadDelay 1 does clean up, so it's not threadDelay itself, just anything taking a long time. In our case, connecting to a postgres database (e.g. with postgresql-libpq's connectStart function) in the finally block also shows this behavior.

This only happens with stack test. Manually compiling with ghc Main.hs && ./Main will clean up, and cabal test --test-show-details=streaming will exit immediately, but finish the cleanup in the background (as a zombie thread?).

Bodigrim commented 1 year ago

I'm afraid we can only be hold responsible for the behavior of a vanilla test executable. If stack test / cabal test perform some magic tricks to catch signals, the ball is in their court.

brandonchinn178 commented 1 year ago

Thanks @Bodigrim opened one here: https://github.com/commercialhaskell/stack/issues/6236