From 2d401764bdc3e28359d12125654cfd8bbb6c1ea9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 11 Jul 2019 08:26:21 +0200 Subject: [PATCH] files - add more tests for writing with stream --- .../services/files/common/fileService.ts | 8 ++- .../files/test/node/diskFileService.test.ts | 62 ++++++++++++------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/services/files/common/fileService.ts b/src/vs/workbench/services/files/common/fileService.ts index 38063562380..a2ad60609c6 100644 --- a/src/vs/workbench/services/files/common/fileService.ts +++ b/src/vs/workbench/services/files/common/fileService.ts @@ -885,6 +885,8 @@ export class FileService extends Disposable implements IFileService { let posInFile = 0; stream.on('data', async chunk => { + + // pause stream to perform async write operation stream.pause(); try { @@ -895,7 +897,11 @@ export class FileService extends Disposable implements IFileService { posInFile += chunk.byteLength; - stream.resume(); + // resume stream now that we have successfully written + // run this on the next tick to prevent increasing the + // execution stack because resume() may call the event + // handler again before finishing. + setTimeout(() => stream.resume()); }); stream.on('error', error => reject(error)); diff --git a/src/vs/workbench/services/files/test/node/diskFileService.test.ts b/src/vs/workbench/services/files/test/node/diskFileService.test.ts index d74db006d1b..fb39acd397d 100644 --- a/src/vs/workbench/services/files/test/node/diskFileService.test.ts +++ b/src/vs/workbench/services/files/test/node/diskFileService.test.ts @@ -14,13 +14,13 @@ import { join, basename, dirname, posix } from 'vs/base/common/path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { copy, rimraf, symlink, RimRafMode, rimrafSync } from 'vs/base/node/pfs'; import { URI } from 'vs/base/common/uri'; -import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync } from 'fs'; +import { existsSync, statSync, readdirSync, readFileSync, writeFileSync, renameSync, unlinkSync, mkdirSync, createReadStream, ReadStream } from 'fs'; import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities, FileChangeType, IFileChange, FileChangesEvent, FileOperationError, etag, IStat, IFileStatWithMetadata } from 'vs/platform/files/common/files'; import { NullLogService } from 'vs/platform/log/common/log'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; -import { VSBuffer, VSBufferReadable, bufferToStream } from 'vs/base/common/buffer'; +import { VSBuffer, VSBufferReadable, writeableBufferStream, VSBufferReadableStream } from 'vs/base/common/buffer'; function getByName(root: IFileStat, name: string): IFileStat | null { if (root.children === undefined) { @@ -58,6 +58,16 @@ function toLineByLineReadable(content: string): VSBufferReadable { }; } +function nodeStreamToVSBufferStream(stream: ReadStream): VSBufferReadableStream { + const vsbufferStream = writeableBufferStream(); + + stream.on('data', data => vsbufferStream.write(VSBuffer.wrap(data))); + stream.on('end', () => vsbufferStream.end()); + stream.on('error', error => vsbufferStream.error(error)); + + return vsbufferStream; +} + export class TestDiskFileSystemProvider extends DiskFileSystemProvider { totalBytesRead: number = 0; @@ -1545,46 +1555,52 @@ suite('Disk File Service', () => { assert.equal(readFileSync(resource.fsPath), newContent); }); - test('writeFile (large file - stream) - buffered', async () => { + test('writeFile (stream) - buffered', async () => { setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); - const resource = URI.file(join(testDir, 'lorem.txt')); + const source = URI.file(join(testDir, 'small.txt')); + const target = URI.file(join(testDir, 'small-copy.txt')); - const content = readFileSync(resource.fsPath); - const newContent = content.toString() + content.toString(); + const fileStat = await service.writeFile(target, nodeStreamToVSBufferStream(createReadStream(source.fsPath))); + assert.equal(fileStat.name, 'small-copy.txt'); - const fileStat = await service.writeFile(resource, bufferToStream(VSBuffer.fromString(newContent))); - assert.equal(fileStat.name, 'lorem.txt'); + assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString()); + }); - assert.equal(readFileSync(resource.fsPath), newContent); + test('writeFile (large file - stream) - buffered', async () => { + setCapabilities(fileProvider, FileSystemProviderCapabilities.FileOpenReadWriteClose); + + const source = URI.file(join(testDir, 'lorem.txt')); + const target = URI.file(join(testDir, 'lorem-copy.txt')); + + const fileStat = await service.writeFile(target, nodeStreamToVSBufferStream(createReadStream(source.fsPath))); + assert.equal(fileStat.name, 'lorem-copy.txt'); + + assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString()); }); test('writeFile (stream) - unbuffered', async () => { setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); - const resource = URI.file(join(testDir, 'small.txt')); + const source = URI.file(join(testDir, 'small.txt')); + const target = URI.file(join(testDir, 'small-copy.txt')); - const content = readFileSync(resource.fsPath); - assert.equal(content, 'Small File'); - - const newContent = 'Updates to the small file'; - await service.writeFile(resource, bufferToStream(VSBuffer.fromString(newContent))); + const fileStat = await service.writeFile(target, nodeStreamToVSBufferStream(createReadStream(source.fsPath))); + assert.equal(fileStat.name, 'small-copy.txt'); - assert.equal(readFileSync(resource.fsPath), newContent); + assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString()); }); test('writeFile (large file - stream) - unbuffered', async () => { setCapabilities(fileProvider, FileSystemProviderCapabilities.FileReadWrite); - const resource = URI.file(join(testDir, 'lorem.txt')); + const source = URI.file(join(testDir, 'lorem.txt')); + const target = URI.file(join(testDir, 'lorem-copy.txt')); - const content = readFileSync(resource.fsPath); - const newContent = content.toString() + content.toString(); + const fileStat = await service.writeFile(target, nodeStreamToVSBufferStream(createReadStream(source.fsPath))); + assert.equal(fileStat.name, 'lorem-copy.txt'); - const fileStat = await service.writeFile(resource, bufferToStream(VSBuffer.fromString(newContent))); - assert.equal(fileStat.name, 'lorem.txt'); - - assert.equal(readFileSync(resource.fsPath), newContent); + assert.equal(readFileSync(source.fsPath).toString(), readFileSync(target.fsPath).toString()); }); test('writeFile (file is created including parents)', async () => { -- GitLab