var shell = require('..'); var common = require('../src/common'); var assert = require('assert'), fs = require('fs'), numLines = require('./utils/utils').numLines; shell.config.silent = true; var isWindows = common.platform === 'win'; // On Windows, symlinks for files need admin permissions. This helper // skips certain tests if we are on Windows and got an EPERM error function skipOnWinForEPERM (action, test) { action(); var error = shell.error(); if (isWindows && error && /EPERM:/.test(error)) { console.log("Got EPERM when testing symlinks on Windows. Assuming non-admin environment and skipping test."); } else { test(); } } shell.rm('-rf', 'tmp'); shell.mkdir('tmp'); // // Invalids // var result = shell.cp(); assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(result.stderr, 'cp: missing and/or '); result = shell.cp('file1'); assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(result.stderr, 'cp: missing and/or '); result = shell.cp('-f'); assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(result.stderr, 'cp: missing and/or '); shell.rm('-rf', 'tmp/*'); result = shell.cp('-@', 'resources/file1', 'tmp/file1'); // option not supported, files OK assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(fs.existsSync('tmp/file1'), false); assert.equal(result.stderr, 'cp: option not recognized: @'); result = shell.cp('-Z', 'asdfasdf', 'tmp/file2'); // option not supported, files NOT OK assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(fs.existsSync('tmp/file2'), false); assert.equal(result.stderr, 'cp: option not recognized: Z'); result = shell.cp('asdfasdf', 'tmp'); // source does not exist assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(numLines(result.stderr), 1); assert.equal(fs.existsSync('tmp/asdfasdf'), false); assert.equal(result.stderr, 'cp: no such file or directory: asdfasdf'); result = shell.cp('asdfasdf1', 'asdfasdf2', 'tmp'); // sources do not exist assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(numLines(result.stderr), 2); assert.equal(fs.existsSync('tmp/asdfasdf1'), false); assert.equal(fs.existsSync('tmp/asdfasdf2'), false); assert.equal(result.stderr, 'cp: no such file or directory: asdfasdf1\ncp: no such file or directory: asdfasdf2'); result = shell.cp('asdfasdf1', 'asdfasdf2', 'resources/file1'); // too many sources (dest is file) assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(result.stderr, 'cp: dest is not a directory (too many sources)'); result = shell.cp('-n', 'resources/file1', 'resources/file2'); // dest already exists assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(result.stderr, 'cp: dest file already exists: resources/file2'); result = shell.cp('resources/file1', 'resources/file2', 'tmp/a_file'); // too many sources assert.ok(shell.error()); assert.equal(result.code, 1); assert.equal(fs.existsSync('tmp/a_file'), false); assert.equal(result.stderr, 'cp: dest is not a directory (too many sources)'); // // Valids // // -f by default result = shell.cp('resources/file2', 'resources/copyfile2'); result = shell.cp('resources/file1', 'resources/file2'); // dest already exists assert.ok(!shell.error()); assert.equal(result.code, 0); assert.ok(!result.stderr); assert.equal(shell.cat('resources/file1') + '', shell.cat('resources/file2') + ''); // after cp shell.mv('resources/copyfile2', 'resources/file2'); // restore assert.ok(!shell.error()); // -f (explicitly) result = shell.cp('resources/file2', 'resources/copyfile2'); result = shell.cp('-f', 'resources/file1', 'resources/file2'); // dest already exists assert.ok(!shell.error()); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(shell.cat('resources/file1') + '', shell.cat('resources/file2') + ''); // after cp shell.mv('resources/copyfile2', 'resources/file2'); // restore assert.ok(!shell.error()); assert.equal(result.code, 0); // simple - to dir result = shell.cp('resources/file1', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(fs.existsSync('tmp/file1'), true); // simple - to file result = shell.cp('resources/file2', 'tmp/file2'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(fs.existsSync('tmp/file2'), true); // simple - file list shell.rm('-rf', 'tmp/*'); result = shell.cp('resources/file1', 'resources/file2', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(fs.existsSync('tmp/file1'), true); assert.equal(fs.existsSync('tmp/file2'), true); // simple - file list, array syntax shell.rm('-rf', 'tmp/*'); result = shell.cp(['resources/file1', 'resources/file2'], 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(fs.existsSync('tmp/file1'), true); assert.equal(fs.existsSync('tmp/file2'), true); result = shell.cp('resources/file2', 'tmp/file3'); assert.equal(fs.existsSync('tmp/file3'), true); result = shell.cp('-f', 'resources/file2', 'tmp/file3'); // file exists, but -f specified assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(fs.existsSync('tmp/file3'), true); // glob shell.rm('-rf', 'tmp/*'); result = shell.cp('resources/file?', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.ok(fs.existsSync('tmp/file1')); assert.ok(fs.existsSync('tmp/file2')); assert.ok(!fs.existsSync('tmp/file1.js')); assert.ok(!fs.existsSync('tmp/file2.js')); assert.ok(!fs.existsSync('tmp/file1.txt')); assert.ok(!fs.existsSync('tmp/file2.txt')); // wildcard shell.rm('tmp/file1', 'tmp/file2'); result = shell.cp('resources/file*', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.ok(fs.existsSync('tmp/file1')); assert.ok(fs.existsSync('tmp/file2')); assert.ok(fs.existsSync('tmp/file1.js')); assert.ok(fs.existsSync('tmp/file2.js')); assert.ok(fs.existsSync('tmp/file1.txt')); assert.ok(fs.existsSync('tmp/file2.txt')); // recursive, with regular files shell.rm('-rf', 'tmp/*'); result = shell.cp('-R', 'resources/file1', 'resources/file2', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.ok(fs.existsSync('tmp/file1')); assert.ok(fs.existsSync('tmp/file2')); // recursive, nothing exists shell.rm('-rf', 'tmp/*'); result = shell.cp('-R', 'resources/cp', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + ''); //recursive, nothing exists, source ends in '/' (see Github issue #15) shell.rm('-rf', 'tmp/*'); result = shell.cp('-R', 'resources/cp/', 'tmp/'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(shell.ls('-R', 'resources/cp') + '', shell.ls('-R', 'tmp/cp') + ''); // recursive, globbing regular files with extension (see Github issue #376) shell.rm('-rf', 'tmp/*'); result = shell.cp('-R', 'resources/file*.txt', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.ok(fs.existsSync('tmp/file1.txt')); assert.ok(fs.existsSync('tmp/file2.txt')); // recursive, copying one regular file (also related to Github issue #376) shell.rm('-rf', 'tmp/*'); result = shell.cp('-R', 'resources/file1.txt', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.ok(fs.existsSync('tmp/file1.txt')); assert.ok(!fs.statSync('tmp/file1.txt').isDirectory()); // don't let it be a dir //recursive, everything exists, no force flag shell.rm('-rf', 'tmp/*'); result = shell.cp('-R', 'resources/cp', 'tmp'); result = shell.cp('-R', 'resources/cp', 'tmp'); assert.equal(shell.error(), null); // crash test only assert.ok(!result.stderr); assert.equal(result.code, 0); //recursive, everything exists, with force flag shell.rm('-rf', 'tmp/*'); result = shell.cp('-R', 'resources/cp', 'tmp'); shell.ShellString('changing things around').to('tmp/cp/dir_a/z'); assert.notEqual(shell.cat('resources/cp/dir_a/z') + '', shell.cat('tmp/cp/dir_a/z') + ''); // before cp result = shell.cp('-Rf', 'resources/cp', 'tmp'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(shell.cat('resources/cp/dir_a/z') + '', shell.cat('tmp/cp/dir_a/z') + ''); // after cp //recursive, creates dest dir since it's only one level deep (see Github issue #44) shell.rm('-rf', 'tmp/*'); result = shell.cp('-r', 'resources/issue44', 'tmp/dir2'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(shell.ls('-R', 'resources/issue44') + '', shell.ls('-R', 'tmp/dir2') + ''); assert.equal(shell.cat('resources/issue44/main.js') + '', shell.cat('tmp/dir2/main.js') + ''); //recursive, does *not* create dest dir since it's too deep (see Github issue #44) shell.rm('-rf', 'tmp/*'); result = shell.cp('-r', 'resources/issue44', 'tmp/dir2/dir3'); assert.ok(shell.error()); assert.equal(result.stderr, 'cp: cannot create directory \'tmp/dir2/dir3\': No such file or directory'); assert.equal(result.code, 1); assert.equal(fs.existsSync('tmp/dir2'), false); //recursive, copies entire directory shell.rm('-rf', 'tmp/*'); result = shell.cp('-r', 'resources/cp/dir_a', 'tmp/dest'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(fs.existsSync('tmp/dest/z'), true); //recursive, with trailing slash, does the exact same shell.rm('-rf', 'tmp/*'); result = shell.cp('-r', 'resources/cp/dir_a/', 'tmp/dest'); assert.equal(shell.error(), null); assert.equal(fs.existsSync('tmp/dest/z'), true); // On Windows, permission bits are quite different so skip those tests for now if (common.platform !== 'win') { //preserve mode bits shell.rm('-rf', 'tmp/*'); var execBit = parseInt('001', 8); assert.equal(fs.statSync('resources/cp-mode-bits/executable').mode & execBit, execBit); shell.cp('resources/cp-mode-bits/executable', 'tmp/executable'); assert.equal(fs.statSync('resources/cp-mode-bits/executable').mode, fs.statSync('tmp/executable').mode); } // Make sure hidden files are copied recursively shell.rm('-rf', 'tmp/'); result = shell.cp('-r', 'resources/ls/', 'tmp/'); assert.ok(!shell.error()); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.ok(fs.existsSync('tmp/.hidden_file')); // no-recursive will copy regular files only shell.rm('-rf', 'tmp/'); shell.mkdir('tmp/'); result = shell.cp('resources/file1.txt', 'resources/ls/', 'tmp/'); assert.ok(shell.error()); assert.ok(!fs.existsSync('tmp/.hidden_file')); // doesn't copy dir contents assert.ok(!fs.existsSync('tmp/ls')); // doesn't copy dir itself assert.ok(fs.existsSync('tmp/file1.txt')); // no-recursive will copy regular files only shell.rm('-rf', 'tmp/'); shell.mkdir('tmp/'); result = shell.cp('resources/file1.txt', 'resources/file2.txt', 'resources/cp', 'resources/ls/', 'tmp/'); assert.ok(shell.error()); assert.ok(!fs.existsSync('tmp/.hidden_file')); // doesn't copy dir contents assert.ok(!fs.existsSync('tmp/ls')); // doesn't copy dir itself assert.ok(!fs.existsSync('tmp/a')); // doesn't copy dir contents assert.ok(!fs.existsSync('tmp/cp')); // doesn't copy dir itself assert.ok(fs.existsSync('tmp/file1.txt')); assert.ok(fs.existsSync('tmp/file2.txt')); // Recursive, copies entire directory with no symlinks and -L option does not cause change in behavior. shell.rm('-rf', 'tmp/*'); result = shell.cp('-rL', 'resources/cp/dir_a', 'tmp/dest'); assert.equal(shell.error(), null); assert.ok(!result.stderr); assert.equal(result.code, 0); assert.equal(fs.existsSync('tmp/dest/z'), true); // Test max depth. shell.rm('-rf', 'tmp/'); shell.mkdir('tmp/'); shell.config.maxdepth = 32; var directory = ''; for (var i = 1; i < 40; i++) { directory += '/'+i; } var directory32deep = ''; for (var i = 1; i < 32; i++) { directory32deep += '/'+i; } shell.mkdir('-p', 'tmp/0' + directory); shell.cp('-r', 'tmp/0', 'tmp/copytestdepth'); // Check full directory exists. assert.ok(shell.test('-d', 'tmp/0/' + directory)); // Check full copy of directory does not exist. assert.ok(!shell.test('-d', 'tmp/copytestdepth'+directory)); // Check last directory to exist is bellow maxdepth. assert.ok(shell.test('-d', 'tmp/copytestdepth'+directory32deep)); assert.ok(!shell.test('-d', 'tmp/copytestdepth'+directory32deep+'/32')); // Only complete sym link checks if script has permission to do so. skipOnWinForEPERM(shell.ln.bind(shell, '-s', 'tmp/0', 'tmp/symlinktest'), function () { if (!shell.test('-L', 'tmp/symlinktest')) { return; } shell.rm('-rf', 'tmp/symlinktest'); // Create sym links to check for cycle. shell.cd('tmp/0/1/2/3/4'); shell.ln('-s', '../../../2', 'link'); shell.ln('-s', './5/6/7', 'link1'); shell.cd('../../../../../..'); assert.ok(shell.test('-d', 'tmp/')); shell.rm('-fr', 'tmp/copytestdepth'); shell.cp('-r', 'tmp/0', 'tmp/copytestdepth'); assert.ok(shell.test('-d', 'tmp/copytestdepth/1/2/3/4/link/3/4/link/3/4')); // Test copying of symlinked files cp -L. shell.rm('-fr', 'tmp'); shell.mkdir('-p', 'tmp/sub'); shell.mkdir('-p', 'tmp/new'); shell.cp('-f', 'resources/file1.txt', 'tmp/sub/file.txt'); shell.cd('tmp/sub'); shell.ln('-s', 'file.txt', 'foo.lnk'); shell.ln('-s', 'file.txt', 'sym.lnk'); shell.cd('..'); shell.cp('-L', 'sub/*', 'new/'); // Ensure copies are files. shell.cd('new'); shell.cp('-f', '../../resources/file2.txt', 'file.txt'); assert.equal(shell.cat('file.txt').toString(), 'test2\n'); // Ensure other files have not changed. assert.equal(shell.cat('foo.lnk').toString(), 'test1\n'); assert.equal(shell.cat('sym.lnk').toString(), 'test1\n'); // Ensure the links are converted to files. assert.equal(shell.test('-L', 'foo.lnk'), false); assert.equal(shell.test('-L', 'sym.lnk'), false); shell.cd('../..'); // Test with recurisve option and symlinks. shell.rm('-fr', 'tmp'); shell.mkdir('-p', 'tmp/sub/sub1'); shell.cp('-f', 'resources/file1.txt', 'tmp/sub/file.txt'); shell.cp('-f', 'resources/file1.txt', 'tmp/sub/sub1/file.txt'); shell.cd('tmp/sub'); shell.ln('-s', 'file.txt', 'foo.lnk'); shell.ln('-s', 'file.txt', 'sym.lnk'); shell.cd('sub1'); shell.ln('-s', '../file.txt', 'foo.lnk'); shell.ln('-s', '../file.txt', 'sym.lnk'); // Ensure file reads from proper source. assert.equal(shell.cat('file.txt').toString(), 'test1\n'); assert.equal(shell.cat('foo.lnk').toString(), 'test1\n'); assert.equal(shell.cat('sym.lnk').toString(), 'test1\n'); assert.equal(shell.test('-L', 'foo.lnk'), true); assert.equal(shell.test('-L', 'sym.lnk'), true); shell.cd('../..'); shell.cp('-rL', 'sub/', 'new/'); shell.cd('new'); // Ensure copies of files are symlinks by updating file contents. shell.cp('-f', '../../resources/file2.txt', 'file.txt'); assert.equal(shell.cat('file.txt').toString(), 'test2\n'); // Ensure other files have not changed. assert.equal(shell.cat('foo.lnk').toString(), 'test1\n'); assert.equal(shell.cat('sym.lnk').toString(), 'test1\n'); // Ensure the links are converted to files. assert.equal(shell.test('-L', 'foo.lnk'), false); assert.equal(shell.test('-L', 'sym.lnk'), false); shell.cd('sub1'); shell.cp('-f', '../../../resources/file2.txt', 'file.txt'); assert.equal(shell.cat('file.txt').toString(), 'test2\n'); // Ensure other files have not changed. assert.equal(shell.cat('foo.lnk').toString(), 'test1\n'); assert.equal(shell.cat('sym.lnk').toString(), 'test1\n'); // Ensure the links are converted to files. assert.equal(shell.test('-L', 'foo.lnk'), false); assert.equal(shell.test('-L', 'sym.lnk'), false); }); shell.exit(123);