【问题标题】:QuickCheck test randomly hangsQuickCheck 测试随机挂起
【发布时间】:2013-08-12 20:43:30
【问题描述】:

我是 Haskell 的新手。我正在玩 QuickCheck 测试,尝试测试一个简单的函数calculateStrengthSingle(请参阅下面的测试者来源)

# Fighter.hs
module Fighter
( Quantity(Quantity)
, Fighter(TeamPlayer, LoneWolf, Polymorph)
, Strength(Strength)
, calculateStrengthSingle)
where

import System.Random

data Strength = Strength Double

instance Eq Strength where
    Strength s1 == Strength s2 =
        s1 == s2

instance Ord Strength where
    Strength s1 < Strength s2 =
        s1 < s2

data Quantity = Quantity Int deriving (Show)

instance Random Quantity where
    randomR (Quantity lo, Quantity hi) g =
        let rand = randomR (lo, hi) g
            (r, g1) = rand
        in (Quantity r, g1)
    random g =
        let rand = random g
            (r, g1) = rand
        in (Quantity r, g1)

data Fighter = TeamPlayer
             | LoneWolf
             | Polymorph
    deriving (Show)


calculateStrengthSingle :: Quantity -> Fighter -> Strength
calculateStrengthSingle (Quantity number) TeamPlayer =
    Strength(log (fromIntegral number))
calculateStrengthSingle _ _ = Strength 0.0

测试看起来像这样

# TestFighter.hs
import qualified Test.QuickCheck as QuickCheck
import Fighter

prop_StrengthPositive quantity fighter =
    Fighter.calculateStrengthSingle quantity fighter >= Strength 0.0

instance QuickCheck.Arbitrary Fighter.Fighter where
    arbitrary = QuickCheck.oneof([return Fighter.TeamPlayer, return Fighter.LoneWolf, return Fighter.Polymorph])

instance QuickCheck.Arbitrary Fighter.Quantity where
    arbitrary = QuickCheck.choose(Fighter.Quantity 1, Fighter.Quantity 10)

main :: IO()
main = do
    QuickCheck.quickCheck prop_StrengthPositive

当我执行runhaskell TestFighter.hs 时,输出(1 test)(数字在变化,有时是0,有时是4)并且CPU 100% 加载。一分钟左右什么都没有发生。当我通过Ctrl+C 中断程序时,它会吐出类似

^C*** Failed! Exception: 'user interrupt' (after 1 test):  
Quantity 2
TeamPlayer

问题:

  1. 我在哪里搞砸了?
  2. 如何调试像这样的无限计算案例?

【问题讨论】:

    标签: haskell quickcheck


    【解决方案1】:

    您没有为Strength 正确定义Ord 实例。您需要定义&lt;= 而不是&lt;

    仅定义&lt; 时,函数&lt;= 进入无限循环,因为它是根据compare 定义的,而compare 是根据&lt;= 定义的。最小定义需要定义compare&lt;=

    这是Ord实例的固定代码

    instance Ord Strength where
        Strength s1 <= Strength s2 =
            s1 <= s2
    

    【讨论】:

    • 调试这些东西怎么样?我怎样才能弄清楚我错过了什么?
    • 没有通用的方法。我通常做的是使用verboseCheck 而不是quickCheck 来查看它失败的测试是什么,然后尝试手动完成测试。这就是我发现 Ord 实例是你案件的罪魁祸首的原因。
    【解决方案2】:

    您可以尝试使用-o 1 作为参数运行测试,这会导致一秒后超时。然后它可以显示导致程序挂起的输入。它并不能准确地告诉你问题出在哪里,但它是一个好的开始。

    编辑:这就是我使用 cabal 测试框架的方式

    test-Suite runTests
       hs-source-dirs:      src, test
       type:                exitcode-stdio-1.0
       main-is:             RunTests.hs
       other-modules:
                           <...>
    
      build-depends:       base >= 3 && < 5,
                           HUnit,
                           test-framework,
                           test-framework-th,
                           test-framework-hunit,
                           test-framework-quickcheck2,
                           QuickCheck,
    

    为了在我的描述中完整,我使用:

    cabal-dev install --enable-tests
    

    【讨论】:

    • 在我的 GHC 中,-o 正在设置文件名,而不是超时
    • 对不起,我应该更具体一些。我使用 -o 1 作为运行时参数,但我也使用了带有测试框架的 quickcheck,所以这里的选项之间可能存在差异。 hackage.haskell.org/package/test-framework
    • 如果你想走测试框架的道路,看看 test-framework-th,这样可以最大限度地减少打字。
    • 也许您也可以给我链接一个将test-frameworkcabal test 集成的方法?好像cabal自己只支持HUnit和QuickCheck,而不是统一的test-framework
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多