node/test/parallel/test-fs-realpath.js
mpmckenna8 73ae2d1895 test: cleanup parallel/test-fs-realpath.js
Replaced .indexOf() with .includes() for more clarity as to
what it is doing.

Many of the instances of var changed to const.

Instances of assert.equal() refactored to assert.strictEqual() or
assert.ifError()

Removed the unlinkSync() call in the exit event handler because it
probably only handles files in the testing tmp directory so there is
no need to unlink them.

PR-URL: https://github.com/nodejs/node/pull/8769
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ilkka Myller <ilkka.myller@nodefield.com>
2016-09-27 10:16:02 +00:00

513 lines
16 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const exec = require('child_process').exec;
var async_completed = 0, async_expected = 0, unlink = [];
var skipSymlinks = false;
common.refreshTmpDir();
var root = '/';
var assertEqualPath = assert.strictEqual;
if (common.isWindows) {
// something like "C:\\"
root = process.cwd().substr(0, 3);
assertEqualPath = function(path_left, path_right, message) {
assert
.strictEqual(path_left.toLowerCase(), path_right.toLowerCase(), message);
};
// On Windows, creating symlinks requires admin privileges.
// We'll only try to run symlink test if we have enough privileges.
try {
exec('whoami /priv', function(err, o) {
if (err || !o.includes('SeCreateSymbolicLinkPrivilege')) {
skipSymlinks = true;
}
runTest();
});
} catch (er) {
// better safe than sorry
skipSymlinks = true;
process.nextTick(runTest);
}
} else {
process.nextTick(runTest);
}
function tmp(p) {
return path.join(common.tmpDir, p);
}
const targetsAbsDir = path.join(common.tmpDir, 'targets');
const tmpAbsDir = common.tmpDir;
// Set up targetsAbsDir and expected subdirectories
fs.mkdirSync(targetsAbsDir);
fs.mkdirSync(path.join(targetsAbsDir, 'nested-index'));
fs.mkdirSync(path.join(targetsAbsDir, 'nested-index', 'one'));
fs.mkdirSync(path.join(targetsAbsDir, 'nested-index', 'two'));
function asynctest(testBlock, args, callback, assertBlock) {
async_expected++;
testBlock.apply(testBlock, args.concat(function(err) {
var ignoreError = false;
if (assertBlock) {
try {
ignoreError = assertBlock.apply(assertBlock, arguments);
} catch (e) {
err = e;
}
}
async_completed++;
callback(ignoreError ? null : err);
}));
}
// sub-tests:
function test_simple_error_callback(cb) {
fs.realpath('/this/path/does/not/exist', common.mustCall(function(err, s) {
assert(err);
assert(!s);
cb();
}));
}
function test_simple_relative_symlink(callback) {
console.log('test_simple_relative_symlink');
if (skipSymlinks) {
common.skip('symlink test (no privs)');
return runNextTest();
}
const entry = common.tmpDir + '/symlink';
const expected = common.tmpDir + '/cycles/root.js';
[
[entry, '../' + common.tmpDirName + '/cycles/root.js']
].forEach(function(t) {
try {fs.unlinkSync(t[0]);} catch (e) {}
console.log('fs.symlinkSync(%j, %j, %j)', t[1], t[0], 'file');
fs.symlinkSync(t[1], t[0], 'file');
unlink.push(t[0]);
});
const result = fs.realpathSync(entry);
assertEqualPath(result, path.resolve(expected));
asynctest(fs.realpath, [entry], callback, function(err, result) {
assertEqualPath(result, path.resolve(expected));
});
}
function test_simple_absolute_symlink(callback) {
console.log('test_simple_absolute_symlink');
// this one should still run, even if skipSymlinks is set,
// because it uses a junction.
const type = skipSymlinks ? 'junction' : 'dir';
console.log('using type=%s', type);
const entry = tmpAbsDir + '/symlink';
const expected = common.fixturesDir + '/nested-index/one';
[
[entry, expected]
].forEach(function(t) {
try {fs.unlinkSync(t[0]);} catch (e) {}
console.error('fs.symlinkSync(%j, %j, %j)', t[1], t[0], type);
fs.symlinkSync(t[1], t[0], type);
unlink.push(t[0]);
});
const result = fs.realpathSync(entry);
assertEqualPath(result, path.resolve(expected));
asynctest(fs.realpath, [entry], callback, function(err, result) {
assertEqualPath(result, path.resolve(expected));
});
}
function test_deep_relative_file_symlink(callback) {
console.log('test_deep_relative_file_symlink');
if (skipSymlinks) {
common.skip('symlink test (no privs)');
return runNextTest();
}
const expected = path.join(common.fixturesDir, 'cycles', 'root.js');
const linkData1 = path
.relative(path.join(targetsAbsDir, 'nested-index', 'one'),
expected);
const linkPath1 = path.join(targetsAbsDir,
'nested-index', 'one', 'symlink1.js');
try {fs.unlinkSync(linkPath1);} catch (e) {}
fs.symlinkSync(linkData1, linkPath1, 'file');
const linkData2 = '../one/symlink1.js';
const entry = path.join(targetsAbsDir,
'nested-index', 'two', 'symlink1-b.js');
try {fs.unlinkSync(entry);} catch (e) {}
fs.symlinkSync(linkData2, entry, 'file');
unlink.push(linkPath1);
unlink.push(entry);
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
asynctest(fs.realpath, [entry], callback, function(err, result) {
assertEqualPath(result, path.resolve(expected));
});
}
function test_deep_relative_dir_symlink(callback) {
console.log('test_deep_relative_dir_symlink');
if (skipSymlinks) {
common.skip('symlink test (no privs)');
return runNextTest();
}
const expected = path.join(common.fixturesDir, 'cycles', 'folder');
const path1b = path.join(targetsAbsDir, 'nested-index', 'one');
const linkPath1b = path.join(path1b, 'symlink1-dir');
const linkData1b = path.relative(path1b, expected);
try {fs.unlinkSync(linkPath1b);} catch (e) {}
fs.symlinkSync(linkData1b, linkPath1b, 'dir');
const linkData2b = '../one/symlink1-dir';
const entry = path.join(targetsAbsDir,
'nested-index', 'two', 'symlink12-dir');
try {fs.unlinkSync(entry);} catch (e) {}
fs.symlinkSync(linkData2b, entry, 'dir');
unlink.push(linkPath1b);
unlink.push(entry);
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
asynctest(fs.realpath, [entry], callback, function(err, result) {
assertEqualPath(result, path.resolve(expected));
});
}
function test_cyclic_link_protection(callback) {
console.log('test_cyclic_link_protection');
if (skipSymlinks) {
common.skip('symlink test (no privs)');
return runNextTest();
}
const entry = common.tmpDir + '/cycles/realpath-3a';
[
[entry, '../cycles/realpath-3b'],
[common.tmpDir + '/cycles/realpath-3b', '../cycles/realpath-3c'],
[common.tmpDir + '/cycles/realpath-3c', '../cycles/realpath-3a']
].forEach(function(t) {
try {fs.unlinkSync(t[0]);} catch (e) {}
fs.symlinkSync(t[1], t[0], 'dir');
unlink.push(t[0]);
});
assert.throws(function() { fs.realpathSync(entry); });
asynctest(fs.realpath, [entry], callback, function(err, result) {
assert.ok(err && true);
return true;
});
}
function test_cyclic_link_overprotection(callback) {
console.log('test_cyclic_link_overprotection');
if (skipSymlinks) {
common.skip('symlink test (no privs)');
return runNextTest();
}
const cycles = common.tmpDir + '/cycles';
const expected = fs.realpathSync(cycles);
const folder = cycles + '/folder';
const link = folder + '/cycles';
var testPath = cycles;
testPath += '/folder/cycles'.repeat(10);
try {fs.unlinkSync(link);} catch (ex) {}
fs.symlinkSync(cycles, link, 'dir');
unlink.push(link);
assertEqualPath(fs.realpathSync(testPath), path.resolve(expected));
asynctest(fs.realpath, [testPath], callback, function(er, res) {
assertEqualPath(res, path.resolve(expected));
});
}
function test_relative_input_cwd(callback) {
console.log('test_relative_input_cwd');
if (skipSymlinks) {
common.skip('symlink test (no privs)');
return runNextTest();
}
// we need to calculate the relative path to the tmp dir from cwd
const entrydir = process.cwd();
const entry = path.relative(entrydir,
path.join(common.tmpDir + '/cycles/realpath-3a'));
const expected = common.tmpDir + '/cycles/root.js';
[
[entry, '../cycles/realpath-3b'],
[common.tmpDir + '/cycles/realpath-3b', '../cycles/realpath-3c'],
[common.tmpDir + '/cycles/realpath-3c', 'root.js']
].forEach(function(t) {
const fn = t[0];
console.error('fn=%j', fn);
try {fs.unlinkSync(fn);} catch (e) {}
const b = path.basename(t[1]);
const type = (b === 'root.js' ? 'file' : 'dir');
console.log('fs.symlinkSync(%j, %j, %j)', t[1], fn, type);
fs.symlinkSync(t[1], fn, 'file');
unlink.push(fn);
});
const origcwd = process.cwd();
process.chdir(entrydir);
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
asynctest(fs.realpath, [entry], callback, function(err, result) {
process.chdir(origcwd);
assertEqualPath(result, path.resolve(expected));
return true;
});
}
function test_deep_symlink_mix(callback) {
console.log('test_deep_symlink_mix');
if (common.isWindows) {
// This one is a mix of files and directories, and it's quite tricky
// to get the file/dir links sorted out correctly.
common.skip('symlink test (no privs)');
return runNextTest();
}
/*
/tmp/node-test-realpath-f1 -> $tmpDir/node-test-realpath-d1/foo
/tmp/node-test-realpath-d1 -> $tmpDir/node-test-realpath-d2
/tmp/node-test-realpath-d2/foo -> $tmpDir/node-test-realpath-f2
/tmp/node-test-realpath-f2
-> $tmpDir/targets/nested-index/one/realpath-c
$tmpDir/targets/nested-index/one/realpath-c
-> $tmpDir/targets/nested-index/two/realpath-c
$tmpDir/targets/nested-index/two/realpath-c -> $tmpDir/cycles/root.js
$tmpDir/targets/cycles/root.js (hard)
*/
const entry = tmp('node-test-realpath-f1');
try { fs.unlinkSync(tmp('node-test-realpath-d2/foo')); } catch (e) {}
try { fs.rmdirSync(tmp('node-test-realpath-d2')); } catch (e) {}
fs.mkdirSync(tmp('node-test-realpath-d2'), 0o700);
try {
[
[entry, common.tmpDir + '/node-test-realpath-d1/foo'],
[tmp('node-test-realpath-d1'),
common.tmpDir + '/node-test-realpath-d2'],
[tmp('node-test-realpath-d2/foo'), '../node-test-realpath-f2'],
[tmp('node-test-realpath-f2'), targetsAbsDir +
'/nested-index/one/realpath-c'],
[targetsAbsDir + '/nested-index/one/realpath-c', targetsAbsDir +
'/nested-index/two/realpath-c'],
[targetsAbsDir + '/nested-index/two/realpath-c',
common.tmpDir + '/cycles/root.js']
].forEach(function(t) {
try { fs.unlinkSync(t[0]); } catch (e) {}
fs.symlinkSync(t[1], t[0]);
unlink.push(t[0]);
});
} finally {
unlink.push(tmp('node-test-realpath-d2'));
}
const expected = tmpAbsDir + '/cycles/root.js';
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
asynctest(fs.realpath, [entry], callback, function(err, result) {
assertEqualPath(result, path.resolve(expected));
return true;
});
}
function test_non_symlinks(callback) {
console.log('test_non_symlinks');
const entrydir = path.dirname(tmpAbsDir);
const entry = tmpAbsDir.substr(entrydir.length + 1) + '/cycles/root.js';
const expected = tmpAbsDir + '/cycles/root.js';
const origcwd = process.cwd();
process.chdir(entrydir);
assertEqualPath(fs.realpathSync(entry), path.resolve(expected));
asynctest(fs.realpath, [entry], callback, function(err, result) {
process.chdir(origcwd);
assertEqualPath(result, path.resolve(expected));
return true;
});
}
const upone = path.join(process.cwd(), '..');
function test_escape_cwd(cb) {
console.log('test_escape_cwd');
asynctest(fs.realpath, ['..'], cb, function(er, uponeActual) {
assertEqualPath(upone, uponeActual,
'realpath("..") expected: ' + path.resolve(upone) +
' actual:' + uponeActual);
});
}
const uponeActual = fs.realpathSync('..');
assertEqualPath(upone, uponeActual,
'realpathSync("..") expected: ' + path.resolve(upone) +
' actual:' + uponeActual);
// going up with .. multiple times
// .
// `-- a/
// |-- b/
// | `-- e -> ..
// `-- d -> ..
// realpath(a/b/e/d/a/b/e/d/a) ==> a
function test_up_multiple(cb) {
console.error('test_up_multiple');
if (skipSymlinks) {
common.skip('symlink test (no privs)');
return runNextTest();
}
function cleanup() {
['a/b',
'a'
].forEach(function(folder) {
try {fs.rmdirSync(tmp(folder));} catch (ex) {}
});
}
function setup() {
cleanup();
}
setup();
fs.mkdirSync(tmp('a'), 0o755);
fs.mkdirSync(tmp('a/b'), 0o755);
fs.symlinkSync('..', tmp('a/d'), 'dir');
unlink.push(tmp('a/d'));
fs.symlinkSync('..', tmp('a/b/e'), 'dir');
unlink.push(tmp('a/b/e'));
const abedabed = tmp('abedabed'.split('').join('/'));
const abedabed_real = tmp('');
const abedabeda = tmp('abedabeda'.split('').join('/'));
const abedabeda_real = tmp('a');
assertEqualPath(fs.realpathSync(abedabeda), abedabeda_real);
assertEqualPath(fs.realpathSync(abedabed), abedabed_real);
fs.realpath(abedabeda, function(er, real) {
if (er) throw er;
assertEqualPath(abedabeda_real, real);
fs.realpath(abedabed, function(er, real) {
if (er) throw er;
assertEqualPath(abedabed_real, real);
cb();
cleanup();
});
});
}
// absolute symlinks with children.
// .
// `-- a/
// |-- b/
// | `-- c/
// | `-- x.txt
// `-- link -> /tmp/node-test-realpath-abs-kids/a/b/
// realpath(root+'/a/link/c/x.txt') ==> root+'/a/b/c/x.txt'
function test_abs_with_kids(cb) {
console.log('test_abs_with_kids');
// this one should still run, even if skipSymlinks is set,
// because it uses a junction.
const type = skipSymlinks ? 'junction' : 'dir';
console.log('using type=%s', type);
const root = tmpAbsDir + '/node-test-realpath-abs-kids';
function cleanup() {
['/a/b/c/x.txt',
'/a/link'
].forEach(function(file) {
try {fs.unlinkSync(root + file);} catch (ex) {}
});
['/a/b/c',
'/a/b',
'/a',
''
].forEach(function(folder) {
try {fs.rmdirSync(root + folder);} catch (ex) {}
});
}
function setup() {
cleanup();
['',
'/a',
'/a/b',
'/a/b/c'
].forEach(function(folder) {
console.log('mkdir ' + root + folder);
fs.mkdirSync(root + folder, 0o700);
});
fs.writeFileSync(root + '/a/b/c/x.txt', 'foo');
fs.symlinkSync(root + '/a/b', root + '/a/link', type);
}
setup();
const linkPath = root + '/a/link/c/x.txt';
const expectPath = root + '/a/b/c/x.txt';
const actual = fs.realpathSync(linkPath);
// console.log({link:linkPath,expect:expectPath,actual:actual},'sync');
assertEqualPath(actual, path.resolve(expectPath));
asynctest(fs.realpath, [linkPath], cb, function(er, actual) {
// console.log({link:linkPath,expect:expectPath,actual:actual},'async');
assertEqualPath(actual, path.resolve(expectPath));
cleanup();
});
}
// ----------------------------------------------------------------------------
const tests = [
test_simple_error_callback,
test_simple_relative_symlink,
test_simple_absolute_symlink,
test_deep_relative_file_symlink,
test_deep_relative_dir_symlink,
test_cyclic_link_protection,
test_cyclic_link_overprotection,
test_relative_input_cwd,
test_deep_symlink_mix,
test_non_symlinks,
test_escape_cwd,
test_abs_with_kids,
test_up_multiple
];
const numtests = tests.length;
var testsRun = 0;
function runNextTest(err) {
if (err) throw err;
const test = tests.shift();
if (!test) {
return console.log(numtests +
' subtests completed OK for fs.realpath');
}
testsRun++;
test(runNextTest);
}
assertEqualPath(root, fs.realpathSync('/'));
fs.realpath('/', function(err, result) {
assert.ifError(err);
assertEqualPath(root, result);
});
function runTest() {
const tmpDirs = ['cycles', 'cycles/folder'];
tmpDirs.forEach(function(t) {
t = tmp(t);
fs.mkdirSync(t, 0o700);
});
fs.writeFileSync(tmp('cycles/root.js'), "console.error('roooot!');");
console.error('start tests');
runNextTest();
}
process.on('exit', function() {
assert.equal(numtests, testsRun);
assert.equal(async_completed, async_expected);
});