Path: blob/main/src/vs/platform/backup/test/electron-main/backupMainService.test.ts
3297 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { createHash } from 'crypto';7import * as fs from 'fs';8import * as os from 'os';9import { Schemas } from '../../../../base/common/network.js';10import * as path from '../../../../base/common/path.js';11import * as platform from '../../../../base/common/platform.js';12import { isEqual } from '../../../../base/common/resources.js';13import { URI } from '../../../../base/common/uri.js';14import { Promises } from '../../../../base/node/pfs.js';15import { flakySuite, getRandomTestPath } from '../../../../base/test/node/testUtils.js';16import { BackupMainService } from '../../electron-main/backupMainService.js';17import { ISerializedBackupWorkspaces, ISerializedWorkspaceBackupInfo } from '../../node/backup.js';18import { TestConfigurationService } from '../../../configuration/test/common/testConfigurationService.js';19import { EnvironmentMainService } from '../../../environment/electron-main/environmentMainService.js';20import { OPTIONS, parseArgs } from '../../../environment/node/argv.js';21import { HotExitConfiguration } from '../../../files/common/files.js';22import { ConsoleMainLogger } from '../../../log/common/log.js';23import product from '../../../product/common/product.js';24import { IFolderBackupInfo, isFolderBackupInfo, IWorkspaceBackupInfo } from '../../common/backup.js';25import { IWorkspaceIdentifier } from '../../../workspace/common/workspace.js';26import { InMemoryTestStateMainService } from '../../../test/electron-main/workbenchTestServices.js';27import { LogService } from '../../../log/common/logService.js';28import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';2930flakySuite('BackupMainService', () => {3132function assertEqualFolderInfos(actual: IFolderBackupInfo[], expected: IFolderBackupInfo[]) {33const withUriAsString = (f: IFolderBackupInfo) => ({ folderUri: f.folderUri.toString(), remoteAuthority: f.remoteAuthority });34assert.deepStrictEqual(actual.map(withUriAsString), expected.map(withUriAsString));35}3637function toWorkspace(path: string): IWorkspaceIdentifier {38return {39id: createHash('md5').update(sanitizePath(path)).digest('hex'), // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length40configPath: URI.file(path)41};42}4344function toWorkspaceBackupInfo(path: string, remoteAuthority?: string): IWorkspaceBackupInfo {45return {46workspace: {47id: createHash('md5').update(sanitizePath(path)).digest('hex'), // CodeQL [SM04514] Using MD5 to convert a file path to a fixed length48configPath: URI.file(path)49},50remoteAuthority51};52}5354function toFolderBackupInfo(uri: URI, remoteAuthority?: string): IFolderBackupInfo {55return { folderUri: uri, remoteAuthority };56}5758function toSerializedWorkspace(ws: IWorkspaceIdentifier): ISerializedWorkspaceBackupInfo {59return {60id: ws.id,61configURIPath: ws.configPath.toString()62};63}6465function ensureFolderExists(uri: URI): Promise<void> {66if (!fs.existsSync(uri.fsPath)) {67fs.mkdirSync(uri.fsPath);68}6970const backupFolder = service.toBackupPath(uri);71return createBackupFolder(backupFolder);72}7374async function ensureWorkspaceExists(workspace: IWorkspaceIdentifier): Promise<IWorkspaceIdentifier> {75if (!fs.existsSync(workspace.configPath.fsPath)) {76await Promises.writeFile(workspace.configPath.fsPath, 'Hello');77}7879const backupFolder = service.toBackupPath(workspace.id);80await createBackupFolder(backupFolder);8182return workspace;83}8485async function createBackupFolder(backupFolder: string): Promise<void> {86if (!fs.existsSync(backupFolder)) {87fs.mkdirSync(backupFolder);88fs.mkdirSync(path.join(backupFolder, Schemas.file));89await Promises.writeFile(path.join(backupFolder, Schemas.file, 'foo.txt'), 'Hello');90}91}9293function readWorkspacesMetadata(): ISerializedBackupWorkspaces {94return stateMainService.getItem('backupWorkspaces') as ISerializedBackupWorkspaces;95}9697function writeWorkspacesMetadata(data: string): void {98if (!data) {99stateMainService.removeItem('backupWorkspaces');100} else {101stateMainService.setItem('backupWorkspaces', JSON.parse(data));102}103}104105function sanitizePath(p: string): string {106return platform.isLinux ? p : p.toLowerCase();107}108109const fooFile = URI.file(platform.isWindows ? 'C:\\foo' : '/foo');110const barFile = URI.file(platform.isWindows ? 'C:\\bar' : '/bar');111112let service: BackupMainService & {113toBackupPath(arg: URI | string): string;114testGetFolderHash(folder: IFolderBackupInfo): string;115testGetWorkspaceBackups(): IWorkspaceBackupInfo[];116testGetFolderBackups(): IFolderBackupInfo[];117};118let configService: TestConfigurationService;119let stateMainService: InMemoryTestStateMainService;120121let environmentService: EnvironmentMainService;122let testDir: string;123let backupHome: string;124let existingTestFolder1: URI;125126setup(async () => {127testDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupmainservice');128backupHome = path.join(testDir, 'Backups');129existingTestFolder1 = URI.file(path.join(testDir, 'folder1'));130131environmentService = new EnvironmentMainService(parseArgs(process.argv, OPTIONS), { _serviceBrand: undefined, ...product });132133await fs.promises.mkdir(backupHome, { recursive: true });134135configService = new TestConfigurationService();136stateMainService = new InMemoryTestStateMainService();137138service = new class TestBackupMainService extends BackupMainService {139constructor() {140super(environmentService, configService, new LogService(new ConsoleMainLogger()), stateMainService);141142this.backupHome = backupHome;143}144145toBackupPath(arg: URI | string): string {146const id = arg instanceof URI ? super.getFolderHash({ folderUri: arg }) : arg;147return path.join(this.backupHome, id);148}149150testGetFolderHash(folder: IFolderBackupInfo): string {151return super.getFolderHash(folder);152}153154testGetWorkspaceBackups(): IWorkspaceBackupInfo[] {155return super.getWorkspaceBackups();156}157158testGetFolderBackups(): IFolderBackupInfo[] {159return super.getFolderBackups();160}161};162163return service.initialize();164});165166teardown(() => {167return Promises.rm(testDir);168});169170test('service validates backup workspaces on startup and cleans up (folder workspaces)', async function () {171172// 1) backup workspace path does not exist173service.registerFolderBackup(toFolderBackupInfo(fooFile));174service.registerFolderBackup(toFolderBackupInfo(barFile));175await service.initialize();176assertEqualFolderInfos(service.testGetFolderBackups(), []);177178// 2) backup workspace path exists with empty contents within179fs.mkdirSync(service.toBackupPath(fooFile));180fs.mkdirSync(service.toBackupPath(barFile));181service.registerFolderBackup(toFolderBackupInfo(fooFile));182service.registerFolderBackup(toFolderBackupInfo(barFile));183await service.initialize();184assertEqualFolderInfos(service.testGetFolderBackups(), []);185assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));186assert.ok(!fs.existsSync(service.toBackupPath(barFile)));187188// 3) backup workspace path exists with empty folders within189fs.mkdirSync(service.toBackupPath(fooFile));190fs.mkdirSync(service.toBackupPath(barFile));191fs.mkdirSync(path.join(service.toBackupPath(fooFile), Schemas.file));192fs.mkdirSync(path.join(service.toBackupPath(barFile), Schemas.untitled));193service.registerFolderBackup(toFolderBackupInfo(fooFile));194service.registerFolderBackup(toFolderBackupInfo(barFile));195await service.initialize();196assertEqualFolderInfos(service.testGetFolderBackups(), []);197assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));198assert.ok(!fs.existsSync(service.toBackupPath(barFile)));199200// 4) backup workspace path points to a workspace that no longer exists201// so it should convert the backup worspace to an empty workspace backup202const fileBackups = path.join(service.toBackupPath(fooFile), Schemas.file);203fs.mkdirSync(service.toBackupPath(fooFile));204fs.mkdirSync(service.toBackupPath(barFile));205fs.mkdirSync(fileBackups);206service.registerFolderBackup(toFolderBackupInfo(fooFile));207assert.strictEqual(service.testGetFolderBackups().length, 1);208assert.strictEqual(service.getEmptyWindowBackups().length, 0);209fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');210await service.initialize();211assert.strictEqual(service.testGetFolderBackups().length, 0);212assert.strictEqual(service.getEmptyWindowBackups().length, 1);213});214215test('service validates backup workspaces on startup and cleans up (root workspaces)', async function () {216217// 1) backup workspace path does not exist218service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));219service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath));220await service.initialize();221assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);222223// 2) backup workspace path exists with empty contents within224fs.mkdirSync(service.toBackupPath(fooFile));225fs.mkdirSync(service.toBackupPath(barFile));226service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));227service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath));228await service.initialize();229assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);230assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));231assert.ok(!fs.existsSync(service.toBackupPath(barFile)));232233// 3) backup workspace path exists with empty folders within234fs.mkdirSync(service.toBackupPath(fooFile));235fs.mkdirSync(service.toBackupPath(barFile));236fs.mkdirSync(path.join(service.toBackupPath(fooFile), Schemas.file));237fs.mkdirSync(path.join(service.toBackupPath(barFile), Schemas.untitled));238service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));239service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath));240await service.initialize();241assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);242assert.ok(!fs.existsSync(service.toBackupPath(fooFile)));243assert.ok(!fs.existsSync(service.toBackupPath(barFile)));244245// 4) backup workspace path points to a workspace that no longer exists246// so it should convert the backup worspace to an empty workspace backup247const fileBackups = path.join(service.toBackupPath(fooFile), Schemas.file);248fs.mkdirSync(service.toBackupPath(fooFile));249fs.mkdirSync(service.toBackupPath(barFile));250fs.mkdirSync(fileBackups);251service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));252assert.strictEqual(service.testGetWorkspaceBackups().length, 1);253assert.strictEqual(service.getEmptyWindowBackups().length, 0);254fs.writeFileSync(path.join(fileBackups, 'backup.txt'), '');255await service.initialize();256assert.strictEqual(service.testGetWorkspaceBackups().length, 0);257assert.strictEqual(service.getEmptyWindowBackups().length, 1);258});259260test('service supports to migrate backup data from another location', async () => {261const backupPathToMigrate = service.toBackupPath(fooFile);262fs.mkdirSync(backupPathToMigrate);263fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');264service.registerFolderBackup(toFolderBackupInfo(URI.file(backupPathToMigrate)));265266const workspaceBackupPath = await service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate);267268assert.ok(fs.existsSync(workspaceBackupPath));269assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt')));270assert.ok(!fs.existsSync(backupPathToMigrate));271272const emptyBackups = service.getEmptyWindowBackups();273assert.strictEqual(0, emptyBackups.length);274});275276test('service backup migration makes sure to preserve existing backups', async () => {277const backupPathToMigrate = service.toBackupPath(fooFile);278fs.mkdirSync(backupPathToMigrate);279fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data');280service.registerFolderBackup(toFolderBackupInfo(URI.file(backupPathToMigrate)));281282const backupPathToPreserve = service.toBackupPath(barFile);283fs.mkdirSync(backupPathToPreserve);284fs.writeFileSync(path.join(backupPathToPreserve, 'backup.txt'), 'Some Data');285service.registerFolderBackup(toFolderBackupInfo(URI.file(backupPathToPreserve)));286287const workspaceBackupPath = await service.registerWorkspaceBackup(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate);288289assert.ok(fs.existsSync(workspaceBackupPath));290assert.ok(fs.existsSync(path.join(workspaceBackupPath, 'backup.txt')));291assert.ok(!fs.existsSync(backupPathToMigrate));292293const emptyBackups = service.getEmptyWindowBackups();294assert.strictEqual(1, emptyBackups.length);295assert.strictEqual(1, fs.readdirSync(path.join(backupHome, emptyBackups[0].backupFolder)).length);296});297298suite('loadSync', () => {299test('getFolderBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {300assertEqualFolderInfos(service.testGetFolderBackups(), []);301});302303test('getFolderBackupPaths() should return [] when folders in workspaces.json is absent', async () => {304writeWorkspacesMetadata('{}');305await service.initialize();306assertEqualFolderInfos(service.testGetFolderBackups(), []);307});308309test('getFolderBackupPaths() should return [] when folders in workspaces.json is not a string array', async () => {310writeWorkspacesMetadata('{"folders":{}}');311await service.initialize();312assertEqualFolderInfos(service.testGetFolderBackups(), []);313writeWorkspacesMetadata('{"folders":{"foo": ["bar"]}}');314await service.initialize();315assertEqualFolderInfos(service.testGetFolderBackups(), []);316writeWorkspacesMetadata('{"folders":{"foo": []}}');317await service.initialize();318assertEqualFolderInfos(service.testGetFolderBackups(), []);319writeWorkspacesMetadata('{"folders":{"foo": "bar"}}');320await service.initialize();321assertEqualFolderInfos(service.testGetFolderBackups(), []);322writeWorkspacesMetadata('{"folders":"foo"}');323await service.initialize();324assertEqualFolderInfos(service.testGetFolderBackups(), []);325writeWorkspacesMetadata('{"folders":1}');326await service.initialize();327assertEqualFolderInfos(service.testGetFolderBackups(), []);328});329330test('getFolderBackupPaths() should return [] when files.hotExit = "onExitAndWindowClose"', async () => {331const fi = toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase()));332service.registerFolderBackup(fi);333assertEqualFolderInfos(service.testGetFolderBackups(), [fi]);334configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);335await service.initialize();336assertEqualFolderInfos(service.testGetFolderBackups(), []);337});338339test('getWorkspaceBackups() should return [] when workspaces.json doesn\'t exist', () => {340assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);341});342343test('getWorkspaceBackups() should return [] when folderWorkspaces in workspaces.json is absent', async () => {344writeWorkspacesMetadata('{}');345await service.initialize();346assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);347});348349test('getWorkspaceBackups() should return [] when rootWorkspaces in workspaces.json is not a object array', async () => {350writeWorkspacesMetadata('{"rootWorkspaces":{}}');351await service.initialize();352assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);353writeWorkspacesMetadata('{"rootWorkspaces":{"foo": ["bar"]}}');354await service.initialize();355assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);356writeWorkspacesMetadata('{"rootWorkspaces":{"foo": []}}');357await service.initialize();358assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);359writeWorkspacesMetadata('{"rootWorkspaces":{"foo": "bar"}}');360await service.initialize();361assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);362writeWorkspacesMetadata('{"rootWorkspaces":"foo"}');363await service.initialize();364assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);365writeWorkspacesMetadata('{"rootWorkspaces":1}');366await service.initialize();367assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);368});369370test('getWorkspaceBackups() should return [] when workspaces in workspaces.json is not a object array', async () => {371writeWorkspacesMetadata('{"workspaces":{}}');372await service.initialize();373assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);374writeWorkspacesMetadata('{"workspaces":{"foo": ["bar"]}}');375await service.initialize();376assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);377writeWorkspacesMetadata('{"workspaces":{"foo": []}}');378await service.initialize();379assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);380writeWorkspacesMetadata('{"workspaces":{"foo": "bar"}}');381await service.initialize();382assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);383writeWorkspacesMetadata('{"workspaces":"foo"}');384await service.initialize();385assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);386writeWorkspacesMetadata('{"workspaces":1}');387await service.initialize();388assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);389});390391test('getWorkspaceBackups() should return [] when files.hotExit = "onExitAndWindowClose"', async () => {392const upperFooPath = fooFile.fsPath.toUpperCase();393service.registerWorkspaceBackup(toWorkspaceBackupInfo(upperFooPath));394assert.strictEqual(service.testGetWorkspaceBackups().length, 1);395assert.deepStrictEqual(service.testGetWorkspaceBackups().map(r => r.workspace.configPath.toString()), [URI.file(upperFooPath).toString()]);396configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE);397await service.initialize();398assert.deepStrictEqual(service.testGetWorkspaceBackups(), []);399});400401test('getEmptyWorkspaceBackupPaths() should return [] when workspaces.json doesn\'t exist', () => {402assert.deepStrictEqual(service.getEmptyWindowBackups(), []);403});404405test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is absent', async () => {406writeWorkspacesMetadata('{}');407await service.initialize();408assert.deepStrictEqual(service.getEmptyWindowBackups(), []);409});410411test('getEmptyWorkspaceBackupPaths() should return [] when folderWorkspaces in workspaces.json is not a string array', async function () {412writeWorkspacesMetadata('{"emptyWorkspaces":{}}');413await service.initialize();414assert.deepStrictEqual(service.getEmptyWindowBackups(), []);415writeWorkspacesMetadata('{"emptyWorkspaces":{"foo": ["bar"]}}');416await service.initialize();417assert.deepStrictEqual(service.getEmptyWindowBackups(), []);418writeWorkspacesMetadata('{"emptyWorkspaces":{"foo": []}}');419await service.initialize();420assert.deepStrictEqual(service.getEmptyWindowBackups(), []);421writeWorkspacesMetadata('{"emptyWorkspaces":{"foo": "bar"}}');422await service.initialize();423assert.deepStrictEqual(service.getEmptyWindowBackups(), []);424writeWorkspacesMetadata('{"emptyWorkspaces":"foo"}');425await service.initialize();426assert.deepStrictEqual(service.getEmptyWindowBackups(), []);427writeWorkspacesMetadata('{"emptyWorkspaces":1}');428await service.initialize();429assert.deepStrictEqual(service.getEmptyWindowBackups(), []);430});431});432433suite('dedupeFolderWorkspaces', () => {434test('should ignore duplicates (folder workspace)', async () => {435436await ensureFolderExists(existingTestFolder1);437438const workspacesJson: ISerializedBackupWorkspaces = {439workspaces: [],440folders: [{ folderUri: existingTestFolder1.toString() }, { folderUri: existingTestFolder1.toString() }],441emptyWindows: []442};443writeWorkspacesMetadata(JSON.stringify(workspacesJson));444await service.initialize();445446const json = readWorkspacesMetadata();447assert.deepStrictEqual(json.folders, [{ folderUri: existingTestFolder1.toString() }]);448});449450test('should ignore duplicates on Windows and Mac (folder workspace)', async () => {451452await ensureFolderExists(existingTestFolder1);453454const workspacesJson: ISerializedBackupWorkspaces = {455workspaces: [],456folders: [{ folderUri: existingTestFolder1.toString() }, { folderUri: existingTestFolder1.toString().toLowerCase() }],457emptyWindows: []458};459writeWorkspacesMetadata(JSON.stringify(workspacesJson));460await service.initialize();461const json = readWorkspacesMetadata();462assert.deepStrictEqual(json.folders, [{ folderUri: existingTestFolder1.toString() }]);463});464465test('should ignore duplicates on Windows and Mac (root workspace)', async () => {466const workspacePath = path.join(testDir, 'Foo.code-workspace');467const workspacePath1 = path.join(testDir, 'FOO.code-workspace');468const workspacePath2 = path.join(testDir, 'foo.code-workspace');469470const workspace1 = await ensureWorkspaceExists(toWorkspace(workspacePath));471const workspace2 = await ensureWorkspaceExists(toWorkspace(workspacePath1));472const workspace3 = await ensureWorkspaceExists(toWorkspace(workspacePath2));473474const workspacesJson: ISerializedBackupWorkspaces = {475workspaces: [workspace1, workspace2, workspace3].map(toSerializedWorkspace),476folders: [],477emptyWindows: []478};479writeWorkspacesMetadata(JSON.stringify(workspacesJson));480await service.initialize();481482const json = readWorkspacesMetadata();483assert.strictEqual(json.workspaces.length, platform.isLinux ? 3 : 1);484if (platform.isLinux) {485assert.deepStrictEqual(json.workspaces.map(r => r.configURIPath), [URI.file(workspacePath).toString(), URI.file(workspacePath1).toString(), URI.file(workspacePath2).toString()]);486} else {487assert.deepStrictEqual(json.workspaces.map(r => r.configURIPath), [URI.file(workspacePath).toString()], 'should return the first duplicated entry');488}489});490});491492suite('registerWindowForBackups', () => {493test('should persist paths to workspaces.json (folder workspace)', async () => {494service.registerFolderBackup(toFolderBackupInfo(fooFile));495service.registerFolderBackup(toFolderBackupInfo(barFile));496assertEqualFolderInfos(service.testGetFolderBackups(), [toFolderBackupInfo(fooFile), toFolderBackupInfo(barFile)]);497498const json = readWorkspacesMetadata();499assert.deepStrictEqual(json.folders, [{ folderUri: fooFile.toString() }, { folderUri: barFile.toString() }]);500});501502test('should persist paths to workspaces.json (root workspace)', async () => {503const ws1 = toWorkspaceBackupInfo(fooFile.fsPath);504service.registerWorkspaceBackup(ws1);505const ws2 = toWorkspaceBackupInfo(barFile.fsPath);506service.registerWorkspaceBackup(ws2);507508assert.deepStrictEqual(service.testGetWorkspaceBackups().map(b => b.workspace.configPath.toString()), [fooFile.toString(), barFile.toString()]);509assert.strictEqual(ws1.workspace.id, service.testGetWorkspaceBackups()[0].workspace.id);510assert.strictEqual(ws2.workspace.id, service.testGetWorkspaceBackups()[1].workspace.id);511512const json = readWorkspacesMetadata();513assert.deepStrictEqual(json.workspaces.map(b => b.configURIPath), [fooFile.toString(), barFile.toString()]);514assert.strictEqual(ws1.workspace.id, json.workspaces[0].id);515assert.strictEqual(ws2.workspace.id, json.workspaces[1].id);516});517});518519test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (folder workspace)', async () => {520service.registerFolderBackup(toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase())));521assertEqualFolderInfos(service.testGetFolderBackups(), [toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase()))]);522523const json = readWorkspacesMetadata();524assert.deepStrictEqual(json.folders, [{ folderUri: URI.file(fooFile.fsPath.toUpperCase()).toString() }]);525});526527test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (root workspace)', async () => {528const upperFooPath = fooFile.fsPath.toUpperCase();529service.registerWorkspaceBackup(toWorkspaceBackupInfo(upperFooPath));530assert.deepStrictEqual(service.testGetWorkspaceBackups().map(b => b.workspace.configPath.toString()), [URI.file(upperFooPath).toString()]);531532const json = readWorkspacesMetadata();533assert.deepStrictEqual(json.workspaces.map(b => b.configURIPath), [URI.file(upperFooPath).toString()]);534});535536suite('getWorkspaceHash', () => {537(platform.isLinux ? test.skip : test)('should ignore case on Windows and Mac', () => {538const assertFolderHash = (uri1: URI, uri2: URI) => {539assert.strictEqual(service.testGetFolderHash(toFolderBackupInfo(uri1)), service.testGetFolderHash(toFolderBackupInfo(uri2)));540};541542if (platform.isMacintosh) {543assertFolderHash(URI.file('/foo'), URI.file('/FOO'));544}545546if (platform.isWindows) {547assertFolderHash(URI.file('c:\\foo'), URI.file('C:\\FOO'));548}549});550});551552suite('mixed path casing', () => {553test('should handle case insensitive paths properly (registerWindowForBackupsSync) (folder workspace)', () => {554service.registerFolderBackup(toFolderBackupInfo(fooFile));555service.registerFolderBackup(toFolderBackupInfo(URI.file(fooFile.fsPath.toUpperCase())));556557if (platform.isLinux) {558assert.strictEqual(service.testGetFolderBackups().length, 2);559} else {560assert.strictEqual(service.testGetFolderBackups().length, 1);561}562});563564test('should handle case insensitive paths properly (registerWindowForBackupsSync) (root workspace)', () => {565service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath));566service.registerWorkspaceBackup(toWorkspaceBackupInfo(fooFile.fsPath.toUpperCase()));567568if (platform.isLinux) {569assert.strictEqual(service.testGetWorkspaceBackups().length, 2);570} else {571assert.strictEqual(service.testGetWorkspaceBackups().length, 1);572}573});574});575576suite('getDirtyWorkspaces', () => {577test('should report if a workspace or folder has backups', async () => {578const folderBackupPath = service.registerFolderBackup(toFolderBackupInfo(fooFile));579580const backupWorkspaceInfo = toWorkspaceBackupInfo(fooFile.fsPath);581const workspaceBackupPath = service.registerWorkspaceBackup(backupWorkspaceInfo);582583assert.strictEqual(((await service.getDirtyWorkspaces()).length), 0);584585try {586await fs.promises.mkdir(path.join(folderBackupPath, Schemas.file), { recursive: true });587await fs.promises.mkdir(path.join(workspaceBackupPath, Schemas.untitled), { recursive: true });588} catch (error) {589// ignore - folder might exist already590}591592assert.strictEqual(((await service.getDirtyWorkspaces()).length), 0);593594fs.writeFileSync(path.join(folderBackupPath, Schemas.file, '594a4a9d82a277a899d4713a5b08f504'), '');595fs.writeFileSync(path.join(workspaceBackupPath, Schemas.untitled, '594a4a9d82a277a899d4713a5b08f504'), '');596597const dirtyWorkspaces = await service.getDirtyWorkspaces();598assert.strictEqual(dirtyWorkspaces.length, 2);599600let found = 0;601for (const dirtyWorkpspace of dirtyWorkspaces) {602if (isFolderBackupInfo(dirtyWorkpspace)) {603if (isEqual(fooFile, dirtyWorkpspace.folderUri)) {604found++;605}606} else {607if (isEqual(backupWorkspaceInfo.workspace.configPath, dirtyWorkpspace.workspace.configPath)) {608found++;609}610}611}612613assert.strictEqual(found, 2);614});615});616617ensureNoDisposablesAreLeakedInTestSuite();618});619620621