如何使用QuickCheck调试发散测试



我有一些使用Megaparsec的解析代码,我已经编写了一个简单的属性来测试(它生成一个随机表达式树,漂亮地打印它,然后检查结果是否解析回原始树(。

不幸的是,似乎有一个错误,如果我在没有任何限制的情况下运行测试,我会看到GHC进程分配越来越多的内存,直到我杀死它或OOM杀手首先到达那里。

没问题,我想。。。但我一辈子都搞不清楚是什么导致了分歧。属性本身看起来是这样的:(我去掉了正确的测试和收缩的代码,试图最大限度地减少实际运行的代码(

prop_parse_expr :: Property
prop_parse_expr =
forAll arbitrary $
( pe ->
let str = prettyParExpr 0 pe in
counterexample ("Rendered to: " ++ show str) $
trace ("STARTING TEST: " ++ show str) $
case parse (expr <* eof) "" str of
Left _ -> trace "NOPE" $ False
Right _ -> trace "GOOD" $ True)

如果我使用评测(使用stack test --profile(进行编译,我可以使用RTS选项运行生成的二进制文件。Ahah,我想,并使用-xc运行,认为当我向卡住的作业发送SIGINT时,我会得到一个有用的堆栈跟踪。似乎不是。使用运行

./long/path/to/foo-test -j1 --test-seed 1 +RTS -xc

我看到这个输出:

STARTING TEST: "0"
GOOD
STARTING TEST: "(x [( !0 )]) "
STARTING TEST: "({ 2 {( !0 )}} ) "
STARTING TEST: "{ 2{ ( x[0? {( 0) ,( x ) } :((0 )? (x ):0) -: ( -(^( x  )) ) ]), 0**( x )} } "
STARTING TEST: "| (0? (x[({ 1{ (0)? x : ( 0 ) }} ) ]) :(~&( 0) ?( x):( (x ) ^( x ) )))"
STARTING TEST: "(0 )"
STARTING TEST: "0"
^C*** Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace: 
Test.Framework.Improving.runImprovingIO,
called from Test.Framework.Providers.QuickCheck2.runProperty,
called from Test.Framework.Providers.QuickCheck2.runTest,
called from Test.Framework.Runners.Core.runSimpleTest,
called from Test.Framework.Runners.Core.runTestTree.go,
called from Test.Framework.Runners.Core.runTestTree,
called from Test.Framework.Runners.Core.runTests',
called from Test.Framework.Runners.Core.runTests,
called from Test.Framework.Runners.Console.defaultMainWithOpts,
called from Test.Framework.Runners.Console.defaultMainWithArgs,
called from Test.Framework.Runners.Console.defaultMain,
called from Main.main
<snip: 2 more identical backtraces>
*** Exception (reporting due to +RTS -xc): (THUNK_STATIC), stack trace: 
Test.Framework.Runners.Console.Utilities.hideCursorDuring,
called from Test.Framework.Runners.Console.Run.showRunTestsTop,
called from Test.Framework.Improving.runImprovingIO,
called from Test.Framework.Providers.QuickCheck2.runProperty,
called from Test.Framework.Providers.QuickCheck2.runTest,
called from Test.Framework.Runners.Core.runSimpleTest,
called from Test.Framework.Runners.Core.runTestTree.go,
called from Test.Framework.Runners.Core.runTestTree,
called from Test.Framework.Runners.Core.runTests',
called from Test.Framework.Runners.Core.runTests,
called from Test.Framework.Runners.Console.defaultMainWithOpts,
called from Test.Framework.Runners.Console.defaultMainWithArgs,
called from Test.Framework.Runners.Console.defaultMain,
called from Main.main

有人能告诉我吗:

  1. 为什么我看到多个STARTING TEST行之间没有GOODNOPE,尽管有-j1?

  2. 如何获得实际的堆栈跟踪,以显示测试将所有内存分配到哪里?

谢谢你的想法!

对于任何发现这个问题的人来说,我的代码的问题是我的表达式的arbitrary实例没有正确地约束大小,所以有时试图制作巨大的树。请参阅QuickCheck手册的"生成递归数据类型"部分,了解我应该做的事情!

我发现运行命令如:

./long/path/to/foo-test -o3 +RTS -xc

帮助我弄清楚发生了什么。奇怪的是,回溯仍然显示了几个执行线程。我真的不明白为什么,但至少我当时可以看到我在"makeAnExpr"函数中花费了时间。诀窍是调整超时(3秒以上(,这样它就不会在测试完全卡住之前杀死测试,但会在它开始吃掉你所有的RAM之前停止它!

相关内容

  • 没有找到相关文章

最新更新