繁体   English   中英

如何防止QuickCheck捕获所有异常?

[英]How can I prevent QuickCheck from catching all exceptions?

QuickCheck库似乎捕获了测试属性时抛出的所有异常。 特别是,这种行为使我无法对整个QuickCheck计算设置时间限制。 例如:

module QuickCheckTimeout where

import System.Timeout (timeout)
import Control.Concurrent (threadDelay)
import Test.QuickCheck (quickCheck, within, Property)
import Test.QuickCheck.Monadic (monadicIO, run, assert)

-- use threadDelay to simulate a slow computation
prop_slow_plus_zero_right_identity :: Int -> Property
prop_slow_plus_zero_right_identity i = monadicIO $ do
  run (threadDelay (100000 * i))
  assert (i + 0 == i)

runTests :: IO ()
runTests = do
  result <- timeout 3000000 (quickCheck prop_slow_plus_zero_right_identity)
  case result of
    Nothing -> putStrLn "timed out!"
    Just _  -> putStrLn "completed!"

因为QuickCheck捕获所有异常, timeout中断:它实际上并不中止计算! 相反,QuickCheck将属性视为失败,并尝试缩小导致失败的输入。 然后,该缩小过程不会以时间限制运行,导致计算使用的总时间超过规定的时间限制。

有人可能会认为我可以within组合器中使用QuickCheck来限制计算时间。 within治疗的属性,如果它没有在给定的时间限制内完成混不下去。)然而, within并不完全做我想做的,因为快速检查仍试图收缩导致失败的输入,这个过程可能花了太长时间。 (对我来说可能的替代方案是within版本阻止QuickCheck尝试将输入缩小到失败的属性,因为它没有在给定的时间限制内完成。)

如何防止QuickCheck捕获所有异常?

由于当用户通过按Ctrl + C手动中断测试时,QuickCheck会做正确的事情,您可以通过编写类似于timeout来解决此问题,但是会抛出异步的UserInterrupt异常而不是自定义异常类型。

这几乎是来自System.Timeout源的直接复制粘贴作业:

import Control.Concurrent
import Control.Exception

timeout' n f = do
    pid <- myThreadId
    bracket (forkIO (threadDelay n >> throwTo pid UserInterrupt))
            (killThread)
            (const f)

使用这种方法,您将必须使用quickCheckResult并检查失败原因以检测测试是否超时。 它似乎足够好用:

> runTests 
*** Failed! Exception: 'user interrupt' (after 13 tests):  
16

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM