[Pkg-javascript-commits] [node-tap] 46/186: Unit test for t.spawn, and fix the bugs it found

Bastien Roucariès rouca at moszumanska.debian.org
Fri Dec 1 16:40:42 UTC 2017


This is an automated email from the git hooks/post-receive script.

rouca pushed a commit to branch master
in repository node-tap.

commit e4a4f90b4ee47476d522e4e407a5a5b3e94be575
Author: isaacs <i at izs.me>
Date:   Mon Oct 30 00:26:11 2017 -0700

    Unit test for t.spawn, and fix the bugs it found
---
 lib/base.js                           |   1 -
 lib/spawn.js                          |  39 ++++----
 lib/stack.js                          |   3 +-
 scripts/generate-test-test.js         |   1 +
 test/test/mochalike--bail--buffer.tap |   2 +-
 test/test/mochalike--bail.tap         |   2 +-
 test/test/mochalike--buffer.tap       |   6 +-
 test/test/mochalike.js                |   3 +-
 test/test/mochalike.tap               |   6 +-
 unit/spawn.js                         | 179 ++++++++++++++++++++++++++++++++++
 10 files changed, 213 insertions(+), 29 deletions(-)

diff --git a/lib/base.js b/lib/base.js
index e0ca317..184eff2 100644
--- a/lib/base.js
+++ b/lib/base.js
@@ -191,7 +191,6 @@ class Base extends MiniPass {
       omitVersion: this.omitVersion,
       preserveWhitespace: this.preserveWhitespace
     })
-    assert(this.parser.preserveWhitespace)
     this.parser.on('line', this.online.bind(this))
     this.parser.once('bailout', this.onbail.bind(this))
     this.parser.on('complete', this.oncomplete.bind(this))
diff --git a/lib/spawn.js b/lib/spawn.js
index f48c83e..49c5d6d 100644
--- a/lib/spawn.js
+++ b/lib/spawn.js
@@ -6,10 +6,7 @@ const util = require('util')
 const ownOr = require('own-or')
 const path = require('path')
 const cleanYamlObject = require('./clean-yaml-object.js')
-
-
 const cp = require('child_process')
