From 698f19735e2c3d99fa944bfb7bb320169792e615 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Fri, 8 Sep 2017 11:01:28 -0700 Subject: [PATCH 1/7] Check added stack trace parts for filename match --- lib/test.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/test.js b/lib/test.js index 9cd9492e..2d68fd1b 100644 --- a/lib/test.js +++ b/lib/test.js @@ -225,18 +225,14 @@ Test.prototype._assert = function assert (ok, opts) { } var s = m[1].split(/\s+/); - var filem = /((?:\/|[A-Z]:\\)[^:\s]+:(\d+)(?::(\d+))?)/.exec(s[1]); - if (!filem) { - filem = /((?:\/|[A-Z]:\\)[^:\s]+:(\d+)(?::(\d+))?)/.exec(s[2]); - - if (!filem) { - filem = /((?:\/|[A-Z]:\\)[^:\s]+:(\d+)(?::(\d+))?)/.exec(s[3]); - - if (!filem) { - continue; - } - } + var filemRe = /((?:\/|[A-Z]:\\)[^:\s]+:(\d+)(?::(\d+))?)/; + var filem; + var sIndex; + for (sIndex in s.slice(0, 4)) { + filem = filemRe.exec(s[sIndex]); + if (filem) break; } + if (! filem) continue; if (filem[1].slice(0, dir.length) === dir) { continue; From 32faf707e770f63071823f4a97122d7724d57aa9 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Sun, 10 Sep 2017 16:07:15 -0700 Subject: [PATCH 2/7] Provide placeholder names for anonymous functions --- lib/test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/test.js b/lib/test.js index 2d68fd1b..4dc90aa2 100644 --- a/lib/test.js +++ b/lib/test.js @@ -237,13 +237,13 @@ Test.prototype._assert = function assert (ok, opts) { if (filem[1].slice(0, dir.length) === dir) { continue; } - - res.functionName = s[0]; + + res.functionName = s.length > 1 ? s[0] : ''; res.file = filem[1]; res.line = Number(filem[2]); if (filem[3]) res.column = filem[3]; - res.at = m[1]; + res.at = s.length > 1 ? m[1] : ' (' + m[1] + ')'; break; } } From f619f604af08075b7c758c77924fdda8f2967704 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Sun, 10 Sep 2017 16:07:35 -0700 Subject: [PATCH 3/7] Update existing tests to properly reference anonymous names --- test/exit.js | 2 +- test/fail.js | 2 +- test/too_many.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/exit.js b/test/exit.js index 2a9d2f0c..283301fc 100644 --- a/test/exit.js +++ b/test/exit.js @@ -52,7 +52,7 @@ tap.test('exit fail', function (t) { ' operator: deepEqual', ' expected: [ [ 1, 2, [ 3, 4444 ] ], [ 5, 6 ] ]', ' actual: [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]', - ' at: Test. ($TEST/exit/fail.js:$LINE:$COL)', + ' at: ($TEST/exit/fail.js:$LINE:$COL)', ' stack: |-', ' Error: should be equivalent', ' [... stack stripped ...]', diff --git a/test/fail.js b/test/fail.js index 73029639..2c975bb8 100644 --- a/test/fail.js +++ b/test/fail.js @@ -22,7 +22,7 @@ tap.test('array test', function (tt) { ' operator: deepEqual', ' expected: [ [ 1, 2, [ 3, 4444 ] ], [ 5, 6 ] ]', ' actual: [ [ 1, 2, [ 3, 4 ] ], [ 5, 6 ] ]', - ' at: Test. ($TEST/fail.js:$LINE:$COL)', + ' at: ($TEST/fail.js:$LINE:$COL)', ' stack: |-', ' Error: should be equivalent', ' [... stack stripped ...]', diff --git a/test/too_many.js b/test/too_many.js index b7d59e86..3bd0ca56 100644 --- a/test/too_many.js +++ b/test/too_many.js @@ -22,7 +22,7 @@ tap.test('array test', function (tt) { ' operator: fail', ' expected: 3', ' actual: 4', - ' at: Test. ($TEST/too_many.js:$LINE:$COL)', + ' at: ($TEST/too_many.js:$LINE:$COL)', ' stack: |-', ' Error: plan != count', ' [... stack stripped ...]', From 601559949295fa6479d877ec5a69ef3dac419f52 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Sun, 10 Sep 2017 16:07:45 -0700 Subject: [PATCH 4/7] Test for anonymous function wrapper --- test/anonymous-fn.js | 41 +++++++++++++++++++++++++++++++ test/anonymous-fn/test-wrapper.js | 16 ++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 test/anonymous-fn.js create mode 100644 test/anonymous-fn/test-wrapper.js diff --git a/test/anonymous-fn.js b/test/anonymous-fn.js new file mode 100644 index 00000000..38712d46 --- /dev/null +++ b/test/anonymous-fn.js @@ -0,0 +1,41 @@ +var tape = require('../'); +var tap = require('tap'); +var concat = require('concat-stream'); + +var stripFullStack = require('./common').stripFullStack; +var testWrapper = require('./anonymous-fn/test-wrapper'); + +tap.test('inside anonymous functions', function (tt) { + tt.plan(1); + + var test = tape.createHarness(); + var tc = function (rows) { + tt.same(stripFullStack(rows.toString('utf8')), [ + 'TAP version 13', + '# wrapped test failure', + 'not ok 1 fail', + ' ---', + ' operator: fail', + ' at: ($TEST/anonymous-fn.js:$LINE:$COL)', + ' stack: |-', + ' Error: fail', + ' [... stack stripped ...]', + ' at $TEST/anonymous-fn.js:$LINE:$COL', + ' at Test. ($TEST/anonymous-fn/test-wrapper.js:$LINE:$COL)', + ' [... stack stripped ...]', + ' ...', + '', + '1..1', + '# tests 1', + '# pass 0', + '# fail 1' + ].join('\n') + '\n'); + }; + + test.createStream().pipe(concat(tc)); + + test('wrapped test failure', testWrapper(function (t) { + t.fail('fail'); + t.end(); + })); +}); diff --git a/test/anonymous-fn/test-wrapper.js b/test/anonymous-fn/test-wrapper.js new file mode 100644 index 00000000..acf66e30 --- /dev/null +++ b/test/anonymous-fn/test-wrapper.js @@ -0,0 +1,16 @@ +// Example of wrapper function that would invoke tape +module.exports = function (testCase) { + return function(t) { + setUp(); + testCase(t); + tearDown(); + }; +} + +function setUp() { + // ... example ... +} + +function tearDown() { + // ... example ... +} From a5fb7eda4cde3880bd07b23efb177bae11124768 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Sun, 10 Sep 2017 16:50:12 -0700 Subject: [PATCH 5/7] Handle stack variation in Node v0.8 --- test/anonymous-fn.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/anonymous-fn.js b/test/anonymous-fn.js index 38712d46..2856ea82 100644 --- a/test/anonymous-fn.js +++ b/test/anonymous-fn.js @@ -10,7 +10,15 @@ tap.test('inside anonymous functions', function (tt) { var test = tape.createHarness(); var tc = function (rows) { - tt.same(stripFullStack(rows.toString('utf8')), [ + var body = stripFullStack(rows.toString('utf8')); + + // Handle stack trace variation in Node v0.8 + body = body.replace( + 'at Test.module.exports', + 'at Test.' + ); + + tt.same(body, [ 'TAP version 13', '# wrapped test failure', 'not ok 1 fail', From 3c2087a214cd9f0086bc12ec1ec24b1ab02293a0 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Mon, 11 Sep 2017 14:39:30 -0700 Subject: [PATCH 6/7] Test name with spaces --- test/has spaces.js | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 test/has spaces.js diff --git a/test/has spaces.js b/test/has spaces.js new file mode 100644 index 00000000..ae18e71d --- /dev/null +++ b/test/has spaces.js @@ -0,0 +1,40 @@ +var tape = require('../'); +var tap = require('tap'); +var concat = require('concat-stream'); + +var stripFullStack = require('./common').stripFullStack; + +tap.test('array test', function (tt) { + tt.plan(1); + + var test = tape.createHarness({ exit : false }); + var tc = function (rows) { + tt.same(stripFullStack(rows.toString('utf8')), [ + 'TAP version 13', + '# fail', + 'not ok 1 this should fail', + ' ---', + ' operator: fail', + ' at: Test. ($TEST/has spaces.js:$LINE:$COL)', + ' stack: |-', + ' Error: this should fail', + ' [... stack stripped ...]', + ' at Test. ($TEST/has spaces.js:$LINE:$COL)', + ' [... stack stripped ...]', + ' ...', + '', + '1..1', + '# tests 1', + '# pass 0', + '# fail 1', + '' + ].join('\n')); + }; + + test.createStream().pipe(concat(tc)); + + test('fail', function (t) { + t.fail('this should fail'); + t.end(); + }); +}); From bf5a750937df4920ab9e2e965be02ac69dd43f72 Mon Sep 17 00:00:00 2001 From: Andrew Fong Date: Mon, 11 Sep 2017 16:36:23 -0700 Subject: [PATCH 7/7] Handle spaces in path name for setting file, line no --- lib/test.js | 68 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/lib/test.js b/lib/test.js index 4dc90aa2..548ffbd2 100644 --- a/lib/test.js +++ b/lib/test.js @@ -217,33 +217,65 @@ Test.prototype._assert = function assert (ok, opts) { var e = new Error('exception'); var err = (e.stack || '').split('\n'); var dir = __dirname + path.sep; - + for (var i = 0; i < err.length; i++) { - var m = /^[^\s]*\s*\bat\s+(.+)/.exec(err[i]); + /* + Stack trace lines may resemble one of the following. We need + to should correctly extract a function name (if any) and + path / line no. for each line. + + at myFunction (/path/to/file.js:123:45) + at myFunction (/path/to/file.other-ext:123:45) + at myFunction (/path to/file.js:123:45) + at myFunction (C:\path\to\file.js:123:45) + at myFunction (/path/to/file.js:123) + at Test. (/path/to/file.js:123:45) + at Test.bound [as run] (/path/to/file.js:123:45) + at /path/to/file.js:123:45 + + Regex has three parts. First is non-capturing group for 'at ' + (plus anything preceding it). + + /^(?:[^\s]*\s*\bat\s+)/ + + Second captures function call description (optional). This is + not necessarily a valid JS function name, but just what the + stack trace is using to represent a function call. It may look + like `` or 'Test.bound [as run]'. + + For our purposes, we assume that, if there is a function + name, it's everything leading up to the first open + parentheses (trimmed) before our pathname. + + /(?:(.*)\s+\()?/ + + Last part captures file path plus line no (and optional + column no). + + /((?:\/|[A-Z]:\\)[^:\)]+:(\d+)(?::(\d+))?)/ + */ + var re = /^(?:[^\s]*\s*\bat\s+)(?:(.*)\s+\()?((?:\/|[A-Z]:\\)[^:\)]+:(\d+)(?::(\d+))?)/ + var m = re.exec(err[i]); + if (!m) { continue; } + + var callDescription = m[1] || ''; + var filePath = m[2]; - var s = m[1].split(/\s+/); - var filemRe = /((?:\/|[A-Z]:\\)[^:\s]+:(\d+)(?::(\d+))?)/; - var filem; - var sIndex; - for (sIndex in s.slice(0, 4)) { - filem = filemRe.exec(s[sIndex]); - if (filem) break; - } - if (! filem) continue; - - if (filem[1].slice(0, dir.length) === dir) { + if (filePath.slice(0, dir.length) === dir) { continue; } - res.functionName = s.length > 1 ? s[0] : ''; - res.file = filem[1]; - res.line = Number(filem[2]); - if (filem[3]) res.column = filem[3]; + // Function call description may not (just) be a function name. + // Try to extract function name by looking at first "word" only. + res.functionName = callDescription.split(/s+/)[0] + res.file = filePath; + res.line = Number(m[3]); + if (m[4]) res.column = Number(m[4]); - res.at = s.length > 1 ? m[1] : ' (' + m[1] + ')'; + res.at = callDescription + ' (' + filePath + ')'; break; } }