提交 eaa959d3 编写于 作者: B Benjamin Pasero

fix #114273

上级 253e9e32
......@@ -4,35 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as platform from 'vs/base/common/platform';
import * as os from 'os';
import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { isWindows } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
import { DefaultEndOfLine } from 'vs/editor/common/model';
import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService';
import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker';
import { workbenchInstantiationService } from 'vs/workbench/test/electron-browser/workbenchTestServices';
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorInput } from 'vs/workbench/common/editor';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/electron-browser/backupFileService.test';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import { isEqual } from 'vs/base/common/resources';
import { TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices';
import { InMemoryTestBackupFileService, TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { BackupRestorer } from 'vs/workbench/contrib/backup/common/backupRestorer';
import { BrowserBackupTracker } from 'vs/workbench/contrib/backup/browser/backupTracker';
class TestBackupRestorer extends BackupRestorer {
async doRestoreBackups(): Promise<URI[] | undefined> {
......@@ -40,49 +25,16 @@ class TestBackupRestorer extends BackupRestorer {
}
}
flakySuite('BackupRestorer', () => {
suite('BackupRestorer', () => {
let accessor: TestServiceAccessor;
let disposables: IDisposable[] = [];
const userdataDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backuprestorer');
const backupHome = path.join(userdataDir, 'Backups');
const workspacesJsonPath = path.join(backupHome, 'workspaces.json');
const workspaceResource = URI.file(platform.isWindows ? 'c:\\workspace' : '/workspace');
const workspaceBackupPath = path.join(backupHome, hashPath(workspaceResource));
const fooFile = URI.file(platform.isWindows ? 'c:\\Foo' : '/Foo');
const barFile = URI.file(platform.isWindows ? 'c:\\Bar' : '/Bar');
const fooFile = URI.file(isWindows ? 'c:\\Foo' : '/Foo');
const barFile = URI.file(isWindows ? 'c:\\Bar' : '/Bar');
const untitledFile1 = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' });
const untitledFile2 = URI.from({ scheme: Schemas.untitled, path: 'Untitled-2' });
setup(async () => {
disposables.push(Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
EditorDescriptor.create(
TextFileEditor,
TextFileEditor.ID,
'Text File Editor'
),
[new SyncDescriptor<EditorInput>(FileEditorInput)]
));
// Delete any existing backups completely and then re-create it.
await pfs.rimraf(backupHome);
await pfs.mkdirp(backupHome);
return pfs.writeFile(workspacesJsonPath, '');
});
teardown(() => {
dispose(disposables);
disposables = [];
(<TextFileEditorModelManager>accessor.textFileService.files).dispose();
return pfs.rimraf(backupHome);
});
test('Restore backups', async function () {
const backupFileService = new NodeTestBackupFileService(workspaceBackupPath);
const backupFileService = new InMemoryTestBackupFileService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IBackupFileService, backupFileService);
......@@ -99,7 +51,7 @@ flakySuite('BackupRestorer', () => {
await part.whenRestored;
const tracker = instantiationService.createInstance(NativeBackupTracker);
const tracker = instantiationService.createInstance(BrowserBackupTracker);
const restorer = instantiationService.createInstance(TestBackupRestorer);
// Backup 2 normal files and 2 untitled file
......
/*---------------------------------------------------------------------------------------------
* 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 { URI } from 'vs/base/common/uri';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
import { IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { toResource } from 'vs/base/test/common/utils';
import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { IWorkingCopyBackup, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { ILogService } from 'vs/platform/log/common/log';
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { InMemoryTestBackupFileService, TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices';
import { CancellationToken } from 'vs/base/common/cancellation';
import { timeout } from 'vs/base/common/async';
import { BrowserBackupTracker } from 'vs/workbench/contrib/backup/browser/backupTracker';
class TestBackupTracker extends BrowserBackupTracker {
constructor(
@IBackupFileService backupFileService: IBackupFileService,
@IFilesConfigurationService filesConfigurationService: IFilesConfigurationService,
@IWorkingCopyService workingCopyService: IWorkingCopyService,
@ILifecycleService lifecycleService: ILifecycleService,
@ILogService logService: ILogService,
) {
super(backupFileService, filesConfigurationService, workingCopyService, lifecycleService, logService);
}
protected getBackupScheduleDelay(): number {
return 10; // Reduce timeout for tests
}
}
suite('BackupTracker (browser)', function () {
let accessor: TestServiceAccessor;
async function createTracker(): Promise<{ accessor: TestServiceAccessor, part: EditorPart, tracker: BackupTracker, backupFileService: InMemoryTestBackupFileService, instantiationService: IInstantiationService, cleanup: () => void }> {
const backupFileService = new InMemoryTestBackupFileService();
const instantiationService = workbenchInstantiationService();
instantiationService.stub(IBackupFileService, backupFileService);
const part = instantiationService.createInstance(EditorPart);
part.create(document.createElement('div'));
part.layout(400, 300);
instantiationService.stub(IEditorGroupsService, part);
const editorService: EditorService = instantiationService.createInstance(EditorService);
instantiationService.stub(IEditorService, editorService);
accessor = instantiationService.createInstance(TestServiceAccessor);
await part.whenRestored;
const tracker = instantiationService.createInstance(TestBackupTracker);
const cleanup = () => {
part.dispose();
tracker.dispose();
};
return { accessor, part, tracker, backupFileService, instantiationService, cleanup };
}
async function untitledBackupTest(untitled: IUntitledTextResourceEditorInput = {}): Promise<void> {
const { accessor, cleanup, backupFileService } = await createTracker();
const untitledEditor = (await accessor.editorService.openEditor(untitled))?.input as UntitledTextEditorInput;
const untitledModel = await untitledEditor.resolve();
if (!untitled?.contents) {
untitledModel.textEditorModel.setValue('Super Good');
}
await backupFileService.joinBackupResource();
assert.equal(backupFileService.hasBackupSync(untitledEditor.resource), true);
untitledModel.dispose();
await backupFileService.joinDiscardBackup();
assert.equal(backupFileService.hasBackupSync(untitledEditor.resource), false);
cleanup();
}
test('Track backups (untitled)', function () {
return untitledBackupTest();
});
test('Track backups (untitled with initial contents)', function () {
return untitledBackupTest({ contents: 'Foo Bar' });
});
test('Track backups (custom)', async function () {
const { accessor, cleanup, backupFileService } = await createTracker();
class TestBackupWorkingCopy extends TestWorkingCopy {
backupDelay = 0;
constructor(resource: URI) {
super(resource);
accessor.workingCopyService.registerWorkingCopy(this);
}
async backup(token: CancellationToken): Promise<IWorkingCopyBackup> {
await timeout(this.backupDelay);
return {};
}
}
const resource = toResource.call(this, '/path/custom.txt');
const customWorkingCopy = new TestBackupWorkingCopy(resource);
// Normal
customWorkingCopy.setDirty(true);
await backupFileService.joinBackupResource();
assert.equal(backupFileService.hasBackupSync(resource), true);
customWorkingCopy.setDirty(false);
customWorkingCopy.setDirty(true);
await backupFileService.joinBackupResource();
assert.equal(backupFileService.hasBackupSync(resource), true);
customWorkingCopy.setDirty(false);
await backupFileService.joinDiscardBackup();
assert.equal(backupFileService.hasBackupSync(resource), false);
// Cancellation
customWorkingCopy.setDirty(true);
await timeout(0);
customWorkingCopy.setDirty(false);
await backupFileService.joinDiscardBackup();
assert.equal(backupFileService.hasBackupSync(resource), false);
customWorkingCopy.dispose();
await cleanup();
});
});
......@@ -18,7 +18,7 @@ import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { EditorService } from 'vs/workbench/services/editor/browser/editorService';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorInput, IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
......@@ -28,7 +28,7 @@ import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/ele
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { toResource } from 'vs/base/test/common/utils';
import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
import { IWorkingCopyBackup, IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { ILogService } from 'vs/platform/log/common/log';
import { HotExitConfiguration } from 'vs/platform/files/common/files';
import { ShutdownReason, ILifecycleService, BeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle';
......@@ -38,16 +38,12 @@ import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { BackupTracker } from 'vs/workbench/contrib/backup/common/backupTracker';
import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/electron-browser/workbenchTestServices';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestFilesConfigurationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices';
import { CancellationToken } from 'vs/base/common/cancellation';
import { timeout } from 'vs/base/common/async';
import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace';
import { IProgressService } from 'vs/platform/progress/common/progress';
......@@ -92,7 +88,7 @@ class BeforeShutdownEventImpl implements BeforeShutdownEvent {
}
}
flakySuite('BackupTracker', function () {
flakySuite('BackupTracker (native)', function () {
let backupHome: string;
let workspaceBackupPath: string;
......@@ -176,38 +172,6 @@ flakySuite('BackupTracker', function () {
return { accessor, part, tracker, instantiationService, cleanup };
}
async function untitledBackupTest(untitled: IUntitledTextResourceEditorInput = {}): Promise<void> {
const { accessor, cleanup } = await createTracker();
const untitledEditor = (await accessor.editorService.openEditor(untitled))?.input as UntitledTextEditorInput;
const untitledModel = await untitledEditor.resolve();
if (!untitled?.contents) {
untitledModel.textEditorModel.setValue('Super Good');
}
await accessor.backupFileService.joinBackupResource();
assert.equal(accessor.backupFileService.hasBackupSync(untitledEditor.resource), true);
untitledModel.dispose();
await accessor.backupFileService.joinDiscardBackup();
assert.equal(accessor.backupFileService.hasBackupSync(untitledEditor.resource), false);
await cleanup();
}
test('Track backups (untitled)', function () {
return untitledBackupTest();
});
test('Track backups (untitled with initial contents)', function () {
return untitledBackupTest({ contents: 'Foo Bar' });
});
test('Track backups (file)', async function () {
const { accessor, cleanup } = await createTracker();
......@@ -230,54 +194,6 @@ flakySuite('BackupTracker', function () {
await cleanup();
});
test('Track backups (custom)', async function () {
const { accessor, cleanup } = await createTracker();
class TestBackupWorkingCopy extends TestWorkingCopy {
backupDelay = 0;
constructor(resource: URI) {
super(resource);
accessor.workingCopyService.registerWorkingCopy(this);
}
async backup(token: CancellationToken): Promise<IWorkingCopyBackup> {
await timeout(this.backupDelay);
return {};
}
}
const resource = toResource.call(this, '/path/custom.txt');
const customWorkingCopy = new TestBackupWorkingCopy(resource);
// Normal
customWorkingCopy.setDirty(true);
await accessor.backupFileService.joinBackupResource();
assert.equal(accessor.backupFileService.hasBackupSync(resource), true);
customWorkingCopy.setDirty(false);
customWorkingCopy.setDirty(true);
await accessor.backupFileService.joinBackupResource();
assert.equal(accessor.backupFileService.hasBackupSync(resource), true);
customWorkingCopy.setDirty(false);
await accessor.backupFileService.joinDiscardBackup();
assert.equal(accessor.backupFileService.hasBackupSync(resource), false);
// Cancellation
customWorkingCopy.setDirty(true);
await timeout(0);
customWorkingCopy.setDirty(false);
await accessor.backupFileService.joinDiscardBackup();
assert.equal(accessor.backupFileService.hasBackupSync(resource), false);
customWorkingCopy.dispose();
await cleanup();
});
test('onWillShutdown - no veto if no dirty files', async function () {
const { accessor, cleanup } = await createTracker();
......
......@@ -13,7 +13,7 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil
import { IEditorInputWithOptions, IEditorIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, IEditorInput, IEditorPane, IEditorCloseEvent, IEditorPartOptions, IRevertOptions, GroupIdentifier, EditorInput, EditorOptions, EditorsOrder, IFileEditorInput, IEditorInputFactoryRegistry, IEditorInputFactory, Extensions as EditorExtensions, ISaveOptions, IMoveResult, ITextEditorPane, ITextDiffEditorPane, IVisibleEditorPane, IEditorOpenContext } from 'vs/workbench/common/editor';
import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView, IEditorGroupsAccessor, IEditorGroupTitleDimensions } from 'vs/workbench/browser/parts/editor/editor';
import { Event, Emitter } from 'vs/base/common/event';
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService, Parts, Position as PartPosition } from 'vs/workbench/services/layout/browser/layoutService';
import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService';
......@@ -114,6 +114,10 @@ import { EncodingOracle, IEncodingOverride } from 'vs/workbench/services/textfil
import { UTF16le, UTF16be, UTF8_with_bom } from 'vs/workbench/services/textfile/common/encoding';
import { ColorScheme } from 'vs/platform/theme/common/theme';
import { Iterable } from 'vs/base/common/iterator';
import { InMemoryBackupFileService } from 'vs/workbench/services/backup/common/backupFileService';
import { hash } from 'vs/base/common/hash';
import { BrowserBackupFileService } from 'vs/workbench/services/backup/browser/backupFileService';
import { FileService } from 'vs/platform/files/common/fileService';
export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput {
return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined);
......@@ -893,19 +897,12 @@ export class TestFileService implements IFileService {
async canDelete(resource: URI, options?: { useTrash?: boolean | undefined; recursive?: boolean | undefined; } | undefined): Promise<Error | true> { return true; }
}
export class TestBackupFileService implements IBackupFileService {
declare readonly _serviceBrand: undefined;
export class TestBackupFileService extends InMemoryBackupFileService {
constructor() {
super(resource => String(hash(resource.path)));
}
hasBackups(): Promise<boolean> { return Promise.resolve(false); }
hasBackup(_resource: URI): Promise<boolean> { return Promise.resolve(false); }
hasBackupSync(resource: URI, versionId?: number): boolean { return false; }
async registerResourceForBackup(_resource: URI): Promise<void> { }
async deregisterResourceForBackup(_resource: URI): Promise<void> { }
async backup<T extends object>(_resource: URI, _content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> { }
getBackups(): Promise<URI[]> { return Promise.resolve([]); }
resolve<T extends object>(_backup: URI): Promise<IResolvedBackup<T> | undefined> { return Promise.resolve(undefined); }
async discardBackup(_resource: URI): Promise<void> { }
async discardBackups(): Promise<void> { }
parseBackupContent(textBufferFactory: ITextBufferFactory): string {
const textBuffer = textBufferFactory.create(DefaultEndOfLine.LF).textBuffer;
const lineCount = textBuffer.getLineCount();
......@@ -914,6 +911,64 @@ export class TestBackupFileService implements IBackupFileService {
}
}
export class InMemoryTestBackupFileService extends BrowserBackupFileService {
readonly fileService: IFileService;
private backupResourceJoiners: Function[];
private discardBackupJoiners: Function[];
discardedBackups: URI[];
constructor() {
const environmentService = TestEnvironmentService;
const logService = new NullLogService();
const fileService = new FileService(logService);
fileService.registerProvider(Schemas.file, new InMemoryFileSystemProvider());
fileService.registerProvider(Schemas.userData, new InMemoryFileSystemProvider());
super(new TestContextService(TestWorkspace), environmentService, fileService, logService);
this.fileService = fileService;
this.backupResourceJoiners = [];
this.discardBackupJoiners = [];
this.discardedBackups = [];
}
joinBackupResource(): Promise<void> {
return new Promise(resolve => this.backupResourceJoiners.push(resolve));
}
joinDiscardBackup(): Promise<void> {
return new Promise(resolve => this.discardBackupJoiners.push(resolve));
}
async backup(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: any, token?: CancellationToken): Promise<void> {
await super.backup(resource, content, versionId, meta, token);
while (this.backupResourceJoiners.length) {
this.backupResourceJoiners.pop()!();
}
}
async discardBackup(resource: URI): Promise<void> {
await super.discardBackup(resource);
this.discardedBackups.push(resource);
while (this.discardBackupJoiners.length) {
this.discardBackupJoiners.pop()!();
}
}
async getBackupContents(resource: URI): Promise<string> {
const backupResource = this.toBackupResource(resource);
const fileContents = await this.fileService.readFile(backupResource);
return fileContents.value.toString();
}
}
export class TestLifecycleService implements ILifecycleService {
declare readonly _serviceBrand: undefined;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册