/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import assert = require('assert'); import os = require('os'); import path = require('path'); import fs = require('fs'); import uuid = require('vs/base/common/uuid'); import strings = require('vs/base/common/strings'); import extfs = require('vs/base/node/extfs'); import { onError } from 'vs/base/test/common/utils'; import { Readable } from 'stream'; const ignore = () => { }; const mkdirp = (path: string, mode: number, callback: (error) => void) => { extfs.mkdirp(path, mode).done(() => callback(null), error => callback(error)); }; const chunkSize = 64 * 1024; const readError = 'Error while reading'; function toReadable(value: string, throwError?: boolean): Readable { const totalChunks = Math.ceil(value.length / chunkSize); const stringChunks: string[] = []; for (let i = 0, j = 0; i < totalChunks; ++i, j += chunkSize) { stringChunks[i] = value.substr(j, chunkSize); } let counter = 0; return new Readable({ read: function () { if (throwError) { this.emit('error', new Error(readError)); } let res: string; let canPush = true; while (canPush && (res = stringChunks[counter++])) { canPush = this.push(res); } // EOS if (!res) { this.push(null); } }, encoding: 'utf8' }); } suite('Extfs', () => { test('mkdirp', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); extfs.del(parentDir, os.tmpdir(), done, ignore); }); // 493 = 0755 }); test('delSync - swallows file not found error', function () { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); extfs.delSync(newDir); assert.ok(!fs.existsSync(newDir)); }); test('delSync - simple', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); extfs.delSync(newDir); assert.ok(!fs.existsSync(newDir)); done(); }); // 493 = 0755 }); test('delSync - recursive folder structure', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents'); fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents'); fs.mkdirSync(path.join(newDir, 'somefolder')); fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents'); extfs.delSync(newDir); assert.ok(!fs.existsSync(newDir)); done(); }); // 493 = 0755 }); test('copy, move and delete', function (done: () => void) { const id = uuid.generateUuid(); const id2 = uuid.generateUuid(); const sourceDir = require.toUrl('./fixtures'); const parentDir = path.join(os.tmpdir(), 'vsctests', 'extfs'); const targetDir = path.join(parentDir, id); const targetDir2 = path.join(parentDir, id2); extfs.copy(sourceDir, targetDir, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(targetDir)); assert.ok(fs.existsSync(path.join(targetDir, 'index.html'))); assert.ok(fs.existsSync(path.join(targetDir, 'site.css'))); assert.ok(fs.existsSync(path.join(targetDir, 'examples'))); assert.ok(fs.statSync(path.join(targetDir, 'examples')).isDirectory()); assert.ok(fs.existsSync(path.join(targetDir, 'examples', 'small.jxs'))); extfs.mv(targetDir, targetDir2, error => { if (error) { return onError(error, done); } assert.ok(!fs.existsSync(targetDir)); assert.ok(fs.existsSync(targetDir2)); assert.ok(fs.existsSync(path.join(targetDir2, 'index.html'))); assert.ok(fs.existsSync(path.join(targetDir2, 'site.css'))); assert.ok(fs.existsSync(path.join(targetDir2, 'examples'))); assert.ok(fs.statSync(path.join(targetDir2, 'examples')).isDirectory()); assert.ok(fs.existsSync(path.join(targetDir2, 'examples', 'small.jxs'))); extfs.mv(path.join(targetDir2, 'index.html'), path.join(targetDir2, 'index_moved.html'), error => { if (error) { return onError(error, done); } assert.ok(!fs.existsSync(path.join(targetDir2, 'index.html'))); assert.ok(fs.existsSync(path.join(targetDir2, 'index_moved.html'))); extfs.del(parentDir, os.tmpdir(), error => { if (error) { return onError(error, done); } }, error => { if (error) { return onError(error, done); } assert.ok(!fs.existsSync(parentDir)); done(); }); }); }); }); }); test('readdir', function (done: () => void) { if (strings.canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id, 'öäü'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); extfs.readdir(path.join(parentDir, 'extfs', id), (error, children) => { assert.equal(children.some(n => n === 'öäü'), true); // Mac always converts to NFD, so extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); // 493 = 0755 } else { done(); } }); test('writeFileAndFlush (string)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { if (error) { return onError(error, done); } assert.equal(fs.readFileSync(testFile), 'Hello World'); const largeString = (new Array(100 * 1024)).join('Large String\n'); extfs.writeFileAndFlush(testFile, largeString, null, error => { if (error) { return onError(error, done); } assert.equal(fs.readFileSync(testFile), largeString); extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); }); test('writeFileAndFlush (stream)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { if (error) { return onError(error, done); } assert.equal(fs.readFileSync(testFile), 'Hello World'); const largeString = (new Array(100 * 1024)).join('Large String\n'); extfs.writeFileAndFlush(testFile, toReadable(largeString), null, error => { if (error) { return onError(error, done); } assert.equal(fs.readFileSync(testFile), largeString); extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); }); test('writeFileAndFlush (file stream)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const sourceFile = require.toUrl('./fixtures/index.html'); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { if (error) { return onError(error, done); } assert.equal(fs.readFileSync(testFile).toString(), fs.readFileSync(sourceFile).toString()); extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); test('writeFileAndFlush (string, error handling)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! extfs.writeFileAndFlush(testFile, 'Hello World', null, error => { if (!error) { return onError(new Error('Expected error for writing to readonly file'), done); } extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); test('writeFileAndFlush (stream, error handling EISDIR)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { if (!error || (error).code !== 'EISDIR') { return onError(new Error('Expected EISDIR error for writing to folder but got: ' + (error ? (error).code : 'no error')), done); } extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); test('writeFileAndFlush (stream, error handling READERROR)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); extfs.writeFileAndFlush(testFile, toReadable('Hello World', true /* throw error */), null, error => { if (!error || error.message !== readError) { return onError(new Error('Expected error for writing to folder'), done); } extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); test('pasero writeFileAndFlush (stream, error handling EACCES)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); fs.writeFileSync(testFile, ''); fs.chmodSync(testFile, 33060); // make readonly extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null, error => { if (!error || !((error).code !== 'EACCES' || (error).code !== 'EPERM')) { return onError(new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (error ? (error).code : 'no error')), done); } extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); test('writeFileAndFlush (file stream, error handling)', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const sourceFile = require.toUrl('./fixtures/index.html'); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory! extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null, error => { if (!error) { return onError(new Error('Expected error for writing to folder'), done); } extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); test('writeFileAndFlushSync', function (done: () => void) { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); const testFile = path.join(newDir, 'flushed.txt'); mkdirp(newDir, 493, error => { if (error) { return onError(error, done); } assert.ok(fs.existsSync(newDir)); extfs.writeFileAndFlushSync(testFile, 'Hello World', null); assert.equal(fs.readFileSync(testFile), 'Hello World'); const largeString = (new Array(100 * 1024)).join('Large String\n'); extfs.writeFileAndFlushSync(testFile, largeString, null); assert.equal(fs.readFileSync(testFile), largeString); extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); test('realcase', (done) => { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { // assume case insensitive file system if (process.platform === 'win32' || process.platform === 'darwin') { const upper = newDir.toUpperCase(); const real = extfs.realcaseSync(upper); if (real) { // can be null in case of permission errors assert.notEqual(real, upper); assert.equal(real.toUpperCase(), upper); assert.equal(real, newDir); } } // linux, unix, etc. -> assume case sensitive file system else { const real = extfs.realcaseSync(newDir); assert.equal(real, newDir); } extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); test('realpath', (done) => { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { extfs.realpath(newDir, (error, realpath) => { assert.ok(realpath); assert.ok(!error); extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); }); test('realpathSync', (done) => { const id = uuid.generateUuid(); const parentDir = path.join(os.tmpdir(), 'vsctests', id); const newDir = path.join(parentDir, 'extfs', id); mkdirp(newDir, 493, error => { let realpath: string; try { realpath = extfs.realpathSync(newDir); } catch (error) { assert.ok(!error); } assert.ok(realpath); extfs.del(parentDir, os.tmpdir(), done, ignore); }); }); });