mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 17:19:48 +01:00
525 lines
16 KiB
TypeScript
525 lines
16 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as assert from 'assert';
|
|
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'vs/base/common/path';
|
|
import { Readable } from 'stream';
|
|
import { canNormalize } from 'vs/base/common/normalization';
|
|
import { isLinux, isWindows } from 'vs/base/common/platform';
|
|
import * as uuid from 'vs/base/common/uuid';
|
|
import * as extfs from 'vs/base/node/extfs';
|
|
import * as pfs from 'vs/base/node/pfs';
|
|
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
|
|
|
const ignore = () => { };
|
|
|
|
const mkdirp = (path: string, mode: number, callback: (error: any) => void) => {
|
|
pfs.mkdirp(path, mode).then(() => 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('stat link', function (done) {
|
|
if (isWindows) {
|
|
// Symlinks are not the same on win, and we can not create them programitically without admin privileges
|
|
return done();
|
|
}
|
|
|
|
const id1 = uuid.generateUuid();
|
|
const parentDir = path.join(os.tmpdir(), 'vsctests', id1);
|
|
const directory = path.join(parentDir, 'extfs', id1);
|
|
|
|
const id2 = uuid.generateUuid();
|
|
const symbolicLink = path.join(parentDir, 'extfs', id2);
|
|
|
|
mkdirp(directory, 493, error => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
fs.symlinkSync(directory, symbolicLink);
|
|
|
|
extfs.statLink(directory, (error, statAndIsLink) => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
assert.ok(!statAndIsLink!.isSymbolicLink);
|
|
|
|
extfs.statLink(symbolicLink, (error, statAndIsLink) => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
assert.ok(statAndIsLink!.isSymbolicLink);
|
|
extfs.delSync(directory);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
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) {
|
|
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 done(error);
|
|
}
|
|
|
|
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) {
|
|
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 done(error);
|
|
}
|
|
|
|
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('readdir', function (done) {
|
|
if (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 done(error);
|
|
}
|
|
|
|
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) {
|
|
const smallData = 'Hello World';
|
|
const bigData = (new Array(100 * 1024)).join('Large String\n');
|
|
|
|
testWriteFileAndFlush(smallData, smallData, bigData, bigData, done);
|
|
});
|
|
|
|
test('writeFileAndFlush (Buffer)', function (done) {
|
|
const smallData = 'Hello World';
|
|
const bigData = (new Array(100 * 1024)).join('Large String\n');
|
|
|
|
testWriteFileAndFlush(Buffer.from(smallData), smallData, Buffer.from(bigData), bigData, done);
|
|
});
|
|
|
|
test('writeFileAndFlush (UInt8Array)', function (done) {
|
|
const smallData = 'Hello World';
|
|
const bigData = (new Array(100 * 1024)).join('Large String\n');
|
|
|
|
testWriteFileAndFlush(new TextEncoder().encode(smallData), smallData, new TextEncoder().encode(bigData), bigData, done);
|
|
});
|
|
|
|
test('writeFileAndFlush (stream)', function (done) {
|
|
const smallData = 'Hello World';
|
|
const bigData = (new Array(100 * 1024)).join('Large String\n');
|
|
|
|
testWriteFileAndFlush(toReadable(smallData), smallData, toReadable(bigData), bigData, done);
|
|
});
|
|
|
|
function testWriteFileAndFlush(
|
|
smallData: string | Buffer | NodeJS.ReadableStream | Uint8Array,
|
|
smallDataValue: string,
|
|
bigData: string | Buffer | NodeJS.ReadableStream | Uint8Array,
|
|
bigDataValue: string,
|
|
done: (error: Error | null) => void
|
|
): 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 done(error);
|
|
}
|
|
|
|
assert.ok(fs.existsSync(newDir));
|
|
|
|
extfs.writeFileAndFlush(testFile, smallData, null!, error => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
assert.equal(fs.readFileSync(testFile), smallDataValue);
|
|
|
|
extfs.writeFileAndFlush(testFile, bigData, null!, error => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
assert.equal(fs.readFileSync(testFile), bigDataValue);
|
|
|
|
extfs.del(parentDir, os.tmpdir(), done, ignore);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
test('writeFileAndFlush (file stream)', function (done) {
|
|
const id = uuid.generateUuid();
|
|
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
|
const sourceFile = getPathFromAmdModule(require, './fixtures/index.html');
|
|
const newDir = path.join(parentDir, 'extfs', id);
|
|
const testFile = path.join(newDir, 'flushed.txt');
|
|
|
|
mkdirp(newDir, 493, error => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
assert.ok(fs.existsSync(newDir));
|
|
|
|
extfs.writeFileAndFlush(testFile, fs.createReadStream(sourceFile), null!, error => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
assert.equal(fs.readFileSync(testFile).toString(), fs.readFileSync(sourceFile).toString());
|
|
|
|
extfs.del(parentDir, os.tmpdir(), done, ignore);
|
|
});
|
|
});
|
|
});
|
|
|
|
test('writeFileAndFlush (string, error handling)', function (done) {
|
|
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 done(error);
|
|
}
|
|
|
|
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 done(new Error('Expected error for writing to readonly file'));
|
|
}
|
|
|
|
extfs.del(parentDir, os.tmpdir(), done, ignore);
|
|
});
|
|
});
|
|
});
|
|
|
|
test('writeFileAndFlush (stream, error handling EISDIR)', function (done) {
|
|
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 done(error);
|
|
}
|
|
|
|
assert.ok(fs.existsSync(newDir));
|
|
|
|
fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory!
|
|
|
|
const readable = toReadable('Hello World');
|
|
extfs.writeFileAndFlush(testFile, readable, null!, error => {
|
|
if (!error || (<any>error).code !== 'EISDIR') {
|
|
return done(new Error('Expected EISDIR error for writing to folder but got: ' + (error ? (<any>error).code : 'no error')));
|
|
}
|
|
|
|
// verify that the stream is still consumable (for https://github.com/Microsoft/vscode/issues/42542)
|
|
assert.equal(readable.read(), 'Hello World');
|
|
|
|
extfs.del(parentDir, os.tmpdir(), done, ignore);
|
|
});
|
|
});
|
|
});
|
|
|
|
test('writeFileAndFlush (stream, error handling READERROR)', function (done) {
|
|
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 done(error);
|
|
}
|
|
|
|
assert.ok(fs.existsSync(newDir));
|
|
|
|
extfs.writeFileAndFlush(testFile, toReadable('Hello World', true /* throw error */), null!, error => {
|
|
if (!error || error.message !== readError) {
|
|
return done(new Error('Expected error for writing to folder'));
|
|
}
|
|
|
|
extfs.del(parentDir, os.tmpdir(), done, ignore);
|
|
});
|
|
});
|
|
});
|
|
|
|
test('writeFileAndFlush (stream, error handling EACCES)', function (done) {
|
|
if (isLinux) {
|
|
return done(); // somehow this test fails on Linux in our TFS builds
|
|
}
|
|
|
|
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 done(error);
|
|
}
|
|
|
|
assert.ok(fs.existsSync(newDir));
|
|
|
|
fs.writeFileSync(testFile, '');
|
|
fs.chmodSync(testFile, 33060); // make readonly
|
|
|
|
extfs.writeFileAndFlush(testFile, toReadable('Hello World'), null!, error => {
|
|
if (!error || !((<any>error).code !== 'EACCES' || (<any>error).code !== 'EPERM')) {
|
|
return done(new Error('Expected EACCES/EPERM error for writing to folder but got: ' + (error ? (<any>error).code : 'no error')));
|
|
}
|
|
|
|
extfs.del(parentDir, os.tmpdir(), done, ignore);
|
|
});
|
|
});
|
|
});
|
|
|
|
test('writeFileAndFlush (file stream, error handling)', function (done) {
|
|
const id = uuid.generateUuid();
|
|
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
|
|
const sourceFile = getPathFromAmdModule(require, './fixtures/index.html');
|
|
const newDir = path.join(parentDir, 'extfs', id);
|
|
const testFile = path.join(newDir, 'flushed.txt');
|
|
|
|
mkdirp(newDir, 493, error => {
|
|
if (error) {
|
|
return done(error);
|
|
}
|
|
|
|
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 done(new Error('Expected error for writing to folder'));
|
|
}
|
|
|
|
extfs.del(parentDir, os.tmpdir(), done, ignore);
|
|
});
|
|
});
|
|
});
|
|
|
|
test('writeFileAndFlushSync', function (done) {
|
|
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 done(error);
|
|
}
|
|
|
|
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);
|
|
});
|
|
});
|
|
|
|
test('sanitizeFilePath', () => {
|
|
if (isWindows) {
|
|
assert.equal(extfs.sanitizeFilePath('.', 'C:\\the\\cwd'), 'C:\\the\\cwd');
|
|
assert.equal(extfs.sanitizeFilePath('', 'C:\\the\\cwd'), 'C:\\the\\cwd');
|
|
|
|
assert.equal(extfs.sanitizeFilePath('C:', 'C:\\the\\cwd'), 'C:\\');
|
|
assert.equal(extfs.sanitizeFilePath('C:\\', 'C:\\the\\cwd'), 'C:\\');
|
|
assert.equal(extfs.sanitizeFilePath('C:\\\\', 'C:\\the\\cwd'), 'C:\\');
|
|
|
|
assert.equal(extfs.sanitizeFilePath('C:\\folder\\my.txt', 'C:\\the\\cwd'), 'C:\\folder\\my.txt');
|
|
assert.equal(extfs.sanitizeFilePath('C:\\folder\\my', 'C:\\the\\cwd'), 'C:\\folder\\my');
|
|
assert.equal(extfs.sanitizeFilePath('C:\\folder\\..\\my', 'C:\\the\\cwd'), 'C:\\my');
|
|
assert.equal(extfs.sanitizeFilePath('C:\\folder\\my\\', 'C:\\the\\cwd'), 'C:\\folder\\my');
|
|
assert.equal(extfs.sanitizeFilePath('C:\\folder\\my\\\\\\', 'C:\\the\\cwd'), 'C:\\folder\\my');
|
|
|
|
assert.equal(extfs.sanitizeFilePath('my.txt', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt');
|
|
assert.equal(extfs.sanitizeFilePath('my.txt\\', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt');
|
|
|
|
assert.equal(extfs.sanitizeFilePath('\\\\localhost\\folder\\my', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my');
|
|
assert.equal(extfs.sanitizeFilePath('\\\\localhost\\folder\\my\\', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my');
|
|
} else {
|
|
assert.equal(extfs.sanitizeFilePath('.', '/the/cwd'), '/the/cwd');
|
|
assert.equal(extfs.sanitizeFilePath('', '/the/cwd'), '/the/cwd');
|
|
assert.equal(extfs.sanitizeFilePath('/', '/the/cwd'), '/');
|
|
|
|
assert.equal(extfs.sanitizeFilePath('/folder/my.txt', '/the/cwd'), '/folder/my.txt');
|
|
assert.equal(extfs.sanitizeFilePath('/folder/my', '/the/cwd'), '/folder/my');
|
|
assert.equal(extfs.sanitizeFilePath('/folder/../my', '/the/cwd'), '/my');
|
|
assert.equal(extfs.sanitizeFilePath('/folder/my/', '/the/cwd'), '/folder/my');
|
|
assert.equal(extfs.sanitizeFilePath('/folder/my///', '/the/cwd'), '/folder/my');
|
|
|
|
assert.equal(extfs.sanitizeFilePath('my.txt', '/the/cwd'), '/the/cwd/my.txt');
|
|
assert.equal(extfs.sanitizeFilePath('my.txt/', '/the/cwd'), '/the/cwd/my.txt');
|
|
}
|
|
});
|
|
});
|