Path: blob/main/src/vs/platform/extensionManagement/test/common/extensionsProfileScannerService.test.ts
3296 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 * as sinon from 'sinon';7import { VSBuffer } from '../../../../base/common/buffer.js';8import { joinPath } from '../../../../base/common/resources.js';9import { URI } from '../../../../base/common/uri.js';10import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';11import { IEnvironmentService } from '../../../environment/common/environment.js';12import { AbstractExtensionsProfileScannerService, ProfileExtensionsEvent } from '../../common/extensionsProfileScannerService.js';13import { ExtensionType, IExtension, IExtensionManifest, TargetPlatform } from '../../../extensions/common/extensions.js';14import { FileService } from '../../../files/common/fileService.js';15import { IFileService } from '../../../files/common/files.js';16import { InMemoryFileSystemProvider } from '../../../files/common/inMemoryFilesystemProvider.js';17import { TestInstantiationService } from '../../../instantiation/test/common/instantiationServiceMock.js';18import { ILogService, NullLogService } from '../../../log/common/log.js';19import { ITelemetryService } from '../../../telemetry/common/telemetry.js';20import { NullTelemetryService } from '../../../telemetry/common/telemetryUtils.js';21import { IUriIdentityService } from '../../../uriIdentity/common/uriIdentity.js';22import { UriIdentityService } from '../../../uriIdentity/common/uriIdentityService.js';23import { IUserDataProfilesService, UserDataProfilesService } from '../../../userDataProfile/common/userDataProfile.js';2425class TestObject extends AbstractExtensionsProfileScannerService { }2627suite('ExtensionsProfileScannerService', () => {2829const ROOT = URI.file('/ROOT');30const disposables = ensureNoDisposablesAreLeakedInTestSuite();3132const extensionsLocation = joinPath(ROOT, 'extensions');33let instantiationService: TestInstantiationService;3435setup(async () => {36instantiationService = disposables.add(new TestInstantiationService());37const logService = new NullLogService();38const fileService = disposables.add(new FileService(logService));39const fileSystemProvider = disposables.add(new InMemoryFileSystemProvider());40disposables.add(fileService.registerProvider(ROOT.scheme, fileSystemProvider));41instantiationService.stub(ILogService, logService);42instantiationService.stub(IFileService, fileService);43instantiationService.stub(ITelemetryService, NullTelemetryService);44const uriIdentityService = instantiationService.stub(IUriIdentityService, disposables.add(new UriIdentityService(fileService)));45const environmentService = instantiationService.stub(IEnvironmentService, { userRoamingDataHome: ROOT, cacheHome: joinPath(ROOT, 'cache'), });46const userDataProfilesService = disposables.add(new UserDataProfilesService(environmentService, fileService, uriIdentityService, logService));47instantiationService.stub(IUserDataProfilesService, userDataProfilesService);48});4950suiteTeardown(() => sinon.restore());5152test('write extensions located in the same extensions folder', async () => {53const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));5455const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');56const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));57await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);5859const actual = await testObject.scanProfileExtensions(extensionsManifest);60assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);61});6263test('write extensions located in the different folder', async () => {64const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));6566const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');67const extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));68await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);6970const actual = await testObject.scanProfileExtensions(extensionsManifest);71assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);72});7374test('write extensions located in the same extensions folder has relative location ', async () => {75const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));7677const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');78const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));79await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);8081const actual = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());82assert.deepStrictEqual(actual, [{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }]);83});8485test('write extensions located in different extensions folder does not has relative location ', async () => {86const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));8788const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');89const extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));90await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);9192const actual = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());93assert.deepStrictEqual(actual, [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version }]);94});9596test('extension in old format is read and migrated', async () => {97const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');98const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));99await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{100identifier: extension.identifier,101location: extension.location.toJSON(),102version: extension.manifest.version,103}])));104105const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));106107const actual = await testObject.scanProfileExtensions(extensionsManifest);108assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);109110const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());111assert.deepStrictEqual(manifestContent, [{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }]);112});113114test('extension in old format is not migrated if not exists in same location', async () => {115const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');116const extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));117await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{118identifier: extension.identifier,119location: extension.location.toJSON(),120version: extension.manifest.version,121}])));122123const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));124125const actual = await testObject.scanProfileExtensions(extensionsManifest);126assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);127128const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());129assert.deepStrictEqual(manifestContent, [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version }]);130});131132test('extension in old format is read and migrated during write', async () => {133const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');134const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));135await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{136identifier: extension.identifier,137location: extension.location.toJSON(),138version: extension.manifest.version,139}])));140141const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));142const extension2 = aExtension('pub.b', joinPath(extensionsLocation, 'pub.b-1.0.0'));143await testObject.addExtensionsToProfile([[extension2, undefined]], extensionsManifest);144145const actual = await testObject.scanProfileExtensions(extensionsManifest);146assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [147{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined },148{ identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: undefined }149]);150151const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());152assert.deepStrictEqual(manifestContent, [153{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version },154{ identifier: extension2.identifier, location: extension2.location.toJSON(), relativeLocation: 'pub.b-1.0.0', version: extension2.manifest.version }155]);156});157158test('extensions in old format and new format is read and migrated', async () => {159const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');160const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));161const extension2 = aExtension('pub.b', joinPath(extensionsLocation, 'pub.b-1.0.0'));162await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{163identifier: extension.identifier,164location: extension.location.toJSON(),165version: extension.manifest.version,166}, {167identifier: extension2.identifier,168location: extension2.location.toJSON(),169relativeLocation: 'pub.b-1.0.0',170version: extension2.manifest.version,171}])));172173const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));174175const actual = await testObject.scanProfileExtensions(extensionsManifest);176assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [177{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined },178{ identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: undefined }179]);180181const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());182assert.deepStrictEqual(manifestContent, [183{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version },184{ identifier: extension2.identifier, location: extension2.location.toJSON(), relativeLocation: 'pub.b-1.0.0', version: extension2.manifest.version }185]);186});187188test('throws error if extension has invalid relativePath', async () => {189const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');190const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));191await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{192identifier: extension.identifier,193location: extension.location.toJSON(),194version: extension.manifest.version,195relativePath: 2196}])));197198const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));199200try {201await testObject.scanProfileExtensions(extensionsManifest);202assert.fail('Should throw error');203} catch (error) { /*expected*/ }204});205206test('throws error if extension has no location', async () => {207const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');208const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));209await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{210identifier: extension.identifier,211version: extension.manifest.version,212relativePath: 'pub.a-1.0.0'213}])));214215const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));216217try {218await testObject.scanProfileExtensions(extensionsManifest);219assert.fail('Should throw error');220} catch (error) { /*expected*/ }221});222223test('throws error if extension location is invalid', async () => {224const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');225const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));226await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{227identifier: extension.identifier,228location: {},229version: extension.manifest.version,230relativePath: 'pub.a-1.0.0'231}])));232233const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));234235try {236await testObject.scanProfileExtensions(extensionsManifest);237assert.fail('Should throw error');238} catch (error) { /*expected*/ }239});240241test('throws error if extension has no identifier', async () => {242const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');243const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));244await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{245location: extension.location.toJSON(),246version: extension.manifest.version,247}])));248249const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));250251try {252await testObject.scanProfileExtensions(extensionsManifest);253assert.fail('Should throw error');254} catch (error) { /*expected*/ }255});256257test('throws error if extension identifier is invalid', async () => {258const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');259const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));260await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{261identifier: 'pub.a',262location: extension.location.toJSON(),263version: extension.manifest.version,264}])));265266const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));267268try {269await testObject.scanProfileExtensions(extensionsManifest);270assert.fail('Should throw error');271} catch (error) { /*expected*/ }272});273274test('throws error if extension has no version', async () => {275const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');276const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));277await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{278identifier: extension.identifier,279location: extension.location.toJSON(),280}])));281282const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));283284try {285await testObject.scanProfileExtensions(extensionsManifest);286assert.fail('Should throw error');287} catch (error) { /*expected*/ }288});289290test('read extension when manifest is empty', async () => {291const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');292await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(''));293294const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));295const actual = await testObject.scanProfileExtensions(extensionsManifest);296assert.deepStrictEqual(actual, []);297});298299test('read extension when manifest has empty lines and spaces', async () => {300const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');301await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(`302303304`));305const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));306const actual = await testObject.scanProfileExtensions(extensionsManifest);307assert.deepStrictEqual(actual, []);308});309310test('read extension when the relative location is empty', async () => {311const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');312const extension = aExtension('pub.a', joinPath(extensionsLocation, 'pub.a-1.0.0'));313await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{314identifier: extension.identifier,315location: extension.location.toJSON(),316relativeLocation: '',317version: extension.manifest.version,318}])));319320const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));321322const actual = await testObject.scanProfileExtensions(extensionsManifest);323assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);324325const manifestContent = JSON.parse((await instantiationService.get(IFileService).readFile(extensionsManifest)).value.toString());326assert.deepStrictEqual(manifestContent, [{ identifier: extension.identifier, location: extension.location.toJSON(), relativeLocation: 'pub.a-1.0.0', version: extension.manifest.version }]);327});328329test('add extension trigger events', async () => {330const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));331const target1 = sinon.stub();332const target2 = sinon.stub();333disposables.add(testObject.onAddExtensions(target1));334disposables.add(testObject.onDidAddExtensions(target2));335336const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');337const extension = aExtension('pub.a', joinPath(ROOT, 'foo', 'pub.a-1.0.0'));338await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);339340const actual = await testObject.scanProfileExtensions(extensionsManifest);341assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);342343assert.ok(target1.calledOnce);344assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString());345assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions.length, 1);346assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].identifier, extension.identifier);347assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].version, extension.manifest.version);348assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].location.toString(), extension.location.toString());349350assert.ok(target2.calledOnce);351assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString());352assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions.length, 1);353assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].identifier, extension.identifier);354assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].version, extension.manifest.version);355assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].location.toString(), extension.location.toString());356});357358test('remove extensions trigger events', async () => {359const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));360const target1 = sinon.stub();361const target2 = sinon.stub();362disposables.add(testObject.onRemoveExtensions(target1));363disposables.add(testObject.onDidRemoveExtensions(target2));364365const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');366const extension1 = aExtension('pub.a', joinPath(ROOT, 'foo', 'pub.a-1.0.0'));367const extension2 = aExtension('pub.b', joinPath(ROOT, 'foo', 'pub.b-1.0.0'));368await testObject.addExtensionsToProfile([[extension1, undefined], [extension2, undefined]], extensionsManifest);369await testObject.removeExtensionsFromProfile([extension1.identifier, extension2.identifier], extensionsManifest);370371const actual = await testObject.scanProfileExtensions(extensionsManifest);372assert.deepStrictEqual(actual.length, 0);373374assert.ok(target1.calledOnce);375assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString());376assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions.length, 2);377assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].identifier, extension1.identifier);378assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].version, extension1.manifest.version);379assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].location.toString(), extension1.location.toString());380assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[1].identifier, extension2.identifier);381assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[1].version, extension2.manifest.version);382assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[1].location.toString(), extension2.location.toString());383384assert.ok(target2.calledOnce);385assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString());386assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions.length, 2);387assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].identifier, extension1.identifier);388assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].version, extension1.manifest.version);389assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString());390assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[1].identifier, extension2.identifier);391assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[1].version, extension2.manifest.version);392assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[1].location.toString(), extension2.location.toString());393});394395test('add extension with same id but different version', async () => {396const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));397398const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');399400const extension1 = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));401await testObject.addExtensionsToProfile([[extension1, undefined]], extensionsManifest);402403const target1 = sinon.stub();404const target2 = sinon.stub();405const target3 = sinon.stub();406const target4 = sinon.stub();407disposables.add(testObject.onAddExtensions(target1));408disposables.add(testObject.onRemoveExtensions(target2));409disposables.add(testObject.onDidAddExtensions(target3));410disposables.add(testObject.onDidRemoveExtensions(target4));411const extension2 = aExtension('pub.a', joinPath(ROOT, 'pub.a-2.0.0'), undefined, { version: '2.0.0' });412await testObject.addExtensionsToProfile([[extension2, undefined]], extensionsManifest);413414const actual = await testObject.scanProfileExtensions(extensionsManifest);415assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: undefined }]);416417assert.ok(target1.calledOnce);418assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString());419assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions.length, 1);420assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].identifier, extension2.identifier);421assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].version, extension2.manifest.version);422assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString());423424assert.ok(target2.calledOnce);425assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString());426assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions.length, 1);427assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].identifier, extension1.identifier);428assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].version, extension1.manifest.version);429assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString());430431assert.ok(target3.calledOnce);432assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString());433assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions.length, 1);434assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].identifier, extension2.identifier);435assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].version, extension2.manifest.version);436assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString());437438assert.ok(target4.calledOnce);439assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString());440assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions.length, 1);441assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].identifier, extension1.identifier);442assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].version, extension1.manifest.version);443assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString());444});445446test('add same extension', async () => {447const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));448449const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');450451const extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));452await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);453454const target1 = sinon.stub();455const target2 = sinon.stub();456const target3 = sinon.stub();457const target4 = sinon.stub();458disposables.add(testObject.onAddExtensions(target1));459disposables.add(testObject.onRemoveExtensions(target2));460disposables.add(testObject.onDidAddExtensions(target3));461disposables.add(testObject.onDidRemoveExtensions(target4));462await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);463464const actual = await testObject.scanProfileExtensions(extensionsManifest);465assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);466assert.ok(target1.notCalled);467assert.ok(target2.notCalled);468assert.ok(target3.notCalled);469assert.ok(target4.notCalled);470});471472test('add same extension with different metadata', async () => {473const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));474475const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');476477const extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));478await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);479480const target1 = sinon.stub();481const target2 = sinon.stub();482const target3 = sinon.stub();483const target4 = sinon.stub();484disposables.add(testObject.onAddExtensions(target1));485disposables.add(testObject.onRemoveExtensions(target2));486disposables.add(testObject.onDidAddExtensions(target3));487disposables.add(testObject.onDidRemoveExtensions(target4));488await testObject.addExtensionsToProfile([[extension, { isApplicationScoped: true }]], extensionsManifest);489490const actual = await testObject.scanProfileExtensions(extensionsManifest);491assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON(), metadata: a.metadata })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: { isApplicationScoped: true } }]);492assert.ok(target1.notCalled);493assert.ok(target2.notCalled);494assert.ok(target3.notCalled);495assert.ok(target4.notCalled);496});497498test('add extension with different version and metadata', async () => {499const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));500501const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');502503const extension1 = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));504await testObject.addExtensionsToProfile([[extension1, undefined]], extensionsManifest);505const extension2 = aExtension('pub.a', joinPath(ROOT, 'pub.a-2.0.0'), undefined, { version: '2.0.0' });506507const target1 = sinon.stub();508const target2 = sinon.stub();509const target3 = sinon.stub();510const target4 = sinon.stub();511disposables.add(testObject.onAddExtensions(target1));512disposables.add(testObject.onRemoveExtensions(target2));513disposables.add(testObject.onDidAddExtensions(target3));514disposables.add(testObject.onDidRemoveExtensions(target4));515await testObject.addExtensionsToProfile([[extension2, { isApplicationScoped: true }]], extensionsManifest);516517const actual = await testObject.scanProfileExtensions(extensionsManifest);518assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON(), metadata: a.metadata })), [{ identifier: extension2.identifier, location: extension2.location.toJSON(), version: extension2.manifest.version, metadata: { isApplicationScoped: true } }]);519520assert.ok(target1.calledOnce);521assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString());522assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions.length, 1);523assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].identifier, extension2.identifier);524assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].version, extension2.manifest.version);525assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString());526527assert.ok(target2.calledOnce);528assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString());529assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions.length, 1);530assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].identifier, extension1.identifier);531assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].version, extension1.manifest.version);532assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString());533534assert.ok(target3.calledOnce);535assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).profileLocation.toString(), extensionsManifest.toString());536assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions.length, 1);537assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].identifier, extension2.identifier);538assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].version, extension2.manifest.version);539assert.deepStrictEqual((<ProfileExtensionsEvent>(target1.args[0][0])).extensions[0].location.toString(), extension2.location.toString());540541assert.ok(target4.calledOnce);542assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).profileLocation.toString(), extensionsManifest.toString());543assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions.length, 1);544assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].identifier, extension1.identifier);545assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].version, extension1.manifest.version);546assert.deepStrictEqual((<ProfileExtensionsEvent>(target2.args[0][0])).extensions[0].location.toString(), extension1.location.toString());547});548549test('add extension with same id and version located in the different folder', async () => {550const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));551552const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');553554let extension = aExtension('pub.a', joinPath(ROOT, 'foo', 'pub.a-1.0.0'));555await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);556557const target1 = sinon.stub();558const target2 = sinon.stub();559const target3 = sinon.stub();560const target4 = sinon.stub();561disposables.add(testObject.onAddExtensions(target1));562disposables.add(testObject.onRemoveExtensions(target2));563disposables.add(testObject.onDidAddExtensions(target3));564disposables.add(testObject.onDidRemoveExtensions(target4));565extension = aExtension('pub.a', joinPath(ROOT, 'pub.a-1.0.0'));566await testObject.addExtensionsToProfile([[extension, undefined]], extensionsManifest);567568const actual = await testObject.scanProfileExtensions(extensionsManifest);569assert.deepStrictEqual(actual.map(a => ({ ...a, location: a.location.toJSON() })), [{ identifier: extension.identifier, location: extension.location.toJSON(), version: extension.manifest.version, metadata: undefined }]);570assert.ok(target1.notCalled);571assert.ok(target2.notCalled);572assert.ok(target3.notCalled);573assert.ok(target4.notCalled);574});575576test('read extension when uuid is different in identifier and manifest', async () => {577const extensionsManifest = joinPath(extensionsLocation, 'extensions.json');578await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{579identifier: {580id: 'pub.a',581uuid: 'uuid1`'582},583version: '1.0.0',584location: joinPath(extensionsLocation, 'pub.a-1.0.0').toString(),585relativeLocation: 'pub.a-1.0.0',586metadata: {587id: 'uuid',588}589}])));590591const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation));592const actual = await testObject.scanProfileExtensions(extensionsManifest);593assert.deepStrictEqual(actual.length, 1);594assert.deepStrictEqual(actual[0].identifier.id, 'pub.a');595assert.deepStrictEqual(actual[0].identifier.uuid, 'uuid');596});597598function aExtension(id: string, location: URI, e?: Partial<IExtension>, manifest?: Partial<IExtensionManifest>): IExtension {599return {600identifier: { id },601location,602type: ExtensionType.User,603targetPlatform: TargetPlatform.DARWIN_X64,604isBuiltin: false,605manifest: {606name: 'name',607publisher: 'publisher',608version: '1.0.0',609engines: { vscode: '1.0.0' },610...manifest,611},612isValid: true,613preRelease: false,614validations: [],615...e616};617}618619});620621622