Skip to content

Commit 1bf1c09

Browse files
committed
First pass on new test harness for command-line tool
1 parent 8b7475a commit 1bf1c09

6 files changed

Lines changed: 518 additions & 64 deletions

File tree

tools/buildmessage.js

Lines changed: 5 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
var _ = require('underscore');
22
var files = require('./files.js');
3+
var parseStack = require('./parse-stack.js');
34

45
var debugBuild = !!process.env.METEOR_DEBUG_BUILD;
56

@@ -23,7 +24,7 @@ var Job = function (options) {
2324

2425
_.extend(Job.prototype, {
2526
// options may include type ("error"), message, func, file, line,
26-
// column, stack (in the format returned by parseStack)
27+
// column, stack (in the format returned by parseStack.parse())
2728
addMessage: function (options) {
2829
var self = this;
2930
self.messages.push(options);
@@ -254,74 +255,14 @@ var jobHasMessages = function () {
254255
return currentJob ? search(currentJob) : false;
255256
};
256257

257-
// Given an Error (eg, 'new Error'), return the stack associated with
258-
// that error as an array. More recently called functions appear first
259-
// and each element is an object with keys:
260-
// - file: filename as it appears in the stack
261-
// - line: 1-indexed line number in file, as a Number
262-
// - column: 1-indexed column in line, as a Number
263-
// - func: name of the function in the frame (maybe null)
264-
//
265-
// Accomplishes this by parsing the text representation of the stack
266-
// with regular expressions. Unlikely to work anywhere but v8.
267-
//
268-
// If a function on the stack has been marked with markBoundary, don't
269-
// return anything past that function. We call this the "user portion"
270-
// of the stack.
271-
var parseStack = function (err) {
272-
var frames = err.stack.split('\n');
273-
274-
frames.shift(); // at least the first line is the exception
275-
var stop = false;
276-
var ret = [];
277-
278-
_.each(frames, function (frame) {
279-
if (stop)
280-
return;
281-
var m;
282-
if (m =
283-
frame.match(/^\s*at\s*((new )?[^\s]+)\s*\(([^:]*)(:(\d+))?(:(\d+))?\)\s*$/)) {
284-
// " at My.Function (/path/to/myfile.js:532:39)"
285-
// " at Array.forEach (native)"
286-
// " at new My.Class (file.js:1:2)"
287-
if (m[1] === "__mark__") {
288-
stop = true;
289-
return;
290-
}
291-
ret.push({
292-
func: m[1],
293-
file: m[3],
294-
line: m[5] ? +m[5] : undefined,
295-
column: m[7] ? +m[7] : undefined
296-
});
297-
} else if (m = frame.match(/^\s*at\s+([^:]+)(:(\d+))?(:(\d+))?\s*$/)) {
298-
// " at /path/to/myfile.js:532:39"
299-
ret.push({
300-
file: m[1],
301-
line: m[3] ? +m[3] : undefined,
302-
column: m[5] ? +m[5] : undefined
303-
});
304-
} else if (_.isEmpty(ret)) {
305-
// We haven't found any stack frames, so probably we have newlines in the
306-
// error message. Just skip this line.
307-
} else {
308-
throw new Error("Couldn't parse stack frame: '" + frame + "'");
309-
}
310-
});
311-
312-
return ret;
313-
};
314-
315258
// Given a function f, return a "marked" version of f. The mark
316259
// indicates that stack traces should stop just above f. So if you
317260
// mark a user-supplied callback function before calling it, you'll be
318261
// able to show the user just the "user portion" of the stack trace
319262
// (the part inside their own code, and not all of the innards of the
320263
// code that called it).
321264
var markBoundary = function (f) {
322-
return function __mark__ () {
323-
return f.apply(this, arguments);
324-
};
265+
return parseStack.markBottom(f);
325266
};
326267

327268
// Record a build error. If inside a job, add the error to the current
@@ -365,7 +306,7 @@ var error = function (message, options) {
365306

366307
if ('useMyCaller' in info) {
367308
if (info.useMyCaller) {
368-
info.stack = parseStack(new Error()).slice(2);
309+
info.stack = parseStack.parse(new Error()).slice(2);
369310
var howManyToSkip = (
370311
typeof info.useMyCaller === "number" ? info.useMyCaller : 0);
371312
var caller = info.stack[howManyToSkip];
@@ -411,7 +352,7 @@ var exception = function (error) {
411352
column: error.column
412353
});
413354
} else {
414-
var stack = parseStack(error);
355+
var stack = parseStack.parse(error);
415356
var locus = stack[0];
416357
currentJob.addMessage({
417358
message: message,

tools/commands.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,3 +1193,16 @@ main.registerCommand({
11931193
}, function (options) {
11941194
return auth.whoAmICommand(options);
11951195
});
1196+
1197+
1198+
///////////////////////////////////////////////////////////////////////////////
1199+
// self-test
1200+
///////////////////////////////////////////////////////////////////////////////
1201+
1202+
main.registerCommand({
1203+
name: 'self-test',
1204+
hidden: true
1205+
}, function (options) {
1206+
var selftest = require('./selftest.js');
1207+
return selftest.runTests();
1208+
});

tools/help.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,3 +301,10 @@ Prints the username of the currently logged-in Meteor developer.
301301

302302
See 'meteor login' to log into or 'meteor logout' to log out of of your
303303
Meteor account.
304+
305+
306+
>>> self-test
307+
Run tests of the 'meteor' tool.
308+
Usage: meteor self-test
309+
310+
Runs internal tests. Exits with status 0 on success.

tools/parse-stack.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
var _ = require('underscore');
2+
3+
// Given an Error (eg, 'new Error'), return the stack associated with
4+
// that error as an array. More recently called functions appear first
5+
// and each element is an object with keys:
6+
// - file: filename as it appears in the stack
7+
// - line: 1-indexed line number in file, as a Number
8+
// - column: 1-indexed column in line, as a Number
9+
// - func: name of the function in the frame (maybe null)
10+
//
11+
// Accomplishes this by parsing the text representation of the stack
12+
// with regular expressions. Unlikely to work anywhere but v8.
13+
//
14+
// If a function on the stack has been marked with mark(), don't
15+
// return anything past that function. We call this the "user portion"
16+
// of the stack.
17+
exports.parse = function (err) {
18+
var frames = err.stack.split('\n');
19+
20+
frames.shift(); // at least the first line is the exception
21+
var stop = false;
22+
var ret = [];
23+
24+
_.each(frames, function (frame) {
25+
if (stop)
26+
return;
27+
var m;
28+
if (m =
29+
frame.match(/^\s*at\s*((new )?[^\s]+)\s*(\[as\s*([^\]]*)\]\s*)?\(([^:]*)(:(\d+))?(:(\d+))?\)\s*$/)) {
30+
// https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
31+
// " at My.Function (/path/to/myfile.js:532:39)"
32+
// " at Array.forEach (native)"
33+
// " at new My.Class (file.js:1:2)"
34+
// " at __top_mark__ [as matchErr] (meteor/tools/parse-stack.js:82:14)"
35+
//
36+
// In that last example, it is not at all clear to me what the
37+
// 'as' stanza refers to, but it is in m[3] if you find a use for it.
38+
if (m[1] === "__top_mark__") {
39+
ret = [];
40+
return;
41+
}
42+
if (m[1] === "__bottom_mark__") {
43+
stop = true;
44+
return;
45+
}
46+
ret.push({
47+
func: m[1],
48+
file: m[5],
49+
line: m[7] ? +m[7] : undefined,
50+
column: m[9] ? +m[9] : undefined
51+
});
52+
} else if (m = frame.match(/^\s*at\s+([^:]+)(:(\d+))?(:(\d+))?\s*$/)) {
53+
// " at /path/to/myfile.js:532:39"
54+
ret.push({
55+
file: m[1],
56+
line: m[3] ? +m[3] : undefined,
57+
column: m[5] ? +m[5] : undefined
58+
});
59+
} else if (m = frame.match(/^\s*-\s*-\s*-\s*-\s*-\s*$/)) {
60+
// " - - - - -"
61+
// This shows up sometimes, with what appears to be a different
62+
// fiber's stack underneath it?
63+
stop = true;
64+
} else if (_.isEmpty(ret)) {
65+
// We haven't found any stack frames, so probably we have newlines in the
66+
// error message. Just skip this line.
67+
} else {
68+
console.log(err.stack);
69+
throw new Error("Couldn't parse stack frame: '" + frame + "'");
70+
}
71+
});
72+
73+
return ret;
74+
};
75+
76+
// Decorator. Mark the point at which a stack trace returned by
77+
// parse() should stop: no frames earlier than this point will be
78+
// included in the parsed stack. Confusingly, in the argot of the
79+
// times, you'd say that frames "higher up" than this or "above" this
80+
// will not be returned, but you'd also say that those frames are "at
81+
// the bottom of the stack".
82+
exports.markBottom = function (f) {
83+
return function __bottom_mark__ () {
84+
return f.apply(this, arguments);
85+
};
86+
};
87+
88+
// Decorator. Mark the point at which a stack trace returned by
89+
// parse() should begin: no frames later than this point will be
90+
// included in the parsed stack. The opposite of markBottom().
91+
exports.markTop = function (f) {
92+
return function __top_mark__ () {
93+
return f.apply(this, arguments);
94+
};
95+
};

tools/run-all.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var Updater = require('./run-updater.js').Updater;
1616
//
1717
// - deal with XXX's in updater about it needing to go though runlog since
1818
// no more stdout redirection
19+
// - audit future yielding, keeping in mind that return yields
1920
//
2021
///////////////////////////////////////////////////////////////////////////////
2122

0 commit comments

Comments
 (0)