Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Out of stack space error in IE #502

Open
satazor opened this Issue · 22 comments
@satazor

Large amounts of tests often cause "out of stack space" errors in Internet Explorer (IE <= 9).
After analyzing the call stack when the error is thrown, it is huge!

If I let the tests "breath":

  process.nextTick(function(){
    setTimeout(function () {
      next(0);
    }, 5);
  });

.. all is fine.

The project in which this is happening has 258 asserts and a total of 102 tests.

@liammclennan

I get this error too. The specific error is: " Unable to get value of the property 'appendChild': object is null or undefined" thrown from runner.on('test end':

stack[0].appendChild(el);

for some reason stack is []

@satazor

@liammclennan that also happens to me sometimes after I get the stack space error on IE and refresh

@derickbailey

we're getting this error in IE10 as well.

does anyone have any clue what's going on? is this a design problem in mocha using recursion to run tests, under a limited stack frame environment? or ???

any help is appreciated, as we are running in to what looks like a limit of around 20 it blocks.

the problem we're seeing is that the tests complete (pass or fail), and then trying to report the problem fails at line 1753:

stack[0].appendChild(el);

stack on this line is an empty array.

@OscarGodson

Uh oh. EpicEditor is nearing over 100 tests as well and @johnmdonahue and I were going to switch over to Mocha. I'll be keeping an eye on this ticket.

@derickbailey

i've spent the better part of the day trying to track this problem down, and here's what i've come up with: Mocha uses recursion to run the specs.

I'm seeing the Runner.runSuite called over and over and over again. I have a test suite with 8 file, 13 "describe" blocks, 22 "it" blocks. By the time it gets down to the last of the it blocks in the last of the describe blocks, there are 535 items on the JavaScript call stack (that is, 535 stack frames).

It looks like the stack is reset at the beginning of each file, some times... but at other times it looks like it is a cumulative stack for all files. It's quite confusing and I'm having a hard time understanding what circumstances would cause such a deep stack trace, especially when the file that finally runs in to this problem only had 3 "describe" and 6 "it" blocks in it.

trying to dig in to this further, but any help from @visionmedia would be greatly appreciated. i don't want to have to go back to jasmine with it's awful "waitsFor" and "runs" methods for async support.

@tj
Owner
tj commented

that's why we have some next ticks in there to kill the stack, we just need a few more I guess

@tj
Owner
tj commented

I haven't seen this myself personally though, maybe im not writing enough tests haha

@derickbailey

next update...

i figured out that the scenario in which i thought recursion was due to a new file, was due to a suite of async tests and them not completing before some synchronous tests.

that made me thing: a deep stack trace can be cut by using any async call to push something to the back of the javascript timer/queue (whatever that's called). a simple setTimeout should do that...

The Workaround

At the beginning of every .spec file, just inside of your wrapping describe block, add an async beforeEach and do a setTimeout(done, 0).

describe("the contents of this file", function(){

  // blow away the stack trace here
  beforeEach(function(done){
    setTimeout(done, 0);
  });

  // do my tests here
  describe("whatever", function(){
    // ...
  });

  // do my tests here
  describe("whatever", function(){
    // ...
  });

  // do my tests here
  describe("whatever", function(){
    // ...
  });


});

The potential problem with this workaround, is that if you have a large spec file with many "describe" and "it" blocks in it, or a deep stack trace due to your own code, you can still run in to this problem. The continued workaround for that, though, is to call setTimeout(done, 0) multiple times - either within the same file, or splitting multiple specs in to multiple files.

At least this is a workaround for now, as ugly as it is.

Can we get a legit fix to this, though? The specs shouldn't be called recursively, IMO. Is there a reason that it was designed this way?

@derickbailey

an even better work around:

create a helper.js file (or open the one you already have) and just put the beforeEach block with the setTimeout(done,0) in that directly, with no describe around it.

this way you only have to do it once, not in every file. it will get run before all of your specs and you'll never have to worry about this again.

@tj
Owner
tj commented

nothing is wrong with recursion at all, we just need to drop the stack every so often, recursion simplifies many things internally

@derickbailey

nothing is wrong with recursion at all, we just need to drop the stack every so often, recursion simplifies many things internally

while i agree with the first part of that on it's own, when the qualification of the second part is a necessity, then recursion is not an appropriate solution for the situation.

... but... yeah :) a fix in Mocha would be awesome. the setTimeout trick works for now.

@tj
Owner
tj commented

when you can nextTick in a tiny fraction of a second it's not an issue, implementing actions against a tree like we have here would get very messy when combined with async callback crap all over, if we used coroutines then yeah maintaining our own stack would be fine

@satazor

@derickbailey thanks for the quick fix, gonna apply that until this issue is resolved

@molily

Any plans to fix this soon? This seems to kill our test suite in IE8 completely. The IE process takes 1.5 GB of memory and then fails with an out of memory error. The setTimeout workaround didn’t help. I know no-one here cares about old IEs, but any real-world client-side JavaScript has to.

@tj
Owner
tj commented

I dont have any windows vms going so I can't test it unfortunately, not any time soon at least. When I get my old MBP fixed I'll chuck ugly old windows on there

@arian

The workaround fixed the tests in IE (kamicane/prime@762be12)

@vmeurisse

visionmedia commented:
that's why we have some next ticks in there to kill the stack, we just need a few more I guess

The problem is that process.nextTick doesn't kill the stack in IE. Here is an implementation which does:

process.nextTick = (function(){
  var timeouts = []
  // postMessage behaves badly on IE8
  if (window.ActiveXObject || !window.postMessage) {
    return function(fn){
      timeouts.push(fn);
      setTimeout(function(){
        if (timeouts.length) timeouts.shift()();
      }, 0);
    }
  }

  // based on setZeroTimeout by David Baron
  // - http://dbaron.org/log/20100309-faster-timeouts
  var name = 'mocha-zero-timeout'

  window.addEventListener('message', function(e){
    if (e.source == window && e.data == name) {
      if (e.stopPropagation) e.stopPropagation();
      if (timeouts.length) timeouts.shift()();
    }
  }, true);

  return function(fn){
    timeouts.push(fn);
    window.postMessage(name, '*');
  }
})();

It also fix issue #737.

@jvilk

I found this while Googling for an appropriate mechanism for resetting IE8's stack for one of my projects.

While setTimeout works, it has a minimum wait time (4ms in the spec, but apparently older browsers had something like 10ms of delay). This is probably fine for tests, but less than ideal for actual applications that have to call this method often.

If anyone figures out a way to clear the stack in IE8 and trigger the callback immediately, let me know. Conversely, if I find anything, I'll update here.

@jvilk

I found this project:
https://github.com/NobleJS/setImmediate

They have a solution for IE6-8, which is quite the hack:
https://github.com/NobleJS/setImmediate/blob/master/setImmediate.js#L161

It inserts a script into the page with a onreadystatechange callback which triggers the task. Totally hacky, but... I'll see if it ends up working for me.

@lambdalisue lambdalisue referenced this issue in lambdalisue/femto
Closed

Out of stack space error in IE #7

@OscarGodson

BALLS. As I called it, this is now happening to me in IE9 as well as 10. :(

@jamuhl jamuhl referenced this issue in i18next/i18next
Closed

Get value of key in IE7/8 #115

@npmcomponent npmcomponent referenced this issue from a commit in npmcomponent/oxyc-luaparse
@oxyc oxyc Make tests run in older IE
Due to mochas recursion IE tests fail due to `out of stack space`.
mochajs/mocha#502
0bdd95b
@twpayne twpayne referenced this issue in openlayers/ol3
Closed

Testing framework (Mocha) fails on IE9 #1634

@lawnsea lawnsea referenced this issue from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.