Skip to content

Sydtest exception handling: crash / lock / ... #80

@guibou

Description

@guibou

The following test suite:

  describe "behavior with exceptions" $ do
      beforeAll (fail "error in beforeAll") $ do
        it "should not kill the test suit" $ do
          1 `shouldBe` 1

Will crash the complete test suite:

sydtest-test: user error (error in beforeAll)

Replace beforeAll by before and you'll get:

Tests:

Test.Syd.AroundSpec
  behavior with exceptions
    before_
      ✗ should not kill the test suit                                      0.04 ms
        Retries: 3 (does not look flaky)
 
Failures:

    test/Test/Syd/AroundSpec.hs:139
  ✗ 1 Test.Syd.AroundSpec.behavior with exceptions.before_.should not kill the test suit
      Retries: 3 (does not look flaky)
      user error (error in beforeAll)
  
 
  Examples:                     3
  Passed:                       0
  Failed:                       1
  Sum of test runtimes:         0.00 seconds
  Test suite took:              0.00 seconds

Using the following changes:

diff --git a/sydtest/src/Test/Syd/Runner/Asynchronous.hs b/sydtest/src/Test/Syd/Runner/Asynchronous.hs
index 413c354..4a9c436 100644
--- a/sydtest/src/Test/Syd/Runner/Asynchronous.hs
+++ b/sydtest/src/Test/Syd/Runner/Asynchronous.hs
@@ -37,6 +37,7 @@ import Test.Syd.Runner.Single
 import Test.Syd.SpecDef
 import Test.Syd.SpecForest
 import Text.Colour
+import Debug.Trace (traceShow)
 
 runSpecForestAsynchronously :: Settings -> Word -> TestForest '[] () -> IO ResultForest
 runSpecForestAsynchronously settings nbThreads testForest = do
@@ -236,15 +237,21 @@ runner settings nbThreads failFastVar handleForest = do
           DefSetupNode func sdf -> do
             liftIO func
             goForest sdf
-          DefBeforeAllNode func sdf -> do
-            b <- liftIO func
+          DefBeforeAllNode func sdf -> traceShow "Asynchronous" $ do
+            bM <- liftIO $ try $ func
+            let b = case bM of 
+                       Right b -> b
+                       Left (e :: SomeException) -> error (show e)
             withReaderT
               (\e -> e {eExternalResources = HCons b (eExternalResources e)})
               (goForest sdf)
           DefBeforeAllWithNode func sdf -> do
             e <- ask
             let HCons x _ = eExternalResources e
-            b <- liftIO $ func x
+            bM <- liftIO $ try $ func x
+            let b = case bM of 
+                       Right b -> b
+                       Left (e :: SomeException) -> error (show e)
             liftIO $
               runReaderT
                 (goForest sdf)

The following test suites now pass:

  describe "behavior with exceptions" $ do
    describe "beforeAll" $ do
      beforeAll (fail "error in beforeAll") $ do
        it "should not kill the test suit" $ do
          1 `shouldBe` 1
      beforeAll (fail "error in beforeAll") $ do
        itWithOuter "should not kill the test suit" $ \outer ->
          outer `shouldBe` 1
        beforeAllWith (\x -> fail "error in beforeAllWith") $ do
          itWithOuter "should not kill the test suit" $ \outer ->
             outer `shouldBe` 1

With the following result:

Test.Syd.AroundSpec
  behavior with exceptions
    beforeAll
      ✓ should not kill the test suit                                      0.00 ms
      ✗ should not kill the test suit                                      0.02 ms
        Retries: 3 (does not look flaky)
      ✗ should not kill the test suit                                      0.00 ms
        Retries: 3 (does not look flaky)
 
Failures:

    test/Test/Syd/AroundSpec.hs:142
  ✗ 1 Test.Syd.AroundSpec.behavior with exceptions.beforeAll.should not kill the test suit
      Retries: 3 (does not look flaky)
      user error (error in beforeAll)
      CallStack (from HasCallStack):
        error, called at src/Test/Syd/Runner/Asynchronous.hs:244:53 in sydtest-0.15.1.1-inplace:Test.Syd.Runner.Asynchronous
  
    test/Test/Syd/AroundSpec.hs:145
  ✗ 2 Test.Syd.AroundSpec.behavior with exceptions.beforeAll.should not kill the test suit
      Retries: 3 (does not look flaky)
      user error (error in beforeAllWith)
      CallStack (from HasCallStack):
        error, called at src/Test/Syd/Runner/Asynchronous.hs:254:53 in sydtest-0.15.1.1-inplace:Test.Syd.Runner.Asynchronous
  
 
  Examples:                     7
  Passed:                       1
  Failed:                       2
  Sum of test runtimes:         0.00 seconds
  Test suite took:              0.00 seconds

A few notes:

  • My understanding of the behavior between before and beforeAll is that the code for before is executed during the it execution, when the code for beforeAll is executed during the tree walking.
  • I've found a few places where similar changes shoud have to be implemented:
	modified:   src/Test/Syd/Runner/Asynchronous.hs
	modified:   src/Test/Syd/Runner/Synchronous/Interleaved.hs
	modified:   src/Test/Syd/Runner/Synchronous/Separate.hs
  • The idea of the change is to sneak the exception lazyly in the ressource. Another option could be to, in case of exception, just walk the tree and report failures.
  • Async exception should be handled instead of SomeException for proper excption handling.

So my question is:

  • Are you interested by an MR doing this change on the three runners
  • Or is this completly stupid and I miss the obvious point.

Thank you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions