mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Refresh master with initial release/0.24 snapshot (#332)
* Initial port of release/0.24 source code * Fix additional headers * Fix a typo in launch.json
This commit is contained in:
@@ -7,10 +7,9 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import * as crypto from 'crypto';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import pfs = require('vs/base/node/pfs');
|
||||
import Uri from 'vs/base/common/uri';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { ResourceQueue } from 'vs/base/common/async';
|
||||
import { IBackupFileService, BACKUP_FILE_UPDATE_OPTIONS } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
@@ -88,24 +87,29 @@ export class BackupFilesModel implements IBackupFilesModel {
|
||||
|
||||
export class BackupFileService implements IBackupFileService {
|
||||
|
||||
private static readonly META_MARKER = '\n';
|
||||
|
||||
public _serviceBrand: any;
|
||||
|
||||
private static readonly META_MARKER = '\n';
|
||||
private backupWorkspacePath: string;
|
||||
|
||||
private isShuttingDown: boolean;
|
||||
private ready: TPromise<IBackupFilesModel>;
|
||||
/**
|
||||
* Ensure IO operations on individual files are performed in order, this could otherwise lead
|
||||
* to unexpected behavior when backups are persisted and discarded in the wrong order.
|
||||
*/
|
||||
private ioOperationQueues: { [path: string]: Queue<void> };
|
||||
private ioOperationQueues: ResourceQueue<void>; // queue IO operations to ensure write order
|
||||
|
||||
constructor(
|
||||
private backupWorkspacePath: string,
|
||||
backupWorkspacePath: string,
|
||||
@IFileService private fileService: IFileService
|
||||
) {
|
||||
this.isShuttingDown = false;
|
||||
this.ioOperationQueues = {};
|
||||
this.ioOperationQueues = new ResourceQueue<void>();
|
||||
|
||||
this.initialize(backupWorkspacePath);
|
||||
}
|
||||
|
||||
public initialize(backupWorkspacePath: string): void {
|
||||
this.backupWorkspacePath = backupWorkspacePath;
|
||||
|
||||
this.ready = this.init();
|
||||
}
|
||||
|
||||
@@ -141,16 +145,6 @@ export class BackupFileService implements IBackupFileService {
|
||||
return backupResource;
|
||||
}
|
||||
|
||||
// Otherwise: on Windows and Mac pre v1.11 we used to store backups in lowercase format
|
||||
// Therefor we also want to check if we have backups of this old format hanging around
|
||||
// TODO@Ben migration
|
||||
if (platform.isWindows || platform.isMacintosh) {
|
||||
const legacyBackupResource = this.getBackupResource(resource, true /* legacyMacWindowsFormat */);
|
||||
if (model.has(legacyBackupResource)) {
|
||||
return legacyBackupResource;
|
||||
}
|
||||
}
|
||||
|
||||
return void 0;
|
||||
});
|
||||
}
|
||||
@@ -173,7 +167,7 @@ export class BackupFileService implements IBackupFileService {
|
||||
// Add metadata to top of file
|
||||
content = `${resource.toString()}${BackupFileService.META_MARKER}${content}`;
|
||||
|
||||
return this.getResourceIOQueue(backupResource).queue(() => {
|
||||
return this.ioOperationQueues.queueFor(backupResource).queue(() => {
|
||||
return this.fileService.updateContent(backupResource, content, BACKUP_FILE_UPDATE_OPTIONS).then(() => model.add(backupResource, versionId));
|
||||
});
|
||||
});
|
||||
@@ -186,40 +180,12 @@ export class BackupFileService implements IBackupFileService {
|
||||
return void 0;
|
||||
}
|
||||
|
||||
return this.getResourceIOQueue(backupResource).queue(() => {
|
||||
return this.ioOperationQueues.queueFor(backupResource).queue(() => {
|
||||
return pfs.del(backupResource.fsPath).then(() => model.remove(backupResource));
|
||||
}).then(() => {
|
||||
|
||||
// On Windows and Mac pre v1.11 we used to store backups in lowercase format
|
||||
// Therefor we also want to check if we have backups of this old format laying around
|
||||
// TODO@Ben migration
|
||||
if (platform.isWindows || platform.isMacintosh) {
|
||||
const legacyBackupResource = this.getBackupResource(resource, true /* legacyMacWindowsFormat */);
|
||||
if (model.has(legacyBackupResource)) {
|
||||
return this.getResourceIOQueue(legacyBackupResource).queue(() => {
|
||||
return pfs.del(legacyBackupResource.fsPath).then(() => model.remove(legacyBackupResource));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return TPromise.as(void 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getResourceIOQueue(resource: Uri) {
|
||||
const key = resource.toString();
|
||||
if (!this.ioOperationQueues[key]) {
|
||||
const queue = new Queue<void>();
|
||||
queue.onFinished(() => {
|
||||
queue.dispose();
|
||||
delete this.ioOperationQueues[key];
|
||||
});
|
||||
this.ioOperationQueues[key] = queue;
|
||||
}
|
||||
return this.ioOperationQueues[key];
|
||||
}
|
||||
|
||||
public discardAllWorkspaceBackups(): TPromise<void> {
|
||||
this.isShuttingDown = true;
|
||||
|
||||
@@ -252,17 +218,15 @@ export class BackupFileService implements IBackupFileService {
|
||||
return textSource.lines.slice(1).join(textSource.EOL); // The first line of a backup text file is the file name
|
||||
}
|
||||
|
||||
protected getBackupResource(resource: Uri, legacyMacWindowsFormat?: boolean): Uri {
|
||||
protected getBackupResource(resource: Uri): Uri {
|
||||
if (!this.backupEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Uri.file(path.join(this.backupWorkspacePath, resource.scheme, this.hashPath(resource, legacyMacWindowsFormat)));
|
||||
return Uri.file(path.join(this.backupWorkspacePath, resource.scheme, this.hashPath(resource)));
|
||||
}
|
||||
|
||||
private hashPath(resource: Uri, legacyMacWindowsFormat?: boolean): string {
|
||||
const caseAwarePath = legacyMacWindowsFormat ? resource.fsPath.toLowerCase() : resource.fsPath;
|
||||
|
||||
return crypto.createHash('md5').update(caseAwarePath).digest('hex');
|
||||
private hashPath(resource: Uri): string {
|
||||
return crypto.createHash('md5').update(resource.fsPath).digest('hex');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,8 @@ import { FileService } from 'vs/workbench/services/files/node/fileService';
|
||||
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
|
||||
import { parseArgs } from 'vs/platform/environment/node/argv';
|
||||
import { RawTextSource } from 'vs/editor/common/model/textSource';
|
||||
import { TestContextService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { Workspace } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestContextService, TestTextResourceConfigurationService, getRandomTestPath } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
|
||||
class TestEnvironmentService extends EnvironmentService {
|
||||
@@ -34,7 +34,7 @@ class TestEnvironmentService extends EnvironmentService {
|
||||
get backupWorkspacesPath(): string { return this._backupWorkspacesPath; }
|
||||
}
|
||||
|
||||
const parentDir = path.join(os.tmpdir(), 'vsctests', 'service');
|
||||
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice');
|
||||
const backupHome = path.join(parentDir, 'Backups');
|
||||
const workspacesJsonPath = path.join(backupHome, 'workspaces.json');
|
||||
|
||||
@@ -44,19 +44,18 @@ const fooFile = Uri.file(platform.isWindows ? 'c:\\Foo' : '/Foo');
|
||||
const barFile = Uri.file(platform.isWindows ? 'c:\\Bar' : '/Bar');
|
||||
const untitledFile = Uri.from({ scheme: 'untitled', path: 'Untitled-1' });
|
||||
const fooBackupPath = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(fooFile.fsPath).digest('hex'));
|
||||
const fooBackupPathLegacy = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(fooFile.fsPath.toLowerCase()).digest('hex'));
|
||||
const barBackupPath = path.join(workspaceBackupPath, 'file', crypto.createHash('md5').update(barFile.fsPath).digest('hex'));
|
||||
const untitledBackupPath = path.join(workspaceBackupPath, 'untitled', crypto.createHash('md5').update(untitledFile.fsPath).digest('hex'));
|
||||
|
||||
class TestBackupFileService extends BackupFileService {
|
||||
constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) {
|
||||
const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, workspace.fsPath, [workspace])), new TestConfigurationService(), { disableWatcher: true });
|
||||
const fileService = new FileService(new TestContextService(new Workspace(workspace.fsPath, workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))), new TestTextResourceConfigurationService(), new TestConfigurationService(), { disableWatcher: true });
|
||||
|
||||
super(workspaceBackupPath, fileService);
|
||||
}
|
||||
|
||||
public getBackupResource(resource: Uri, legacyMacWindowsFormat?: boolean): Uri {
|
||||
return super.getBackupResource(resource, legacyMacWindowsFormat);
|
||||
public getBackupResource(resource: Uri): Uri {
|
||||
return super.getBackupResource(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,47 +114,6 @@ suite('BackupFileService', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should return whether a backup resource exists - legacy support (read old lowercase format as fallback)', done => {
|
||||
if (platform.isLinux) {
|
||||
done();
|
||||
return; // only on mac and windows
|
||||
}
|
||||
|
||||
pfs.mkdirp(path.dirname(fooBackupPath)).then(() => {
|
||||
fs.writeFileSync(fooBackupPathLegacy, 'foo');
|
||||
service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);
|
||||
service.loadBackupResource(fooFile).then(resource => {
|
||||
assert.ok(resource);
|
||||
assert.equal(path.basename(resource.fsPath), path.basename(fooBackupPathLegacy));
|
||||
return service.hasBackups().then(hasBackups => {
|
||||
assert.ok(hasBackups);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should return whether a backup resource exists - legacy support #2 (both cases present, return case sensitive backup)', done => {
|
||||
if (platform.isLinux) {
|
||||
done();
|
||||
return; // only on mac and windows
|
||||
}
|
||||
|
||||
pfs.mkdirp(path.dirname(fooBackupPath)).then(() => {
|
||||
fs.writeFileSync(fooBackupPath, 'foo');
|
||||
fs.writeFileSync(fooBackupPathLegacy, 'foo');
|
||||
service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);
|
||||
service.loadBackupResource(fooFile).then(resource => {
|
||||
assert.ok(resource);
|
||||
assert.equal(path.basename(resource.fsPath), path.basename(fooBackupPath));
|
||||
return service.hasBackups().then(hasBackups => {
|
||||
assert.ok(hasBackups);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('backupResource', () => {
|
||||
@@ -200,27 +158,6 @@ suite('BackupFileService', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('text file - legacy support (dicard lowercase backup file if present)', done => {
|
||||
if (platform.isLinux) {
|
||||
done();
|
||||
return; // only on mac and windows
|
||||
}
|
||||
|
||||
pfs.mkdirp(path.dirname(fooBackupPath)).then(() => {
|
||||
fs.writeFileSync(fooBackupPathLegacy, 'foo');
|
||||
service = new TestBackupFileService(workspaceResource, backupHome, workspacesJsonPath);
|
||||
service.backupResource(fooFile, 'test').then(() => {
|
||||
assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 2);
|
||||
service.discardResourceBackup(fooFile).then(() => {
|
||||
assert.equal(fs.existsSync(fooBackupPath), false);
|
||||
assert.equal(fs.existsSync(fooBackupPathLegacy), false);
|
||||
assert.equal(fs.readdirSync(path.join(workspaceBackupPath, 'file')).length, 0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('discardAllWorkspaceBackups', () => {
|
||||
|
||||
Reference in New Issue
Block a user