-const spawn = cp.spawn
 
 class Spawn extends Base {
   constructor (options) {
@@ -32,6 +29,7 @@ class Spawn extends Base {
       this.stdio = [ 0, 'pipe', 2 ]
 
     this.stdio[1] = 'pipe'
+    options.stdio = this.stdio
     const env = options.env || process.env
     this.env = Object.assign({}, env)
 
@@ -60,7 +58,7 @@ class Spawn extends Base {
     if (this.proc)
       this.proc.kill('SIGKILL')
     this.parser.abort('test unfinished')
-    this.cb()
+    this.callCb()
   }
 
   main (cb) {
@@ -72,7 +70,7 @@ class Spawn extends Base {
       stdio: this.stdio
     }, this.options)
     try {
-      const proc = this.proc = spawn(this.command, this.args, options)
+      const proc = this.proc = cp.spawn(this.command, this.args, options)
       proc.stdout.pipe(this.parser)
       proc.on('close', this.onprocclose.bind(this))
       proc.on('error', this.threw.bind(this))
@@ -81,6 +79,12 @@ class Spawn extends Base {
     }
   }
 
+  callCb () {
+    if (this.cb)
+      this.cb()
+    this.cb = null
+  }
+
   threw (er, extra, proxy) {
     extra = Base.prototype.threw.call(this, er, extra, proxy)
     extra = cleanYamlObject(extra)
@@ -92,7 +96,7 @@ class Spawn extends Base {
       this.proc.removeAllListeners('close')
       this.proc.kill('SIGKILL')
     }
-    this.cb()
+    this.callCb()
   }
 
   onprocclose (code, signal) {
@@ -100,7 +104,6 @@ class Spawn extends Base {
     this.options.exitCode = code
     if (signal)
       this.options.signal = signal
-    this.results = this.results || {}
 
     // spawn closing with no tests is treated as a skip.
     if (this.results.plan && this.results.plan.skipAll && !code && !signal)
@@ -110,20 +113,22 @@ class Spawn extends Base {
       this.results.ok = false
       this.parser.ok = false
     }
-    return this.cb()
+    return this.callCb()
   }
 
   timeout (extra) {
-    if (this.proc)
+    if (this.proc) {
       this.proc.kill('SIGTERM')
-    const t = setTimeout(() => {
-      if (!this.options.signal && this.options.exitCode === undefined) {
-        Base.prototype.timeout.call(this, extra)
-        this.proc.kill('SIGKILL')
-      }
-    }, 1000)
-    if (t.unref)
-      t.unref()
+      const t = setTimeout(() => {
+        if (!this.options.signal && this.options.exitCode === undefined) {
+          Base.prototype.timeout.call(this, extra)
+          this.proc.kill('SIGKILL')
+        }
+      }, 1000)
+      /* istanbul ignore else */
+      if (t.unref)
+        t.unref()
+    }
   }
 }
 
diff --git a/lib/stack.js b/lib/stack.js
index fcd132f..5c8e511 100644
--- a/lib/stack.js
+++ b/lib/stack.js
@@ -3,6 +3,7 @@ const sourceMapSupport = require('source-map-support')
 const StackUtils = require('stack-utils')
 const path = require('path')
 const tapDir = path.resolve(__dirname, '..')
+const osHomedir = require('os-homedir')
 
 const resc = str =>
   str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')
@@ -15,7 +16,7 @@ const skip = (process.cwd() !== tapDir ||
   +process.env.TAP_DEV_LONGSTACK !== 1
 ? [
     /node_modules[\/\\]tap[\/\\]/,
-    new RegExp(resc(path.resolve(process.env.HOME, '.node-spawn-wrap-')) + '.*'),
+    new RegExp(resc(path.resolve(osHomedir(), '.node-spawn-wrap-')) + '.*'),
     new RegExp(resc(tapDir) + '\\b', 'i'),
     new RegExp(resc(require.resolve('function-loop'))),
     new RegExp(resc(path.dirname(require.resolve('bluebird/package.json'))))
diff --git a/scripts/generate-test-test.js b/scripts/generate-test-test.js
index 0a55d3c..dc47975 100644
--- a/scripts/generate-test-test.js
+++ b/scripts/generate-test-test.js
@@ -133,6 +133,7 @@ function yamlishToJson (output) {
     if (inyaml) {
       if (line.match(/^\s+\.\.\.$/) && line.length === startlen) {
         const yload = yaml.safeLoad(y)
+        delete yload.stdio
         const data = JSON.stringify(deStackify(yload))
         ret += new Array(startlen - 2).join(' ') +
           data + '\n' + line + '\n'
diff --git a/test/test/mochalike--bail--buffer.tap b/test/test/mochalike--bail--buffer.tap
index 3b62371..4657532 100644
--- a/test/test/mochalike--bail--buffer.tap
+++ b/test/test/mochalike--bail--buffer.tap
@@ -53,7 +53,7 @@ not ok 5 - failing indented things ___/# time=[0-9.]+(ms)?/~~~ {
         not ok 1 - has no asserts, only throws ___/# time=[0-9.]+(ms)?/~~~ {
             not ok 1 - false is not true on line 50
               ---
-              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":54},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
+              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":52},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
               ...
             
             Bail out! # false is not true on line 50
diff --git a/test/test/mochalike--bail.tap b/test/test/mochalike--bail.tap
index ff93287..66cab2b 100644
--- a/test/test/mochalike--bail.tap
+++ b/test/test/mochalike--bail.tap
@@ -53,7 +53,7 @@ ok 4 - reasonably indented things ___/# time=[0-9.]+(ms)?/~~~
         # Subtest: has no asserts, only throws
             not ok 1 - false is not true on line 50
               ---
-              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":54},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
+              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":52},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
               ...
             
             Bail out! # false is not true on line 50
diff --git a/test/test/mochalike--buffer.tap b/test/test/mochalike--buffer.tap
index fca2006..f6348e2 100644
--- a/test/test/mochalike--buffer.tap
+++ b/test/test/mochalike--buffer.tap
@@ -53,7 +53,7 @@ not ok 5 - failing indented things ___/# time=[0-9.]+(ms)?/~~~ {
         not ok 1 - has no asserts, only throws ___/# time=[0-9.]+(ms)?/~~~ {
             not ok 1 - false is not true on line 50
               ---
-              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":54},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
+              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":52},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
               ...
             
             1..1
@@ -69,7 +69,7 @@ not ok 5 - failing indented things ___/# time=[0-9.]+(ms)?/~~~ {
     not ok 2 - second subset ___/# time=[0-9.]+(ms)?/~~~ {
         not ok 1 - objectify the truthiness
           ---
-          {"actual":false,"at":{"column":5,"file":"test/test/mochalike.js","line":60},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(!{}, 'objectify the truthiness')\n","test":"second subset","type":"AssertionError"}
+          {"actual":false,"at":{"column":5,"file":"test/test/mochalike.js","line":58},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(!{}, 'objectify the truthiness')\n","test":"second subset","type":"AssertionError"}
           ...
         
         1..1
@@ -84,7 +84,7 @@ not ok 6 - a test passing an error to done() callback ___/# time=[0-9.]+(ms)?/~~
     not ok 1 - is marked as failed ___/# time=[0-9.]+(ms)?/~~~ {
         not ok 1 - error arg
           ---
-          {"at":{"column":12,"file":"test/test/mochalike.js","line":67},"source":"done(new Error('error arg'))\n","test":"is marked as failed"}
+          {"at":{"column":12,"file":"test/test/mochalike.js","line":65},"source":"done(new Error('error arg'))\n","test":"is marked as failed"}
           ...
         
         1..1
diff --git a/test/test/mochalike.js b/test/test/mochalike.js
index f93e755..940a4ef 100644
--- a/test/test/mochalike.js
+++ b/test/test/mochalike.js
@@ -1,6 +1,5 @@
-if (typeof describe !== 'function') {
+if (typeof describe !== 'function')
   require('../../lib/mocha.js').global()
-}
 
 /* global describe, it */
 
diff --git a/test/test/mochalike.tap b/test/test/mochalike.tap
index d464c9e..3a096d2 100644
--- a/test/test/mochalike.tap
+++ b/test/test/mochalike.tap
@@ -53,7 +53,7 @@ ok 4 - reasonably indented things ___/# time=[0-9.]+(ms)?/~~~
         # Subtest: has no asserts, only throws
             not ok 1 - false is not true on line 50
               ---
-              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":54},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
+              {"actual":false,"at":{"column":7,"file":"test/test/mochalike.js","line":52},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(false, 'false is not true on line 50')\n","test":"has no asserts, only throws","type":"AssertionError"}
               ...
             
             1..1
@@ -69,7 +69,7 @@ ok 4 - reasonably indented things ___/# time=[0-9.]+(ms)?/~~~
     # Subtest: second subset
         not ok 1 - objectify the truthiness
           ---
-          {"actual":false,"at":{"column":5,"file":"test/test/mochalike.js","line":60},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(!{}, 'objectify the truthiness')\n","test":"second subset","type":"AssertionError"}
+          {"actual":false,"at":{"column":5,"file":"test/test/mochalike.js","line":58},"expected":true,"generatedMessage":false,"operator":"==","source":"ok(!{}, 'objectify the truthiness')\n","test":"second subset","type":"AssertionError"}
           ...
         
         1..1
@@ -84,7 +84,7 @@ not ok 5 - failing indented things ___/# time=[0-9.]+(ms)?/~~~
     # Subtest: is marked as failed
         not ok 1 - error arg
           ---
-          {"at":{"column":12,"file":"test/test/mochalike.js","line":67},"source":"done(new Error('error arg'))\n","test":"is marked as failed"}
+          {"at":{"column":12,"file":"test/test/mochalike.js","line":65},"source":"done(new Error('error arg'))\n","test":"is marked as failed"}
           ...
         
         1..1
diff --git a/unit/spawn.js b/unit/spawn.js
new file mode 100644
index 0000000..38d513c
--- /dev/null
+++ b/unit/spawn.js
@@ -0,0 +1,179 @@
+const Spawn = require('../lib/spawn.js')
+const t = require('../')
+const Test = t.Test
+const Parser = require('tap-parser')
+const node = process.execPath
+const file = __filename
+
+const main = () => {
+  t.test('basic child process', t =>
+    t.spawn(node, [ file, 'ok' ]))
+
+  t.test('using a plan', t =>
+    t.spawn(node, [ file, 'plan-ok' ]))
+
+  t.test('hard quit', t =>
+    t.spawn(node, [ file, 'sigself' ],
+      { expectFail: true }))
+
+  t.test('failing process', t =>
+    t.spawn(node, [ file, 'not-ok' ],
+            { expectFail: true }))
+
+  t.test('timeout', t =>
+    t.spawn(node, [ file, 'timeout' ], {
+      expectFail: true,
+      timeout: 1
+    }))
+
+  t.test('timeout KILL', t => {
+    const s = new Spawn({
+      command: node,
+      args: [ file, 'catch-term' ],
+      timeout: 500,
+      buffered: true,
+      name: 'killa'
+    })
+    s.main(_ => {
+      t.equal(s.output, `
+not ok 1 - timeout!
+  ---
+  expired: killa
+  ...
+1..1
+# failed 1 test
+`)
+      t.end()
+    })
+  })
+
+  t.test('skip stuff', t => {
+    const tt = new Test({ buffered: true })
+    tt.spawn(node, [ file, 'skip' ], {
+      bail: true
+    }, 'skipper')
+    tt.spawn(node, [ file, 'skip-reason' ])
+    tt.test('check it', ttt => {
+      const output = tt.output
+      t.match(output, 'skipper # SKIP')
+      t.match(output,
+        'spawn.js skip-reason # SKIP for no raisins')
+      t.end()
+    })
+  })
+
+  t.test('timeout before spawn is no-op', t => {
+    const s = new Spawn({
+      command: node,
+      args: [ file, 'timeout' ]
+    })
+    s.timeout()
+    t.end()
+  })
+
+  t.test('failure to spawn', t => {
+    const s = new Spawn({
+      command: 'something that does not exist',
+      args: [],
+      buffered: true,
+      stdio: [0, 1, 2]
+    })
+    s.main(_ => {
+      t.match(s.output,
+        'not ok 1 - spawn something that does not exist ENOENT\n')
+      t.end()
+    })
+  })
+
+  t.test('failure to spawn even harder', t => {
+    const cp = require('child_process')
+    const spawn = cp.spawn
+    const poop = new Error('poop error')
+    cp.spawn = () => { throw poop }
+    t.teardown(_ => cp.spawn = spawn)
+    const s = new Spawn({
+      command: 'poop',
+      args: [],
+      buffered: true,
+      stdio: 'inherit'
+    })
+    s.main(_ => {
+      t.match(s.output, 'not ok 1 - poop error\n')
+      t.match(s.output, 'command: poop\n')
+      t.end()
+    })
+  })
+
+  t.throws(_ => new Spawn(),
+           new TypeError('no command provided'))
+
+  t.test('endAll called', t => {
+    t.test('call proc', t => {
+      const s = new Spawn({
+        command: node,
+        args: [ file, 'timeout' ],
+        buffered: true
+      })
+      s.main(_ => {
+        t.match(s.output, 'not ok 1 - test unfinished')
+        t.end()
+      })
+      setTimeout(_ => s.endAll())
+    })
+
+    t.test('pre-call', t => {
+      const cp = require('child_process')
+      const spawn = cp.spawn
+      const poop = new Error('poop error')
+      cp.spawn = () => { throw poop }
+      t.teardown(_ => cp.spawn = spawn)
+      const s = new Spawn({
+        command: node,
+        args: [ file, 'timeout' ],
+        buffered: true
+      })
+      s.endAll()
+      t.match(s.output, 'not ok 1 - test unfinished')
+      t.end()
+    })
+    t.end()
+  })
+}
+
+switch (process.argv[2]) {
+  case 'ok':
+    t.pass('this is fine')
+    break
+
+  case 'plan-ok':
+    t.plan(1)
+    t.pass('this is fine')
+    setTimeout(_ => _, 200)
+    break
+
+  case 'sigself':
+    setTimeout(_ =>
+      process.kill(process.pid, 'SIGQUIT'), 300)
+    break
+
+  case 'not-ok':
+    process.exit(1)
+    break
+
+  case 'skip':
+    t.plan(0)
+    break
+
+  case 'skip-reason':
+    t.plan(0, 'for no raisins')
+    break
+
+  case 'catch-term':
+    process.on('SIGTERM', _ => _)
+  case 'timeout':
+    setTimeout(_ => _, 5000)
+    break
+
+  default:
+    main()
+}

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-tap.git



More information about the Pkg-javascript-commits mailing list