karma-runner/grunt-karma

Running with "background" set to true raises error "Cannot read property 'write' of null"

Opened this issue · 2 comments

Environment: Windows 7 x64, Node.js 4.2.3, grunt-karma 0.12.1, karma 0.13.15.

Recently upgraded from Node.js 0.12.x, grunt-karma ^0.10.1, karma ^0.12.31

We have two different grunt tasks - the first that runs karma in a single-run/foreground setup that is used for CI, etc, and the second in a more "watch/live-reload" style setup.

The two tasks have almost identical setups, with the only difference being around the singleRun and background settings, and it is changing the background setting that raises the issue. When background is set to true, the task fails with the following errors:

TypeError: Cannot read property 'write' of null
    at Object.<anonymous> (C:\project\node_modules\grunt-karma\tasks\grunt-karma.js:118:30)
    at Object.<anonymous> (C:\project\node_modules\grunt\lib\grunt\task.js:264:15)
    at Object.thisTask.fn (C:\project\node_modules\grunt\lib\grunt\task.js:82:16)
    at Object.<anonymous> (C:\project\node_modules\grunt\lib\util\task.js:301:30)
    at Task.runTaskFn (C:\project\node_modules\grunt\lib\util\task.js:251:24)
    at Task.<anonymous> (C:\project\node_modules\grunt\lib\util\task.js:300:12)
    at C:\project\node_modules\grunt\lib\util\task.js:227:11
    at doNTCallback0 (node.js:419:9)
    at process._tickCallback (node.js:348:13)

This appears to be an issue with passing the parameters across stdin to the background process.

A second after the above error there is a second error logged to the console, which seems to be the failure on the other side of this:

TypeError: Cannot read property 'logLevel' of null
    at new Server (C:\project\node_modules\karma\lib\server.js:52:25)
    at ReadStream.<anonymous> (C:\project\node_modules\grunt-karma\lib\background.js:4:16)
    at emitNone (events.js:67:13)
    at ReadStream.emit (events.js:166:7)
    at emitReadable_ (_stream_readable.js:411:10)
    at emitReadable (_stream_readable.js:405:7)
    at ReadStream.Readable.read (_stream_readable.js:268:7)
    at ReadStream.Socket.read (net.js:283:43)
    at nReadingNextTick (_stream_readable.js:693:8)
    at doNTCallback1 (node.js:430:9)
    at process._tickCallback (node.js:352:17)
    at Function.Module.runMain (module.js:469:11)
    at startup (node.js:136:18)
    at node.js:963:3

(I'm a member of Ian's team)

I believe I've tracked this down to a case of dependency hell. We use the grunt-citare-scriptum module, which is written in CoffeeScript. CoffeeScript itself intercepts child_process.fork.

It appears that that interceptor doesn't allow the args parameter to be omitted. It works correctly if an empty array is passed for the args parameter:

    // allow karma to be run in the background so it doesn't block grunt
    if (data.background) {
      var backgroundProcess = require('child_process').fork(
          path.join(__dirname, '..', 'lib', 'background.js'),
          [], // must provide 'args' or CoffeeScript's intercepted fork won't pass it to Node
          { silent: true }
      )

I don't think that communicating with the background process via stdin is the best approach. Node.js provides a private communication channel between the parent and child processes, using child.send. I've implemented this myself and can provide a pull request if you're interested?

@MikeDimmick thanks for the update. Yes I would be very interested in improving this, so would love to see a PR