diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..ebd6fc07 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,24 @@ +# http://www.appveyor.com/docs/appveyor-yml +# http://www.appveyor.com/docs/lang/nodejs-iojs + +environment: + matrix: + # node.js + - nodejs_version: "0.10" + - nodejs_version: "0.12" + - nodejs_version: "4" + - nodejs_version: "5" + +install: + - ps: Install-Product node $env:nodejs_version + - npm install + +test_script: + - node --version + - npm --version + - cmd: npm test + +build: off + +# build version format +version: "{build}" diff --git a/lib/dest/writeContents/writeDir.js b/lib/dest/writeContents/writeDir.js index dd5c9a4a..ef7622c9 100644 --- a/lib/dest/writeContents/writeDir.js +++ b/lib/dest/writeContents/writeDir.js @@ -6,7 +6,11 @@ var mkdirp = require('mkdirp'); var fo = require('../../fileOperations'); function writeDir(writePath, file, written) { - mkdirp(writePath, file.stat.mode, onMkdirp); + var mkdirpOpts = { + mode: file.stat.mode, + fs: fs, + }; + mkdirp(writePath, mkdirpOpts, onMkdirp); function onMkdirp(mkdirpErr) { if (mkdirpErr) { diff --git a/lib/dest/writeContents/writeStream.js b/lib/dest/writeContents/writeStream.js index a1a4c92b..e9af5f1d 100644 --- a/lib/dest/writeContents/writeStream.js +++ b/lib/dest/writeContents/writeStream.js @@ -9,7 +9,6 @@ function writeStream(writePath, file, written) { var opt = { mode: file.stat.mode, flag: file.flag, - autoClose: false, }; var outStream = fs.createWriteStream(writePath, opt); diff --git a/lib/fileOperations.js b/lib/fileOperations.js index b038e3bc..4c194d4d 100644 --- a/lib/fileOperations.js +++ b/lib/fileOperations.js @@ -69,8 +69,18 @@ function getTimesDiff(fsStat, vinylStat) { } function isOwner(fsStat) { + var hasGetuid = (typeof process.getuid === 'function'); + var hasGeteuid = (typeof process.geteuid === 'function'); + + // If we don't have either, assume we don't have permissions. + // This should only happen on Windows. + // Windows basically noops fchmod and errors on futimes called on directories. + if (!hasGeteuid && !hasGetuid) { + return false; + } + var uid; - if (typeof process.geteuid === 'function') { + if (hasGeteuid) { uid = process.geteuid(); } else { uid = process.getuid(); diff --git a/lib/prepareWrite.js b/lib/prepareWrite.js index 31bcd8a3..50d86cd1 100644 --- a/lib/prepareWrite.js +++ b/lib/prepareWrite.js @@ -54,7 +54,11 @@ function prepareWrite(outFolder, file, opt, cb) { file.path = writePath; // Mkdirp the folder the file is going in - mkdirp(writeFolder, options.dirMode, function(err) { + var mkdirpOpts = { + mode: options.dirMode, + fs: fs, + }; + mkdirp(writeFolder, mkdirpOpts, function(err) { if (err) { return cb(err); } diff --git a/lib/src/index.js b/lib/src/index.js index b8790c6a..a20f7052 100644 --- a/lib/src/index.js +++ b/lib/src/index.js @@ -1,5 +1,6 @@ 'use strict'; +var path = require('path'); var assign = require('object-assign'); var through2 = require('through2'); var gs = require('glob-stream'); @@ -13,6 +14,21 @@ var isValidGlob = require('is-valid-glob'); var getContents = require('./getContents'); var resolveSymlinks = require('./resolveSymlinks'); +function normalizePath(options) { + + function normalize(globFile, enc, cb) { + // TODO: probably move this somewhere + // Ref https://github.com/gulpjs/vinyl/issues/80 + var normalizedFile = assign({}, globFile, { + path: path.normalize(globFile.path), + }); + + cb(null, normalizedFile); + } + + return through2.obj(options, normalize); +} + function createFile(globFile, enc, cb) { cb(null, new File(globFile)); } @@ -36,6 +52,7 @@ function src(glob, opt) { var globStream = gs.create(glob, options); var outputStream = globStream + .pipe(normalizePath(options)) .pipe(resolveSymlinks(options)) .pipe(through2.obj(opt, createFile)); diff --git a/test/dest.js b/test/dest.js index 93921bcc..da9dcbe3 100644 --- a/test/dest.js +++ b/test/dest.js @@ -8,6 +8,7 @@ var fstatSpy = spies.fstatSpy; var vfs = require('../'); +var os = require('os'); var path = require('path'); var fs = require('graceful-fs'); var del = require('del'); @@ -29,8 +30,12 @@ var wipeOut = function() { fchmodSpy.reset(); futimesSpy.reset(); expect.restoreSpies(); - del.sync(path.join(__dirname, './fixtures/highwatermark')); - del.sync(path.join(__dirname, './out-fixtures/')); + + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + return del(path.join(__dirname, './fixtures/highwatermark')) + .then(function() { + return del(path.join(__dirname, './out-fixtures/')); + }); }; var dataWrap = function(fn) { @@ -299,16 +304,12 @@ describe('dest stream', function() { var expectedContents = fs.readFileSync(inputPath); var expectedCwd = __dirname; var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('655', 8); var expectedFile = new File({ base: inputBase, cwd: __dirname, path: inputPath, contents: expectedContents, - stat: { - mode: expectedMode, - }, }); var buffered = []; @@ -321,7 +322,6 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); done(); }; @@ -340,7 +340,6 @@ describe('dest stream', function() { var expectedContents = fs.readFileSync(inputPath); var expectedCwd = __dirname; var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('655', 8); var contentStream = through.obj(); var expectedFile = new File({ @@ -348,9 +347,6 @@ describe('dest stream', function() { cwd: __dirname, path: inputPath, contents: contentStream, - stat: { - mode: expectedMode, - }, }); var buffered = []; @@ -363,13 +359,13 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); bufEqual(fs.readFileSync(expectedPath), expectedContents).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); done(); }; var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); + var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered))); + bufferStream.on('finish', onEnd); stream.pipe(bufferStream); stream.write(expectedFile); setTimeout(function() { @@ -385,7 +381,6 @@ describe('dest stream', function() { var expectedPath = path.join(__dirname, './out-fixtures/test'); var expectedCwd = __dirname; var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('655', 8); var expectedFile = new File({ base: inputBase, @@ -396,7 +391,6 @@ describe('dest stream', function() { isDirectory: function() { return true; }, - mode: expectedMode, }, }); @@ -410,7 +404,6 @@ describe('dest stream', function() { buffered[0].path.should.equal(expectedPath, 'path should have changed'); fs.existsSync(expectedPath).should.equal(true); fs.lstatSync(expectedPath).isDirectory().should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); done(); }; @@ -494,359 +487,6 @@ describe('dest stream', function() { stream.end(); }); - it('should write new files with the specified mode', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMode = parseInt('744', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - chmodSpy.reset(); - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, mode: expectedMode }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should update file mode to match the vinyl mode', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var startMode = parseInt('0655', 8); - var expectedMode = parseInt('0722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - var buffered = []; - - var onEnd = function() { - should(chmodSpy.called).be.ok; - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, startMode); - - chmodSpy.reset(); - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should update directory mode to match the vinyl mode', function(done) { - var inputBase = path.join(__dirname, './fixtures/'); - var inputPath = path.join(__dirname, './fixtures/wow'); - var expectedPath = path.join(__dirname, './out-fixtures/wow'); - var expectedCwd = __dirname; - var expectedBase = path.join(__dirname, './out-fixtures'); - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: expectedPath, - stat: fs.statSync(inputPath), - }); - var startMode = firstFile.stat.mode; - var expectedMode = parseInt('727', 8); - - var expectedFile = new File(firstFile); - expectedFile.stat.mode = (startMode & ~parseInt('7777', 8)) | expectedMode; - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(2); - buffered[0].should.equal(firstFile); - buffered[1].should.equal(expectedFile); - buffered[0].cwd.should.equal(expectedCwd, 'cwd should have changed'); - buffered[0].base.should.equal(expectedBase, 'base should have changed'); - buffered[0].path.should.equal(expectedPath, 'path should have changed'); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - fs.mkdirSync(expectedBase); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.write(expectedFile); - stream.end(); - }); - - it('should use different modes for files and directories', function(done) { - var inputBase = path.join(__dirname, './fixtures'); - var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); - var expectedBase = path.join(__dirname, './out-fixtures/wow'); - var expectedDirMode = parseInt('755', 8); - var expectedFileMode = parseInt('655', 8); - - var firstFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - stat: fs.statSync(inputPath), - }); - - var buffered = []; - - var onEnd = function() { - realMode(fs.lstatSync(expectedBase).mode).should.equal(expectedDirMode); - realMode(buffered[0].stat.mode).should.equal(expectedFileMode); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { - cwd: __dirname, - mode: expectedFileMode, - dirMode: expectedDirMode, - }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(firstFile); - stream.end(); - }); - - it('should not call futimes when no mtime is provided on the vinyl stat', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedAtime = new Date(); - var expectedMtime = new Date(); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: {}, - }); - - var buffered = []; - - var onEnd = function() { - futimesSpy.called.should.equal(false); - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).atime.setMilliseconds(0).should.equal(expectedAtime.setMilliseconds(0)); - fs.lstatSync(expectedPath).mtime.setMilliseconds(0).should.equal(expectedMtime.setMilliseconds(0)); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should call futimes when an mtime is provided on the vinyl stat', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMtime = fs.lstatSync(inputPath).mtime; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mtime: expectedMtime, - }, - }); - - var buffered = []; - - var onEnd = function() { - futimesSpy.called.should.equal(true); - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).mtime.getTime().should.equal(expectedMtime.getTime()); - expectedFile.stat.should.have.property('mtime'); - expectedFile.stat.mtime.should.equal(expectedMtime); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should not call futimes when provided mtime on the vinyl stat is invalid', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMtime = new Date(); - var invalidMtime = new Date(undefined); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mtime: invalidMtime, - }, - }); - - var buffered = []; - - var onEnd = function() { - futimesSpy.called.should.equal(false); - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).mtime.setMilliseconds(0).should.equal(expectedMtime.setMilliseconds(0)); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should call futimes when provided mtime on the vinyl stat is valid but provided atime is invalid', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedMtime = fs.lstatSync(inputPath).mtime; - var invalidAtime = new Date(undefined); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - atime: invalidAtime, - mtime: expectedMtime, - }, - }); - - var buffered = []; - - var onEnd = function() { - futimesSpy.called.should.equal(true); - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).mtime.getTime().should.equal(expectedMtime.getTime()); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should write file atime and mtime using the vinyl stat', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedAtime = fs.lstatSync(inputPath).atime; - var expectedMtime = fs.lstatSync(inputPath).mtime; - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - atime: expectedAtime, - mtime: expectedMtime, - }, - }); - - var buffered = []; - - var onEnd = function() { - buffered.length.should.equal(1); - buffered[0].should.equal(expectedFile); - fs.existsSync(expectedPath).should.equal(true); - fs.lstatSync(expectedPath).atime.getTime().should.equal(expectedAtime.getTime()); - fs.lstatSync(expectedPath).mtime.getTime().should.equal(expectedMtime.getTime()); - expectedFile.stat.should.have.property('mtime'); - expectedFile.stat.mtime.should.equal(expectedMtime); - expectedFile.stat.should.have.property('atime'); - expectedFile.stat.atime.should.equal(expectedAtime); - done(); - }; - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - it('should change to the specified base as string', function(done) { var inputBase = path.join(__dirname, './fixtures'); var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); @@ -915,16 +555,12 @@ describe('dest stream', function() { var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); var expectedContents = fs.readFileSync(inputPath); var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('722', 8); var expectedFile = new File({ base: inputBase, cwd: __dirname, path: inputPath, contents: expectedContents, - stat: { - mode: expectedMode, - }, }); fs.mkdirSync(expectedBase); @@ -933,7 +569,7 @@ describe('dest stream', function() { var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); stream.on('error', function(err) { - err.code.should.equal('EACCES'); + expect(err).toExist(); done(); }); stream.write(expectedFile); @@ -974,137 +610,6 @@ describe('dest stream', function() { stream.write(expectedFile); }); - it('should report chmod errors', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - - spies.setError(function(mod, fn) { - if (fn === 'fchmod' && typeof arguments[2] === 'number') { - return new Error('chmod error'); - } - }); - - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - stream.on('error', function(err) { - err.message.should.equal('chmod error'); - done(); - }); - stream.write(expectedFile); - }); - - it('should not chmod a matching file', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: expectedMode, - }, - }); - - var expectedCount = 0; - spies.setError(function(mod, fn) { - if (fn === 'fstat' && typeof arguments[2] === 'number') { - expectedCount++; - } - }); - - var onEnd = function() { - expectedCount.should.equal(1); - fchmodSpy.called.should.equal(false); - realMode(fs.lstatSync(expectedPath).mode).should.equal(expectedMode); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, expectedMode); - - fstatSpy.reset(); - fchmodSpy.reset(); - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var buffered = []; - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - - it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(done) { - var inputPath = path.join(__dirname, './fixtures/test.coffee'); - var inputBase = path.join(__dirname, './fixtures/'); - var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); - var expectedContents = fs.readFileSync(inputPath); - var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('3722', 8); - var normalMode = parseInt('722', 8); - - var expectedFile = new File({ - base: inputBase, - cwd: __dirname, - path: inputPath, - contents: expectedContents, - stat: { - mode: normalMode, - }, - }); - - var expectedCount = 0; - spies.setError(function(mod, fn) { - if (fn === 'fstat' && typeof arguments[2] === 'number') { - expectedCount++; - } - }); - - var onEnd = function() { - expectedCount.should.equal(1); - fchmodSpy.called.should.equal(false); - done(); - }; - - fs.mkdirSync(expectedBase); - fs.closeSync(fs.openSync(expectedPath, 'w')); - fs.chmodSync(expectedPath, expectedMode); - - fstatSpy.reset(); - fchmodSpy.reset(); - var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); - - var buffered = []; - var bufferStream = through.obj(dataWrap(buffered.push.bind(buffered)), onEnd); - - stream.pipe(bufferStream); - stream.write(expectedFile); - stream.end(); - }); - it('should not overwrite files with overwrite option set to false', function(done) { var inputPath = path.join(__dirname, './fixtures/test.coffee'); var inputBase = path.join(__dirname, './fixtures/'); @@ -1525,14 +1030,13 @@ describe('dest stream', function() { // This can be a very slow test on boxes with slow disk i/o this.timeout(0); - // Make a ton of hard links + // Make a ton of files. Changed from hard links due to Windows failures var numFiles = 6000; - var srcFile = path.join(__dirname, './fixtures/test.coffee'); fs.mkdirSync(path.join(__dirname, './out-fixtures')); fs.mkdirSync(path.join(__dirname, './out-fixtures/in/')); for (var idx = 0; idx < numFiles; idx++) { - fs.linkSync(srcFile, path.join(__dirname, './out-fixtures/in/test' + idx + '.coffee')); + fs.writeFileSync(path.join(__dirname, './out-fixtures/in/test' + idx + '.coffee'), ''); } var srcStream = vfs.src(path.join(__dirname, './out-fixtures/in/*.coffee'), { buffer: false }); @@ -1554,11 +1058,14 @@ describe('dest stream', function() { }); it('errors if we cannot mkdirp', function(done) { + var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(function() { + var callback = arguments[arguments.length - 1]; + callback(new Error('mocked error')); + }); + var outputDir = path.join(__dirname, './out-fixtures/'); var inputPath = path.join(__dirname, './fixtures/test.coffee'); - fs.mkdirSync(outputDir, parseInt('000', 8)); - var expectedFile = new File({ base: __dirname, cwd: __dirname, @@ -1570,11 +1077,23 @@ describe('dest stream', function() { stream.write(expectedFile); stream.on('error', function(err) { expect(err).toExist(); + expect(mkdirSpy.calls.length).toEqual(1); done(); }); }); it('errors if vinyl object is a directory and we cannot mkdirp', function(done) { + var ogMkdir = fs.mkdir; + + var mkdirSpy = expect.spyOn(fs, 'mkdir').andCall(function() { + if (mkdirSpy.calls.length > 1) { + var callback = arguments[arguments.length - 1]; + callback(new Error('mocked error')); + } else { + ogMkdir.apply(null, arguments); + } + }); + var outputDir = path.join(__dirname, './out-fixtures/'); var inputPath = path.join(__dirname, './other-dir/'); @@ -1590,10 +1109,11 @@ describe('dest stream', function() { }, }); - var stream = vfs.dest(outputDir, { dirMode: parseInt('000', 8) }); + var stream = vfs.dest(outputDir); stream.write(expectedFile); stream.on('error', function(err) { expect(err).toExist(); + expect(mkdirSpy.calls.length).toEqual(2); done(); }); }); diff --git a/test/destModes.js b/test/destModes.js new file mode 100644 index 00000000..84a05256 --- /dev/null +++ b/test/destModes.js @@ -0,0 +1,405 @@ +'use strict'; + +var os = require('os'); +var path = require('path'); + +var fs = require('graceful-fs'); +var del = require('del'); +var File = require('vinyl'); +var expect = require('expect'); +var through = require('through2'); + +var vfs = require('../'); + +function wipeOut() { + this.timeout(20000); + + expect.restoreSpies(); + + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + return del(path.join(__dirname, './fixtures/highwatermark')) + .then(function() { + return del(path.join(__dirname, './out-fixtures/')); + }); +} + +var MASK_MODE = parseInt('777', 8); + +function masked(mode) { + return mode & MASK_MODE; +} + +var isWindows = (os.platform() === 'win32'); + +describe('.dest() with custom modes', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it('should set the mode of a written buffer file if set on the vinyl object', function(done) { + if (isWindows) { + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + console.log('Windows is treated as though it does not have permission to make this operation.'); + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('655', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should set the mode of a written stream file if set on the vinyl object', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('655', 8); + + var contentStream = through.obj(); + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: contentStream, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + setTimeout(function() { + contentStream.write(expectedContents); + contentStream.end(); + }, 100); + stream.end(); + }); + + it('should set the mode of a written directory if set on the vinyl object', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test'); + var expectedMode = parseInt('655', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: null, + stat: { + isDirectory: function() { + return true; + }, + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should write new files with the mode specified in options', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('744', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname, mode: expectedMode }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should update file mode to match the vinyl mode', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var startMode = parseInt('0655', 8); + var expectedMode = parseInt('0722', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, startMode); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should update directory mode to match the vinyl mode', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputBase = path.join(__dirname, './fixtures/'); + var inputPath = path.join(__dirname, './fixtures/wow'); + var expectedPath = path.join(__dirname, './out-fixtures/wow'); + var expectedBase = path.join(__dirname, './out-fixtures'); + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: expectedPath, + stat: fs.statSync(inputPath), + }); + var startMode = firstFile.stat.mode; + var expectedMode = parseInt('727', 8); + + var expectedFile = new File(firstFile); + expectedFile.stat.mode = (startMode & ~parseInt('7777', 8)) | expectedMode; + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + fs.mkdirSync(expectedBase); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(firstFile); + stream.write(expectedFile); + stream.end(); + }); + + it('should use different modes for files and directories', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputBase = path.join(__dirname, './fixtures'); + var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); + var expectedBase = path.join(__dirname, './out-fixtures/wow'); + var expectedPath = path.join(__dirname, './out-fixtures/wow/suchempty'); + var expectedDirMode = parseInt('755', 8); + var expectedFileMode = parseInt('655', 8); + + var firstFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: fs.readFileSync(inputPath), + stat: fs.statSync(inputPath), + }); + + var onEnd = function() { + expect(masked(fs.lstatSync(expectedBase).mode)).toEqual(expectedDirMode); + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedFileMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { + cwd: __dirname, + mode: expectedFileMode, + dirMode: expectedDirMode, + }); + stream.on('end', onEnd); + stream.write(firstFile); + stream.end(); + }); + + it('should not fchmod a matching file', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMode = parseInt('711', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + var onEnd = function() { + expect(fchmodSpy.calls.length).toEqual(0); + expect(masked(fs.lstatSync(expectedPath).mode)).toEqual(expectedMode); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should see a file with special chmod (setuid/setgid/sticky) as matching', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = parseInt('3722', 8); + var normalMode = parseInt('722', 8); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: normalMode, + }, + }); + + var onEnd = function() { + expect(fchmodSpy.calls.length).toEqual(0); + done(); + }; + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + fs.chmodSync(expectedPath, expectedMode); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should report fchmod errors', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedBase = path.join(__dirname, './out-fixtures'); + var expectedMode = parseInt('722', 8); + + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function() { + var callback = arguments[arguments.length - 1]; + callback(new Error('mocked error')); + }); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mode: expectedMode, + }, + }); + + fs.mkdirSync(expectedBase); + fs.closeSync(fs.openSync(expectedPath, 'w')); + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('error', function(err) { + expect(err).toExist(); + expect(fchmodSpy.calls.length).toEqual(1); + done(); + }); + stream.write(expectedFile); + }); +}); diff --git a/test/destTimes.js b/test/destTimes.js new file mode 100644 index 00000000..9d69100a --- /dev/null +++ b/test/destTimes.js @@ -0,0 +1,223 @@ +'use strict'; + +var os = require('os'); +var path = require('path'); + +var fs = require('graceful-fs'); +var del = require('del'); +var File = require('vinyl'); +var expect = require('expect'); + +var vfs = require('../'); + +function wipeOut() { + this.timeout(20000); + + expect.restoreSpies(); + + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + return del(path.join(__dirname, './out-fixtures/')); +} + +var isWindows = (os.platform() === 'win32'); + +describe('.dest() with custom times', function() { + beforeEach(wipeOut); + afterEach(wipeOut); + + it('should not call futimes when no mtime is provided on the vinyl stat', function(done) { + if (isWindows) { + console.log('Changing the time of a directory errors in Windows.'); + console.log('Windows is treated as though it does not have permission to make this operation.'); + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var earlier = Date.now() - 1000; + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: {}, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(0); + expect(stats.atime.getTime()).toBeGreaterThan(earlier); + expect(stats.mtime.getTime()).toBeGreaterThan(earlier); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should call futimes when an mtime is provided on the vinyl stat', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMtime = fs.lstatSync(inputPath).mtime; + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mtime: expectedMtime, + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(1); + expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); + expect(expectedFile.stat.mtime).toEqual(expectedMtime); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should not call futimes when provided mtime on the vinyl stat is invalid', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var earlier = Date.now() - 1000; + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + mtime: new Date(undefined), + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(0); + expect(stats.mtime.getTime()).toBeGreaterThan(earlier); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should call futimes when provided mtime on the vinyl stat is valid but provided atime is invalid', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedMtime = fs.lstatSync(inputPath).mtime; + var invalidAtime = new Date(undefined); + + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + atime: invalidAtime, + mtime: expectedMtime, + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(futimesSpy.calls.length).toEqual(1); + expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); + + it('should write file atime and mtime using the vinyl stat', function(done) { + if (isWindows) { + this.skip(); + return; + } + + var inputPath = path.join(__dirname, './fixtures/test.coffee'); + var inputBase = path.join(__dirname, './fixtures/'); + var expectedPath = path.join(__dirname, './out-fixtures/test.coffee'); + var expectedContents = fs.readFileSync(inputPath); + var expectedAtime = fs.lstatSync(inputPath).atime; + var expectedMtime = fs.lstatSync(inputPath).mtime; + + var expectedFile = new File({ + base: inputBase, + cwd: __dirname, + path: inputPath, + contents: expectedContents, + stat: { + atime: expectedAtime, + mtime: expectedMtime, + }, + }); + + var onEnd = function() { + var stats = fs.lstatSync(expectedPath); + + expect(stats.atime.getTime()).toEqual(expectedAtime.getTime()); + expect(stats.mtime.getTime()).toEqual(expectedMtime.getTime()); + expect(expectedFile.stat.mtime).toEqual(expectedMtime); + expect(expectedFile.stat.atime).toEqual(expectedAtime); + done(); + }; + + var stream = vfs.dest('./out-fixtures/', { cwd: __dirname }); + stream.on('end', onEnd); + stream.write(expectedFile); + stream.end(); + }); +}); diff --git a/test/fileOperations.js b/test/fileOperations.js index 62946ad5..454eaba9 100644 --- a/test/fileOperations.js +++ b/test/fileOperations.js @@ -2,6 +2,7 @@ var expect = require('expect'); +var os = require('os'); var fs = require('graceful-fs'); var del = require('del'); var path = require('path'); @@ -27,6 +28,8 @@ function masked(mode) { function noop() {} +var isWindows = (os.platform() === 'win32'); + describe('isOwner', function() { var ownerStat = { @@ -45,6 +48,11 @@ describe('isOwner', function() { process.geteuid = noop; } + // Windows :( + if (typeof process.getuid !== 'function') { + process.getuid = noop; + } + getuidSpy = expect.spyOn(process, 'getuid').andReturn(ownerStat.uid); geteuidSpy = expect.spyOn(process, 'geteuid').andReturn(ownerStat.uid); @@ -58,9 +66,16 @@ describe('isOwner', function() { delete process.geteuid; } + // Windows :( + if (process.getuid === noop) { + delete process.getuid; + } + done(); }); + // TODO: test for having neither + it('uses process.geteuid() when available', function(done) { isOwner(ownerStat); @@ -323,7 +338,7 @@ describe('closeFd', function() { }); it('calls the callback with close error if no error to propagate', function(done) { - closeFd(null, 9001, function(err) { + closeFd(null, -1, function(err) { expect(err).toExist(); done(); @@ -333,7 +348,7 @@ describe('closeFd', function() { it('calls the callback with propagated error if close errors', function(done) { var propagatedError = new Error(); - closeFd(propagatedError, 9001, function(err) { + closeFd(propagatedError, -1, function(err) { expect(err).toEqual(propagatedError); done(); @@ -383,10 +398,9 @@ describe('writeFile', function() { done(); }); - afterEach(function(done) { - del.sync(filepath); - - done(); + afterEach(function() { + // Async del to get sort-of-fix for https://github.com/isaacs/rimraf/issues/72 + return del(filepath); }); it('writes a file to the filesystem, does not close and returns the fd', function(done) { @@ -426,6 +440,12 @@ describe('writeFile', function() { }); it('accepts a different mode in options', function(done) { + if (isWindows) { + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + this.skip(); + return; + } + var expected = parseInt('0777', 8) & (~process.umask()); var content = new Buffer('test'); var options = { @@ -587,6 +607,14 @@ describe('updateMetadata', function() { }); it('passes the error and file descriptor if fstat fails', function(done) { + if (isWindows) { + console.log('Changing the time of a directory errors in Windows.'); + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + console.log('Windows is treated as though it does not have permission to make these operations.'); + this.skip(); + return; + } + var fd = 9001; updateMetadata(fd, file, function(err, fd2) { @@ -599,6 +627,11 @@ describe('updateMetadata', function() { }); it('updates the vinyl object with fs stats', function(done) { + if (isWindows) { + this.skip(); + return; + } + var fd = fs.openSync(inputPath, 'w+'); var stats = fs.fstatSync(fd); @@ -613,6 +646,11 @@ describe('updateMetadata', function() { }); it('does not touch the fs if nothing to update', function(done) { + if (isWindows) { + this.skip(); + return; + } + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); @@ -627,6 +665,11 @@ describe('updateMetadata', function() { }); it('does not touch the fs if process is not owner of the file', function(done) { + if (isWindows) { + this.skip(); + return; + } + if (typeof process.geteuid !== 'function') { process.geteuid = noop; } @@ -648,6 +691,11 @@ describe('updateMetadata', function() { }); it('updates times on fs and vinyl object if there is a diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); var now = Date.now(); @@ -674,6 +722,11 @@ describe('updateMetadata', function() { }); it('forwards futimes error and descriptor upon error', function(done) { + if (isWindows) { + this.skip(); + return; + } + var futimesSpy = expect.spyOn(fs, 'futimes').andCall(function(fd, atime, mtime, cb) { cb(new Error('mocked error')); }); @@ -695,6 +748,11 @@ describe('updateMetadata', function() { }); it('updates the mode on fs and vinyl object if there is a diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var mode = parseInt('777', 8); @@ -712,15 +770,20 @@ describe('updateMetadata', function() { }); it('forwards fchmod error and descriptor upon error', function(done) { - var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { - cb(new Error('mocked error')); - }); + if (isWindows) { + this.skip(); + return; + } var mode = parseInt('777', 8); file.stat.mode = mode; var fd = fs.openSync(inputPath, 'w+'); + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { + cb(new Error('mocked error')); + }); + updateMetadata(fd, file, function(err, fd2) { expect(err).toExist(); expect(fchmodSpy.calls.length).toEqual(1); @@ -731,6 +794,11 @@ describe('updateMetadata', function() { }); it('updates the mode & times on fs and vinyl object if there is a diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + var fchmodSpy = expect.spyOn(fs, 'fchmod').andCallThrough(); var futimesSpy = expect.spyOn(fs, 'futimes').andCallThrough(); @@ -765,6 +833,11 @@ describe('updateMetadata', function() { }); it('forwards fchmod error and descriptor through futimes if there is a time diff', function(done) { + if (isWindows) { + this.skip(); + return; + } + var expectedErr = new Error('mocked error'); var fchmodSpy = expect.spyOn(fs, 'fchmod').andCall(function(fd, mode, cb) { @@ -780,7 +853,7 @@ describe('updateMetadata', function() { file.stat.mtime = new Date(then); file.stat.atime = new Date(then); - var fd = fs.openSync(inputPath, 'w+'); + var fd = fs.openSync(inputPath, 'w'); updateMetadata(fd, file, function(err, fd2) { expect(err).toExist(); diff --git a/test/fixtures/test-symlink b/test/fixtures/test-symlink deleted file mode 120000 index 3fcfe6c7..00000000 --- a/test/fixtures/test-symlink +++ /dev/null @@ -1 +0,0 @@ -test.coffee \ No newline at end of file diff --git a/test/fixtures/test-symlink-dir b/test/fixtures/test-symlink-dir deleted file mode 120000 index 9a17374b..00000000 --- a/test/fixtures/test-symlink-dir +++ /dev/null @@ -1 +0,0 @@ -wow \ No newline at end of file diff --git a/test/not-owned.js b/test/notOwned.js similarity index 100% rename from test/not-owned.js rename to test/notOwned.js diff --git a/test/src.js b/test/src.js index 34a6f0e5..3fa2c655 100644 --- a/test/src.js +++ b/test/src.js @@ -396,38 +396,54 @@ describe('source stream', function() { stream1.pipe(stream2).pipe(bufferStream); }); - it('should follow file symlinks', function(done) { - var expectedPath = path.join(__dirname, './fixtures/test.coffee'); +}); + +describe('.src() symlinks', function() { + + + var dirPath = path.join(__dirname, './fixtures/wow'); + var dirSymlinkPath = path.join(__dirname, './fixtures/test-symlink-dir'); + + var filePath = path.join(__dirname, './fixtures/test.coffee'); + var fileSymlinkPath = path.join(__dirname, './fixtures/test-symlink'); + + beforeEach(function(done) { + fs.symlinkSync(dirPath, dirSymlinkPath); + fs.symlinkSync(filePath, fileSymlinkPath); + done(); + }); + + afterEach(function(done) { + fs.unlinkSync(dirSymlinkPath); + fs.unlinkSync(fileSymlinkPath); + done(); + }); - var stream = vfs.src('./fixtures/test-symlink', { cwd: __dirname }); + it('should follow file symlinks', function(done) { + var stream = vfs.src(fileSymlinkPath, { cwd: __dirname }); stream.on('data', function(file) { - file.path.should.equal(expectedPath); + file.path.should.equal(filePath); done(); }); }); it('should follow dir symlinks', function(done) { - var expectedPath = path.join(__dirname, './fixtures/wow'); - - var stream = vfs.src('./fixtures/test-symlink-dir', { cwd: __dirname }); + var stream = vfs.src(dirSymlinkPath, { cwd: __dirname }); stream.on('data', function(file) { - file.path.should.equal(expectedPath); + file.path.should.equal(dirPath); done(); }); }); it('should preserve file symlinks with followSymlinks option set to false', function(done) { - var sourcePath = path.join(__dirname, './fixtures/test-symlink'); - var expectedPath = sourcePath; - - fs.readlink(sourcePath, function(err, expectedRelativeSymlinkPath) { + fs.readlink(fileSymlinkPath, function(err, expectedRelativeSymlinkPath) { if (err) { throw err; } - var stream = vfs.src('./fixtures/test-symlink', { cwd: __dirname, followSymlinks: false }); + var stream = vfs.src(fileSymlinkPath, { cwd: __dirname, followSymlinks: false }); stream.on('data', function(file) { - file.path.should.equal(expectedPath); + file.path.should.equal(fileSymlinkPath); file.symlink.should.equal(expectedRelativeSymlinkPath); done(); }); @@ -435,21 +451,17 @@ describe('source stream', function() { }); it('should preserve dir symlinks with followSymlinks option set to false', function(done) { - var sourcePath = path.join(__dirname, './fixtures/test-symlink-dir'); - var expectedPath = sourcePath; - - fs.readlink(sourcePath, function(err, expectedRelativeSymlinkPath) { + fs.readlink(dirSymlinkPath, function(err, expectedRelativeSymlinkPath) { if (err) { throw err; } - var stream = vfs.src(sourcePath, { cwd: __dirname, followSymlinks: false }); + var stream = vfs.src(dirSymlinkPath, { cwd: __dirname, followSymlinks: false }); stream.on('data', function(file) { - file.path.should.equal(expectedPath); + file.path.should.equal(dirSymlinkPath); file.symlink.should.equal(expectedRelativeSymlinkPath); done(); }); }); }); - }); diff --git a/test/symlink.js b/test/symlink.js index d1b3df9c..a070afe8 100644 --- a/test/symlink.js +++ b/test/symlink.js @@ -6,9 +6,10 @@ var statSpy = spies.statSpy; var vfs = require('../'); +var os = require('os'); var path = require('path'); var fs = require('graceful-fs'); -var rimraf = require('rimraf'); +var del = require('del'); var bufEqual = require('buffer-equal'); var through = require('through2'); @@ -17,12 +18,13 @@ var File = require('vinyl'); var should = require('should'); require('mocha'); -var wipeOut = function(cb) { - rimraf(path.join(__dirname, './out-fixtures/'), cb); +function wipeOut() { spies.setError('false'); statSpy.reset(); chmodSpy.reset(); -}; + + return del(path.join(__dirname, './out-fixtures/')); +} var dataWrap = function(fn) { return function(data, enc, cb) { @@ -35,6 +37,8 @@ var realMode = function(n) { return n & parseInt('777', 8); }; +var isWindows = (os.platform() === 'win32'); + describe('symlink stream', function() { beforeEach(wipeOut); afterEach(wipeOut); @@ -307,6 +311,12 @@ describe('symlink stream', function() { }); it('should use different modes for files and directories', function(done) { + if (isWindows) { + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + this.skip(); + return; + } + var inputBase = path.join(__dirname, './fixtures'); var inputPath = path.join(__dirname, './fixtures/wow/suchempty'); var expectedBase = path.join(__dirname, './out-fixtures/wow'); @@ -372,20 +382,23 @@ describe('symlink stream', function() { }); it('should report IO errors', function(done) { + if (isWindows) { + console.log('Changing the mode of a file is not supported by node.js in Windows.'); + console.log('This test is skipped on Windows because we have to chmod the file to 0.'); + this.skip(); + return; + } + var inputPath = path.join(__dirname, './fixtures/test.coffee'); var inputBase = path.join(__dirname, './fixtures/'); var expectedContents = fs.readFileSync(inputPath); var expectedBase = path.join(__dirname, './out-fixtures'); - var expectedMode = parseInt('722', 8); var expectedFile = new File({ base: inputBase, cwd: __dirname, path: inputPath, contents: expectedContents, - stat: { - mode: expectedMode, - }, }); fs.mkdirSync(expectedBase);