2 var common = require('../common');
3 var assert = require('assert');
4 var fs = require('fs');
5 var path = require('path');
6 var exec = require('child_process').exec;
7 var async_completed = 0, async_expected = 0, unlink = [];
8 var skipSymlinks = false;
10 common.refreshTmpDir();
13 if (common.isWindows) {
14 // something like "C:\\"
15 root = process.cwd().substr(0, 3);
17 // On Windows, creating symlinks requires admin privileges.
18 // We'll only try to run symlink test if we have enough privileges.
20 exec('whoami /priv', function(err, o) {
21 if (err || o.indexOf('SeCreateSymbolicLinkPrivilege') == -1) {
27 // better safe than sorry
29 process.nextTick(runTest);
32 process.nextTick(runTest);
37 return path.join(common.tmpDir, p);
40 var fixturesAbsDir = common.fixturesDir;
41 var tmpAbsDir = common.tmpDir;
42 assert(fixturesAbsDir !== tmpAbsDir);
44 console.error('absolutes\n%s\n%s', fixturesAbsDir, tmpAbsDir);
46 function asynctest(testBlock, args, callback, assertBlock) {
48 testBlock.apply(testBlock, args.concat(function(err) {
49 var ignoreError = false;
52 ignoreError = assertBlock.apply(assertBlock, arguments);
59 callback(ignoreError ? null : err);
64 function test_simple_error_callback(cb) {
67 fs.realpath('/this/path/does/not/exist', function(err, s) {
74 process.on('exit', function() {
75 assert.equal(ncalls, 1);
79 function test_simple_relative_symlink(callback) {
80 console.log('test_simple_relative_symlink');
82 console.log('1..0 # Skipped: symlink test (no privs)');
85 var entry = common.tmpDir + '/symlink',
86 expected = common.tmpDir + '/cycles/root.js';
88 [entry, '../' + common.tmpDirName + '/cycles/root.js']
89 ].forEach(function(t) {
90 try {fs.unlinkSync(t[0]);}catch (e) {}
91 console.log('fs.symlinkSync(%j, %j, %j)', t[1], t[0], 'file');
92 fs.symlinkSync(t[1], t[0], 'file');
95 var result = fs.realpathSync(entry);
96 assert.equal(result, path.resolve(expected));
97 asynctest(fs.realpath, [entry], callback, function(err, result) {
98 assert.equal(result, path.resolve(expected));
102 function test_simple_absolute_symlink(callback) {
103 console.log('test_simple_absolute_symlink');
105 // this one should still run, even if skipSymlinks is set,
106 // because it uses a junction.
107 var type = skipSymlinks ? 'junction' : 'dir';
109 console.log('using type=%s', type);
111 var entry = tmpAbsDir + '/symlink',
112 expected = fixturesAbsDir + '/nested-index/one';
115 ].forEach(function(t) {
116 try {fs.unlinkSync(t[0]);} catch (e) {}
117 console.error('fs.symlinkSync(%j, %j, %j)', t[1], t[0], type);
118 fs.symlinkSync(t[1], t[0], type);
121 var result = fs.realpathSync(entry);
122 assert.equal(result, path.resolve(expected));
123 asynctest(fs.realpath, [entry], callback, function(err, result) {
124 assert.equal(result, path.resolve(expected));
128 function test_deep_relative_file_symlink(callback) {
129 console.log('test_deep_relative_file_symlink');
131 console.log('1..0 # Skipped: symlink test (no privs)');
132 return runNextTest();
135 var expected = path.join(common.fixturesDir, 'cycles', 'root.js');
136 var linkData1 = '../../cycles/root.js';
137 var linkPath1 = path.join(common.fixturesDir,
138 'nested-index', 'one', 'symlink1.js');
139 try {fs.unlinkSync(linkPath1);} catch (e) {}
140 fs.symlinkSync(linkData1, linkPath1, 'file');
142 var linkData2 = '../one/symlink1.js';
143 var entry = path.join(common.fixturesDir,
144 'nested-index', 'two', 'symlink1-b.js');
145 try {fs.unlinkSync(entry);} catch (e) {}
146 fs.symlinkSync(linkData2, entry, 'file');
147 unlink.push(linkPath1);
150 assert.equal(fs.realpathSync(entry), path.resolve(expected));
151 asynctest(fs.realpath, [entry], callback, function(err, result) {
152 assert.equal(result, path.resolve(expected));
156 function test_deep_relative_dir_symlink(callback) {
157 console.log('test_deep_relative_dir_symlink');
159 console.log('1..0 # Skipped: symlink test (no privs)');
160 return runNextTest();
162 var expected = path.join(common.fixturesDir, 'cycles', 'folder');
163 var linkData1b = '../../cycles/folder';
164 var linkPath1b = path.join(common.fixturesDir,
165 'nested-index', 'one', 'symlink1-dir');
166 try {fs.unlinkSync(linkPath1b);} catch (e) {}
167 fs.symlinkSync(linkData1b, linkPath1b, 'dir');
169 var linkData2b = '../one/symlink1-dir';
170 var entry = path.join(common.fixturesDir,
171 'nested-index', 'two', 'symlink12-dir');
172 try {fs.unlinkSync(entry);} catch (e) {}
173 fs.symlinkSync(linkData2b, entry, 'dir');
174 unlink.push(linkPath1b);
177 assert.equal(fs.realpathSync(entry), path.resolve(expected));
179 asynctest(fs.realpath, [entry], callback, function(err, result) {
180 assert.equal(result, path.resolve(expected));
184 function test_cyclic_link_protection(callback) {
185 console.log('test_cyclic_link_protection');
187 console.log('1..0 # Skipped: symlink test (no privs)');
188 return runNextTest();
190 var entry = common.tmpDir + '/cycles/realpath-3a';
192 [entry, '../cycles/realpath-3b'],
193 [common.tmpDir + '/cycles/realpath-3b', '../cycles/realpath-3c'],
194 [common.tmpDir + '/cycles/realpath-3c', '../cycles/realpath-3a']
195 ].forEach(function(t) {
196 try {fs.unlinkSync(t[0]);} catch (e) {}
197 fs.symlinkSync(t[1], t[0], 'dir');
200 assert.throws(function() { fs.realpathSync(entry); });
201 asynctest(fs.realpath, [entry], callback, function(err, result) {
202 assert.ok(err && true);
207 function test_cyclic_link_overprotection(callback) {
208 console.log('test_cyclic_link_overprotection');
210 console.log('1..0 # Skipped: symlink test (no privs)');
211 return runNextTest();
213 var cycles = common.tmpDir + '/cycles';
214 var expected = fs.realpathSync(cycles);
215 var folder = cycles + '/folder';
216 var link = folder + '/cycles';
217 var testPath = cycles;
218 for (var i = 0; i < 10; i++) testPath += '/folder/cycles';
219 try {fs.unlinkSync(link);} catch (ex) {}
220 fs.symlinkSync(cycles, link, 'dir');
222 assert.equal(fs.realpathSync(testPath), path.resolve(expected));
223 asynctest(fs.realpath, [testPath], callback, function(er, res) {
224 assert.equal(res, path.resolve(expected));
228 function test_relative_input_cwd(callback) {
229 console.log('test_relative_input_cwd');
231 console.log('1..0 # Skipped: symlink test (no privs)');
232 return runNextTest();
235 // we need to get the relative path to the tmp dir from cwd.
236 // When the test runner is running it, that will be .../node/test
237 // but it's more common to run `./node test/.../`, so detect it here.
238 var entrydir = process.cwd();
239 var entry = common.tmpDir.substr(entrydir.length + 1) + '/cycles/realpath-3a';
240 var expected = common.tmpDir + '/cycles/root.js';
242 [entry, '../cycles/realpath-3b'],
243 [common.tmpDir + '/cycles/realpath-3b', '../cycles/realpath-3c'],
244 [common.tmpDir + '/cycles/realpath-3c', 'root.js']
245 ].forEach(function(t) {
247 console.error('fn=%j', fn);
248 try {fs.unlinkSync(fn);} catch (e) {}
249 var b = path.basename(t[1]);
250 var type = (b === 'root.js' ? 'file' : 'dir');
251 console.log('fs.symlinkSync(%j, %j, %j)', t[1], fn, type);
252 fs.symlinkSync(t[1], fn, 'file');
256 var origcwd = process.cwd();
257 process.chdir(entrydir);
258 assert.equal(fs.realpathSync(entry), path.resolve(expected));
259 asynctest(fs.realpath, [entry], callback, function(err, result) {
260 process.chdir(origcwd);
261 assert.equal(result, path.resolve(expected));
266 function test_deep_symlink_mix(callback) {
267 console.log('test_deep_symlink_mix');
268 if (common.isWindows) {
269 // This one is a mix of files and directories, and it's quite tricky
270 // to get the file/dir links sorted out correctly.
271 console.log('1..0 # Skipped: symlink test (no privs)');
272 return runNextTest();
276 /tmp/node-test-realpath-f1 -> $tmpDir/node-test-realpath-d1/foo
277 /tmp/node-test-realpath-d1 -> $tmpDir/node-test-realpath-d2
278 /tmp/node-test-realpath-d2/foo -> $tmpDir/node-test-realpath-f2
279 /tmp/node-test-realpath-f2
280 -> /node/test/fixtures/nested-index/one/realpath-c
281 /node/test/fixtures/nested-index/one/realpath-c
282 -> /node/test/fixtures/nested-index/two/realpath-c
283 /node/test/fixtures/nested-index/two/realpath-c -> $tmpDir/cycles/root.js
284 /node/test/fixtures/cycles/root.js (hard)
286 var entry = tmp('node-test-realpath-f1');
287 try { fs.unlinkSync(tmp('node-test-realpath-d2/foo')); } catch (e) {}
288 try { fs.rmdirSync(tmp('node-test-realpath-d2')); } catch (e) {}
289 fs.mkdirSync(tmp('node-test-realpath-d2'), 0o700);
292 [entry, common.tmpDir + '/node-test-realpath-d1/foo'],
293 [tmp('node-test-realpath-d1'),
294 common.tmpDir + '/node-test-realpath-d2'],
295 [tmp('node-test-realpath-d2/foo'), '../node-test-realpath-f2'],
296 [tmp('node-test-realpath-f2'), fixturesAbsDir +
297 '/nested-index/one/realpath-c'],
298 [fixturesAbsDir + '/nested-index/one/realpath-c', fixturesAbsDir +
299 '/nested-index/two/realpath-c'],
300 [fixturesAbsDir + '/nested-index/two/realpath-c',
301 common.tmpDir + '/cycles/root.js']
302 ].forEach(function(t) {
303 try { fs.unlinkSync(t[0]); } catch (e) {}
304 fs.symlinkSync(t[1], t[0]);
308 unlink.push(tmp('node-test-realpath-d2'));
310 var expected = tmpAbsDir + '/cycles/root.js';
311 assert.equal(fs.realpathSync(entry), path.resolve(expected));
312 asynctest(fs.realpath, [entry], callback, function(err, result) {
313 assert.equal(result, path.resolve(expected));
318 function test_non_symlinks(callback) {
319 console.log('test_non_symlinks');
320 var entrydir = path.dirname(tmpAbsDir);
321 var entry = tmpAbsDir.substr(entrydir.length + 1) + '/cycles/root.js';
322 var expected = tmpAbsDir + '/cycles/root.js';
323 var origcwd = process.cwd();
324 process.chdir(entrydir);
325 assert.equal(fs.realpathSync(entry), path.resolve(expected));
326 asynctest(fs.realpath, [entry], callback, function(err, result) {
327 process.chdir(origcwd);
328 assert.equal(result, path.resolve(expected));
333 var upone = path.join(process.cwd(), '..');
334 function test_escape_cwd(cb) {
335 console.log('test_escape_cwd');
336 asynctest(fs.realpath, ['..'], cb, function(er, uponeActual) {
337 assert.equal(upone, uponeActual,
338 'realpath("..") expected: ' + path.resolve(upone) +
339 ' actual:' + uponeActual);
342 var uponeActual = fs.realpathSync('..');
343 assert.equal(upone, uponeActual,
344 'realpathSync("..") expected: ' + path.resolve(upone) +
345 ' actual:' + uponeActual);
348 // going up with .. multiple times
354 // realpath(a/b/e/d/a/b/e/d/a) ==> a
355 function test_up_multiple(cb) {
356 console.error('test_up_multiple');
358 console.log('1..0 # Skipped: symlink test (no privs)');
359 return runNextTest();
364 ].forEach(function(folder) {
365 try {fs.rmdirSync(tmp(folder));} catch (ex) {}
372 fs.mkdirSync(tmp('a'), 0o755);
373 fs.mkdirSync(tmp('a/b'), 0o755);
374 fs.symlinkSync('..', tmp('a/d'), 'dir');
375 unlink.push(tmp('a/d'));
376 fs.symlinkSync('..', tmp('a/b/e'), 'dir');
377 unlink.push(tmp('a/b/e'));
379 var abedabed = tmp('abedabed'.split('').join('/'));
380 var abedabed_real = tmp('');
382 var abedabeda = tmp('abedabeda'.split('').join('/'));
383 var abedabeda_real = tmp('a');
385 assert.equal(fs.realpathSync(abedabeda), abedabeda_real);
386 assert.equal(fs.realpathSync(abedabed), abedabed_real);
387 fs.realpath(abedabeda, function(er, real) {
389 assert.equal(abedabeda_real, real);
390 fs.realpath(abedabed, function(er, real) {
392 assert.equal(abedabed_real, real);
400 // absolute symlinks with children.
406 // `-- link -> /tmp/node-test-realpath-abs-kids/a/b/
407 // realpath(root+'/a/link/c/x.txt') ==> root+'/a/b/c/x.txt'
408 function test_abs_with_kids(cb) {
409 console.log('test_abs_with_kids');
411 // this one should still run, even if skipSymlinks is set,
412 // because it uses a junction.
413 var type = skipSymlinks ? 'junction' : 'dir';
415 console.log('using type=%s', type);
417 var root = tmpAbsDir + '/node-test-realpath-abs-kids';
421 ].forEach(function(file) {
422 try {fs.unlinkSync(root + file);} catch (ex) {}
428 ].forEach(function(folder) {
429 try {fs.rmdirSync(root + folder);} catch (ex) {}
438 ].forEach(function(folder) {
439 console.log('mkdir ' + root + folder);
440 fs.mkdirSync(root + folder, 0o700);
442 fs.writeFileSync(root + '/a/b/c/x.txt', 'foo');
443 fs.symlinkSync(root + '/a/b', root + '/a/link', type);
446 var linkPath = root + '/a/link/c/x.txt';
447 var expectPath = root + '/a/b/c/x.txt';
448 var actual = fs.realpathSync(linkPath);
449 // console.log({link:linkPath,expect:expectPath,actual:actual},'sync');
450 assert.equal(actual, path.resolve(expectPath));
451 asynctest(fs.realpath, [linkPath], cb, function(er, actual) {
452 // console.log({link:linkPath,expect:expectPath,actual:actual},'async');
453 assert.equal(actual, path.resolve(expectPath));
458 function test_lying_cache_liar(cb) {
461 // this should not require *any* stat calls, since everything
462 // checked by realpath will be found in the cache.
463 console.log('test_lying_cache_liar');
464 var cache = { '/foo/bar/baz/bluff' : '/foo/bar/bluff',
465 '/1/2/3/4/5/6/7' : '/1',
469 '/a/b/d' : '/a/b/d' };
470 if (common.isWindows) {
472 Object.keys(cache).forEach(function(k) {
473 wc[ path.resolve(k) ] = path.resolve(cache[k]);
478 var bluff = path.resolve('/foo/bar/baz/bluff');
479 var rps = fs.realpathSync(bluff, cache);
480 assert.equal(cache[bluff], rps);
481 var nums = path.resolve('/1/2/3/4/5/6/7');
482 var called = false; // no sync cb calling!
483 fs.realpath(nums, cache, function(er, rp) {
485 assert.equal(cache[nums], rp);
488 assert(called === false);
490 var test = path.resolve('/a/b/c/d'),
491 expect = path.resolve('/a/b/d');
492 var actual = fs.realpathSync(test, cache);
493 assert.equal(expect, actual);
494 fs.realpath(test, cache, function(er, actual) {
495 assert.equal(expect, actual);
500 // ----------------------------------------------------------------------------
503 test_simple_error_callback,
504 test_simple_relative_symlink,
505 test_simple_absolute_symlink,
506 test_deep_relative_file_symlink,
507 test_deep_relative_dir_symlink,
508 test_cyclic_link_protection,
509 test_cyclic_link_overprotection,
510 test_relative_input_cwd,
511 test_deep_symlink_mix,
515 test_lying_cache_liar,
518 var numtests = tests.length;
520 function runNextTest(err) {
522 var test = tests.shift();
524 return console.log(numtests +
525 ' subtests completed OK for fs.realpath');
532 assert.equal(root, fs.realpathSync('/'));
533 fs.realpath('/', function(err, result) {
534 assert.equal(null, err);
535 assert.equal(root, result);
540 var tmpDirs = ['cycles', 'cycles/folder'];
541 tmpDirs.forEach(function(t) {
543 fs.mkdirSync(t, 0o700);
545 fs.writeFileSync(tmp('cycles/root.js'), "console.error('roooot!');");
546 console.error('start tests');
551 process.on('exit', function() {
552 assert.equal(numtests, testsRun);
553 unlink.forEach(function(path) { try {fs.unlinkSync(path);} catch (e) {} });
554 assert.equal(async_completed, async_expected);