Skip to content

[#411] Display callstack in Nettest errors

Diogo Castro requested to merge diogo/#411-callstack into master

Description

Say we have a test like this:

test_Example :: TestTree
test_Example =
  nettestScenarioCaps "Example" $ do
    addr <- originateSimple "" 2 countdown
    call addr CallDefault ()
    call addr CallDefault ()
    call addr CallDefault ()
    call addr CallDefault ()
    call addr CallDefault ()


countdown :: Contract () Integer
countdown = defaultContract $
  cdr # push @Integer 1 # L.swap # sub # dup #
  ifLt0
    fail_
    (nil @Operation # pair)

When this test fails, all we get is an error message with little context, and in some cases it's impossible to tell which call was responsible for the error.

image

Personally, in order to diagnose the issue, my workflow is usually to sprinkle the code with comment/traceM, maybe comment out some things, but that's a poor dev experience.

With this MR, the tasty logs now show the full Haskell expression that lead to that error, and where in the source code it's located.

image

This is very similar to Hedghog's logs, with some notable differences:

  1. We show (by default) 5 lines before and after the error location, whereas Hedgehog is smarter and displays the full top-level declaration. We actually do not want hedgehog's behaviour, because we usually have lots of small tests embedded in a single top-level Tasty TestTree, and we don't want to display all of that when a single test fails.
  2. Hedgehog uses ansi-terminal to colourize its output (which tasty then overrides and just prints everything in red anyway 😞). We could implement this (in a way that cannot be overridden by tasty), but this MR is already big enough and it's a good improvement over what we have, so I'll leave this for some other time, it's not that important.
  3. Our implementation also displays the full callstack at the bottom, with the filepath/line/column. I think this is helpful because:
    1. in some editors/terminals, you can click on the filepath link and have the editor jump straight to that location in the source code.
    2. in more complex tests, where you have helper functions and layers of abstractions, an exception will have a callstack with multiple stack frames, but we'll only print the source code for the top-most frame. By printing the callstack as well, we can trace back the origin of the call and see exactly what happened.

Related issue(s)

Resolves part of #411

Checklist for your Merge Request

Related changes (conditional)

  • Tests (see short guidelines)

    • If I added new functionality, I added tests covering it.
    • If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again.
  • Documentation

    • I checked whether I should update the docs and did so if necessary:
    • I updated changelog files of all affected packages released to Hackage if my changes are externally visible.

Stylistic guide (mandatory)

Edited by Diogo Castro

Merge request reports