3 var util = require('../');
5 var fs = require('fs');
6 var path = require('path');
8 var Tempfile = require('temporary/lib/file');
10 exports['util.callbackify'] = {
11 'return': function(test) {
13 // This function returns a value.
17 util.callbackify(add)(1, 2, function(result) {
18 test.equal(result, 3, 'should be the correct result.');
22 'callback (sync)': function(test) {
24 // This function accepts a callback which it calls synchronously.
25 function add(a, b, done) {
28 util.callbackify(add)(1, 2, function(result) {
29 test.equal(result, 3, 'should be the correct result.');
33 'callback (async)': function(test) {
35 // This function accepts a callback which it calls asynchronously.
36 function add(a, b, done) {
37 setTimeout(done.bind(null, a + b), 0);
39 util.callbackify(add)(1, 2, function(result) {
40 test.equal(result, 3, 'should be the correct result.');
47 'error': function(test) {
49 var origError = new Error('Original error.');
51 var err = util.error('Test message.');
52 test.ok(err instanceof Error, 'Should be an Error.');
53 test.equal(err.name, 'Error', 'Should be an Error.');
54 test.equal(err.message, 'Test message.', 'Should have the correct message.');
56 err = util.error('Test message.', origError);
57 test.ok(err instanceof Error, 'Should be an Error.');
58 test.equal(err.name, 'Error', 'Should be an Error.');
59 test.equal(err.message, 'Test message.', 'Should have the correct message.');
60 test.equal(err.origError, origError, 'Should reflect the original error.');
62 var newError = new Error('Test message.');
63 err = util.error(newError, origError);
64 test.equal(err, newError, 'Should be the passed-in Error.');
65 test.equal(err.origError, origError, 'Should reflect the original error.');
68 'linefeed': function(test) {
70 if (process.platform === 'win32') {
71 test.equal(util.linefeed, '\r\n', 'linefeed should be operating-system appropriate.');
73 test.equal(util.linefeed, '\n', 'linefeed should be operating-system appropriate.');
77 'normalizelf': function(test) {
79 if (process.platform === 'win32') {
80 test.equal(util.normalizelf('foo\nbar\r\nbaz\r\n\r\nqux\n\nquux'), 'foo\r\nbar\r\nbaz\r\n\r\nqux\r\n\r\nquux', 'linefeeds should be normalized');
82 test.equal(util.normalizelf('foo\nbar\r\nbaz\r\n\r\nqux\n\nquux'), 'foo\nbar\nbaz\n\nqux\n\nquux', 'linefeeds should be normalized');
88 exports['util.spawn'] = {
89 setUp: function(done) {
90 this.script = path.resolve('test/fixtures/spawn.js');
93 'exit code 0': function(test) {
96 cmd: process.execPath,
97 args: [ this.script, 0 ],
98 }, function(err, result, code) {
99 test.equals(err, null);
100 test.equals(code, 0);
101 test.equals(result.stdout, 'stdout');
102 test.equals(result.stderr, 'stderr');
103 test.equals(result.code, 0);
104 test.equals(String(result), 'stdout');
108 'exit code 0, fallback': function(test) {
111 cmd: process.execPath,
112 args: [ this.script, 0 ],
113 fallback: 'ignored if exit code is 0'
114 }, function(err, result, code) {
115 test.equals(err, null);
116 test.equals(code, 0);
117 test.equals(result.stdout, 'stdout');
118 test.equals(result.stderr, 'stderr');
119 test.equals(result.code, 0);
120 test.equals(String(result), 'stdout');
124 'non-zero exit code': function(test) {
127 cmd: process.execPath,
128 args: [ this.script, 123 ],
129 }, function(err, result, code) {
130 test.ok(err instanceof Error);
131 test.equals(err.message, 'stderr');
132 test.equals(code, 123);
133 test.equals(result.stdout, 'stdout');
134 test.equals(result.stderr, 'stderr');
135 test.equals(result.code, 123);
136 test.equals(String(result), 'stderr');
140 'non-zero exit code, fallback': function(test) {
143 cmd: process.execPath,
144 args: [ this.script, 123 ],
145 fallback: 'custom fallback'
146 }, function(err, result, code) {
147 test.equals(err, null);
148 test.equals(code, 123);
149 test.equals(result.stdout, 'stdout');
150 test.equals(result.stderr, 'stderr');
151 test.equals(result.code, 123);
152 test.equals(String(result), 'custom fallback');
156 'cmd not found': function(test) {
159 cmd: 'nodewtfmisspelled',
160 }, function(err, result, code) {
161 test.ok(err instanceof Error);
162 test.equals(code, 127);
163 test.equals(result.code, 127);
167 'cmd not found, fallback': function(test) {
170 cmd: 'nodewtfmisspelled',
171 fallback: 'use a fallback or good luck'
172 }, function(err, result, code) {
173 test.equals(err, null);
174 test.equals(code, 127);
175 test.equals(result.code, 127);
176 test.equals(String(result), 'use a fallback or good luck');
180 'cmd not in path': function(test) {
182 var win32 = process.platform === 'win32';
184 cmd: 'test\\fixtures\\exec' + (win32 ? '.cmd' : '.sh'),
185 }, function(err, result, code) {
186 test.equals(err, null);
187 test.equals(code, 0);
188 test.equals(result.stdout, 'done');
189 test.equals(result.stderr, '');
190 test.equals(result.code, 0);
191 test.equals(String(result), 'done');
195 'cmd not in path (with cwd)': function(test) {
197 var win32 = process.platform === 'win32';
199 cmd: './exec' + (win32 ? '.cmd' : '.sh'),
200 opts: {cwd: 'test/fixtures'},
201 }, function(err, result, code) {
202 test.equals(err, null);
203 test.equals(code, 0);
204 test.equals(result.stdout, 'done');
205 test.equals(result.stderr, '');
206 test.equals(result.code, 0);
207 test.equals(String(result), 'done');
211 'grunt': function(test) {
215 args: [ '--gruntfile', 'test/fixtures/Gruntfile-print-text.js', 'print:foo' ],
216 }, function(err, result, code) {
217 test.equals(err, null);
218 test.equals(code, 0);
219 test.ok(/^OUTPUT: foo/m.test(result.stdout), 'stdout should contain output indicating the grunt task was run.');
223 'grunt (with cwd)': function(test) {
227 args: [ '--gruntfile', 'Gruntfile-print-text.js', 'print:foo' ],
228 opts: {cwd: 'test/fixtures'},
229 }, function(err, result, code) {
230 test.equals(err, null);
231 test.equals(code, 0);
232 test.ok(/^OUTPUT: foo/m.test(result.stdout), 'stdout should contain output indicating the grunt task was run.');
236 'grunt passes execArgv': function(test) {
239 cmd: process.execPath,
240 args: [ '--harmony', process.argv[1], '--gruntfile', 'test/fixtures/Gruntfile-execArgv.js'],
241 }, function(err, result, code) {
242 test.equals(err, null);
243 test.equals(code, 0);
244 test.ok(/^OUTPUT: --harmony/m.test(result.stdout), 'stdout should contain passed-through process.execArgv.');
248 'grunt result.toString() with error': function(test) {
249 // grunt.log.error uses standard out, to be fixed in 0.5.
253 args: [ 'nonexistentTask' ]
254 }, function(err, result, code) {
255 test.ok(err instanceof Error, 'Should be an Error.');
256 test.equal(err.name, 'Error', 'Should be an Error.');
257 test.equals(code, 3);
258 test.ok(/Warning: Task "nonexistentTask" not found./m.test(result.toString()), 'stdout should contain output indicating the grunt task was (attempted to be) run.');
262 'custom stdio stream(s)': function(test) {
264 var stdoutFile = new Tempfile();
265 var stderrFile = new Tempfile();
266 var stdout = fs.openSync(stdoutFile.path, 'a');
267 var stderr = fs.openSync(stderrFile.path, 'a');
268 var child = util.spawn({
269 cmd: process.execPath,
270 args: [ this.script, 0 ],
271 opts: {stdio: [null, stdout, stderr]},
272 }, function(err, result, code) {
273 test.equals(code, 0);
274 test.equals(String(fs.readFileSync(stdoutFile.path)), 'stdout\n', 'Child process stdout should have been captured via custom stream.');
275 test.equals(String(fs.readFileSync(stderrFile.path)), 'stderr\n', 'Child process stderr should have been captured via custom stream.');
276 stdoutFile.unlinkSync();
277 stderrFile.unlinkSync();
278 test.equals(result.stdout, '', 'Nothing will be passed to the stdout string when spawn stdio is a custom stream.');
281 test.ok(!child.stdout, 'child should not have a stdout property.');
282 test.ok(!child.stderr, 'child should not have a stderr property.');
286 exports['util.spawn.multibyte'] = {
287 setUp: function(done) {
288 this.script = path.resolve('test/fixtures/spawn-multibyte.js');
291 'partial stdout': function(test) {
294 cmd: process.execPath,
295 args: [ this.script ],
296 }, function(err, result, code) {
297 test.equals(err, null);
298 test.equals(code, 0);
299 test.equals(result.stdout, 'こんにちは');
300 test.equals(result.stderr, 'こんにちは');
306 exports['util.underscore.string'] = function(test) {
308 test.equals(util._.trim(' foo '), 'foo', 'Should have trimmed the string.');
309 test.equals(util._.capitalize('foo'), 'Foo', 'Should have capitalized the first letter.');
310 test.equals(util._.words('one two three').length, 3, 'Should have counted three words.');
311 test.ok(util._.isBlank(' '), 'Should be blank.');
315 function getType(val) {
316 if (Buffer.isBuffer(val)) { return 'buffer'; }
317 return Object.prototype.toString.call(val).slice(8, -1).toLowerCase();
320 exports['util.recurse'] = {
321 setUp: function(done) {
322 this.typeValue = function(value) {
325 type: getType(value),
330 'primitives': function(test) {
332 var actual = util.recurse({
340 bool: {type: 'boolean', value: true},
341 num: {type: 'number', value: 1},
342 str: {type: 'string', value: 'foo'},
343 nul: {type: 'null', value: null},
344 undef: {type: 'undefined', value: undefined},
346 test.deepEqual(actual, expected, 'Should process primitive values.');
349 'array': function(test) {
351 var actual = util.recurse({
369 {type: 'boolean', value: true},
370 {type: 'number', value: 1},
371 {type: 'string', value: 'foo'},
372 {type: 'null', value: null},
373 {type: 'undefined', value: undefined},
375 {type: 'boolean', value: true},
376 {type: 'number', value: 1},
377 {type: 'string', value: 'foo'},
378 {type: 'null', value: null},
379 {type: 'undefined', value: undefined},
383 test.deepEqual(actual, expected, 'Should recurse over arrays.');
386 'object': function(test) {
388 var actual = util.recurse({
406 bool: {type: 'boolean', value: true},
407 num: {type: 'number', value: 1},
408 str: {type: 'string', value: 'foo'},
409 nul: {type: 'null', value: null},
410 undef: {type: 'undefined', value: undefined},
412 bool: {type: 'boolean', value: true},
413 num: {type: 'number', value: 1},
414 str: {type: 'string', value: 'foo'},
415 nul: {type: 'null', value: null},
416 undef: {type: 'undefined', value: undefined},
420 test.deepEqual(actual, expected, 'Should recurse over objects.');
423 'array in object': function(test) {
425 var actual = util.recurse({
439 {type: 'boolean', value: true},
440 {type: 'number', value: 1},
441 {type: 'string', value: 'foo'},
442 {type: 'null', value: null},
443 {type: 'undefined', value: undefined},
447 test.deepEqual(actual, expected, 'Should recurse over arrays in objects.');
450 'object in array': function(test) {
452 var actual = util.recurse({
465 {type: 'boolean', value: true},
467 num: {type: 'number', value: 1},
468 str: {type: 'string', value: 'foo'},
470 {type: 'null', value: null},
471 {type: 'undefined', value: undefined},
474 test.deepEqual(actual, expected, 'Should recurse over objects in arrays.');
477 'buffer': function(test) {
479 var actual = util.recurse({
480 buf: new Buffer('buf'),
483 buf: {type: 'buffer', value: new Buffer('buf')},
485 test.deepEqual(actual, expected, 'Should not mangle Buffer instances.');
488 'inherited properties': function(test) {
490 var actual = util.recurse({
491 obj: Object.create({num: 1}, {
492 str: {value: 'foo', enumerable: true},
493 ignored: {value: 'ignored', enumerable: false},
498 num: {type: 'number', value: 1},
499 str: {type: 'string', value: 'foo'},
502 test.deepEqual(actual, expected, 'Should enumerate inherited object properties.');
505 'circular references': function(test) {
507 function assertErrorWithPath(expectedPath) {
508 return function(actual) {
509 return actual.path === expectedPath &&
510 actual.message === 'Circular reference detected (' + expectedPath + ')';
513 test.doesNotThrow(function() {
516 a:[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]],
518 b:[[[[],[[[],[[[[],[[[],[[[],[[[],[[[],[[[[],[[]]]]]]]]]]]]]]]]]]]]],
520 c:{d:{e:{f:{g:{h:{i:{j:{k:{l:{m:{n:{o:{p:{q:{r:{s:{}}}}}}}}}}}}}}}}},
522 t:[{u:[{v:[[[[],[[[],[[[{w:[{x:[[[],[[[{y:[[1]]}]]]]]}]}]]]]]]]]}]}],
524 util.recurse(obj, function(v) { return v; });
525 }, 'Should not throw when no circular reference is detected.');
526 test.throws(function() {
527 var obj = {a: 1, b: 2};
529 util.recurse(obj, function(v) { return v; });
530 }, assertErrorWithPath('.obj'), 'Should throw when a circular reference is detected.');
531 test.throws(function() {
532 var obj = {a:{'b b':{'c-c':{d_d:{e:{f:{g:{h:{i:{j:{k:{l:{}}}}}}}}}}}}};
533 obj.a['b b']['c-c'].d_d.e.f.g.h.i.j.k.l.obj = obj;
534 util.recurse(obj, function(v) { return v; });
535 }, assertErrorWithPath('.a["b b"]["c-c"].d_d.e.f.g.h.i.j.k.l.obj'), 'Should throw when a circular reference is detected.');
536 test.throws(function() {
537 var obj = {a: 1, b: 2};
538 obj.arr = [1, 2, obj, 3, 4];
539 util.recurse(obj, function(v) { return v; });
540 }, assertErrorWithPath('.arr[2]'), 'Should throw when a circular reference is detected.');
541 test.throws(function() {
542 var obj = {a: 1, b: 2};
543 obj.arr = [{a:[1,{b:[2,{c:[3,obj,4]},5]},6]},7];
544 util.recurse(obj, function(v) { return v; });
545 }, assertErrorWithPath('.arr[0].a[1].b[1].c[1]'), 'Should throw when a circular reference is detected.');
546 test.throws(function() {
547 var obj = {a: 1, b: 2};
549 obj.arr.push(0,{a:[1,{b:[2,{c:[3,obj.arr,4]},5]},6]},7);
550 util.recurse(obj, function(v) { return v; });
551 }, assertErrorWithPath('.arr[1].a[1].b[1].c[1]'), 'Should throw when a circular reference is detected.');