Haskell 如何使用快速检查调试发散测试

Haskell 如何使用快速检查调试发散测试,haskell,ghc,quickcheck,Haskell,Ghc,Quickcheck,我有一些使用Megaparsec的解析代码,我编写了一个简单的属性来测试(它生成一个随机表达式树,漂亮地打印它,然后检查结果是否解析回原始树) 不幸的是,似乎有一个bug,如果我无限制地运行测试,我会看到GHC进程分配越来越多的内存,直到我杀死它或者OOM杀手首先到达那里 没问题,我想。。。但我一辈子都搞不清楚是什么导致了这种分歧。属性本身看起来是这样的:(我已经删除了适当的测试和收缩代码,以尽量减少实际运行的代码) 我看到这个输出: STARTING TEST: "0" GOOD STARTI

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

不幸的是,似乎有一个bug,如果我无限制地运行测试,我会看到GHC进程分配越来越多的内存,直到我杀死它或者OOM杀手首先到达那里

没问题,我想。。。但我一辈子都搞不清楚是什么导致了这种分歧。属性本身看起来是这样的:(我已经删除了适当的测试和收缩代码,以尽量减少实际运行的代码)

我看到这个输出:

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
启动测试:“0”
好
启动测试:“(x[(!0)])
正在启动测试:({2{(!0}})”
开始测试:“{2{(x[0{(0),(x)}:((0)?(x):0)-:((^(x))]),0**(x)}”
开始测试:“(0)(x[({1{(0)→x:(0)})]:(&(0)→x:((x)^(x)))
启动测试:“(0)”
启动测试:“0”
^C***异常(由于+RTS-xc而报告):(THUNK_静态),堆栈跟踪:
Test.Framework.Improving.runImprovingIO、,
从Test.Framework.Providers.QuickCheck2.runProperty调用,
从Test.Framework.Providers.QuickCheck2.runTest调用,
从Test.Framework.Runners.Core.runSimpleTest调用,
从Test.Framework.Runners.Core.runTestTree.go调用,
从Test.Framework.Runners.Core.runTestTree调用,
从Test.Framework.Runners.Core.runTests'调用,
从Test.Framework.Runners.Core.runTests调用,
从Test.Framework.Runners.Console.defaultMainWithOpts调用,
从Test.Framework.Runners.Console.defaultMainWithArgs调用,
从Test.Framework.Runners.Console.defaultMain调用,
从Main.Main调用
***异常(由于+RTS-xc而报告):(THUNK_静态),堆栈跟踪:
Test.Framework.Runners.Console.Utilities.hidecursorsorduring、,
从Test.Framework.Runners.Console.Run.showRunTestsTop调用,
从Test.Framework.Improving.runImprovingIO调用,
从Test.Framework.Providers.QuickCheck2.runProperty调用,
从Test.Framework.Providers.QuickCheck2.runTest调用,
从Test.Framework.Runners.Core.runSimpleTest调用,
从Test.Framework.Runners.Core.runTestTree.go调用,
从Test.Framework.Runners.Core.runTestTree调用,
从Test.Framework.Runners.Core.runTests'调用,
从Test.Framework.Runners.Core.runTests调用,
从Test.Framework.Runners.Console.defaultMainWithOpts调用,
从Test.Framework.Runners.Console.defaultMainWithArgs调用,
从Test.Framework.Runners.Console.defaultMain调用,
从Main.Main调用
谁能告诉我:

  • 为什么我看到多个
    开始测试
    行之间没有
    GOOD
    NOPE
    ,尽管有-j1

  • 我如何得到一个实际的堆栈跟踪,显示测试在哪里分配所有内存


  • 谢谢你的建议

    对于任何发现这个问题的人来说,我的代码的问题是,我的表达式的
    任意
    实例没有正确地约束大小,因此有时会尝试生成巨大的树。请参阅的“生成递归数据类型”部分,了解我应该做的事情

    我发现运行以下命令:

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

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

    对于任何发现这个问题的人来说,我的代码的问题是,我的表达式的
    任意
    实例没有正确地约束大小,因此有时会尝试生成巨大的树。请参阅的“生成递归数据类型”部分,了解我应该做的事情

    我发现运行以下命令:

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

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

    跟踪“GOOD”True
    没有自由变量;它很可能被优化并重新定义为一个全局常量,因此您最多只能看到它打印一次。堆栈跟踪中没有提到您的库,这有点可疑。现在您有了一个反例,您可能想尝试将其隔离在一个独立的可执行文件中。。。感谢您发现跟踪问题。今晚我将再次仔细地看一看这个问题。
    trace“GOOD”True
    没有自由变量;它很可能被优化并重新定义为一个全局常量,因此您最多只能看到它打印一次。堆栈跟踪中没有提到您的库,这有点可疑。现在您有了一个反例,您可能想尝试将其隔离在一个独立的可执行文件中。。。感谢您发现跟踪问题。今晚我会再好好地看一眼。
    ./long/path/to/foo-test -o3 +RTS -xc