elm-explorations / test

Write unit and fuzz tests for Elm code.
https://package.elm-lang.org/packages/elm-explorations/test/latest
BSD 3-Clause "New" or "Revised" License
236 stars 40 forks source link

“Maximum call stack size exceeded” on comparison of large data structures #22

Open MazeChaZer opened 6 years ago

MazeChaZer commented 6 years ago

I stumbled across this crash when I migrated one of my Elm projects to 0.19. This is the test I wrote with Elm 0.18, where it worked without problems: https://gitlab.com/MazeChaZer/knobster/blob/8d39fa8de9f5744cdaff45755b23050f4d9d06e0/tests/GridTest.elm

elm: 0.19.0 elm-explorations/test: 1.1.0 node-test-runner: 0.19.0-beta8

SSCCE:

module Example exposing (suite)

import Expect
import Test exposing (..)

suite : Test
suite =
    test "foo bar" <|
        \() ->
            Expect.equal
                [ [ { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  ]
                , [ { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  ]
                , [ { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  ]
                , [ { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  ]
                , [ { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  ]
                , [ { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  , { foo = "foo", bar = "bar" }
                  ]
                ]
                []

Output:

$ elm-test
Success!
Success! Compiled 1 module.

elm-test 0.19.0-beta8
---------------------

Running 1 test. To reproduce these results, run: elm-test --fuzz 100 --seed 195384286911629

[project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3665
                        var change = function () {
                                              ^

RangeError: Maximum call stack size exceeded
    at [project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3665:26
    at Function.f ([project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3707:5)
    at A5 ([project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:113:28)
    at [project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3711:13
    at Function.f ([project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3648:11)
    at A2 ([project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:104:28)
    at Function.f ([project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3708:11)
    at A5 ([project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:113:28)
    at [project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3711:13
    at Function.f ([project]/elm-stuff/generated-code/elm-explorations/test/elmTestOutput.js:3648:11)

There was an unexpected runtime exception while running tests

A normal == comparison is no problem with this data structure: https://ellie-app.com/3fQ2FSpkxYca1

MazeChaZer commented 6 years ago

As a workaround I only verified a subset of the data structure, which works fine so this isn't a blocker for me.

stephenreddek commented 6 years ago

You can also try running your tests with an increased stack size:node --stack-size=65500 node_modules/.bin/elm-test

mbylstra commented 6 years ago

I ran into this problem when I was comparing a large Model data structure. However, I really just needed to check a single field of the model, not the entire model.

Passing a record accessor function in the pipeline like this:

model
|> doSomeUpdates 
|> .myField
|> Expect.equal
    "something simple"

...avoided the stack size exception and resulted in a readable failing test diff. You might not always be able to simplify comparisons like this, but it's worth asking yourself why you need to compare large data structures when testing.