Path: blob/main/src/vs/workbench/services/label/test/browser/label.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 * as resources from '../../../../../base/common/resources.js';6import assert from 'assert';7import { TestEnvironmentService, TestLifecycleService, TestPathService, TestRemoteAgentService } from '../../../../test/browser/workbenchTestServices.js';8import { URI } from '../../../../../base/common/uri.js';9import { LabelService } from '../../common/labelService.js';10import { TestContextService, TestStorageService } from '../../../../test/common/workbenchTestServices.js';11import { WorkspaceFolder } from '../../../../../platform/workspace/common/workspace.js';12import { TestWorkspace, Workspace } from '../../../../../platform/workspace/test/common/testWorkspace.js';13import { isWindows } from '../../../../../base/common/platform.js';14import { StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';15import { Memento } from '../../../../common/memento.js';16import { ResourceLabelFormatter } from '../../../../../platform/label/common/label.js';17import { sep } from '../../../../../base/common/path.js';18import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';19import { DisposableStore } from '../../../../../base/common/lifecycle.js';2021suite('URI Label', () => {22let labelService: LabelService;23let storageService: TestStorageService;2425setup(() => {26storageService = new TestStorageService();27labelService = new LabelService(TestEnvironmentService, new TestContextService(), new TestPathService(URI.file('/foobar')), new TestRemoteAgentService(), storageService, new TestLifecycleService());28});2930ensureNoDisposablesAreLeakedInTestSuite();3132test('custom scheme', function () {33labelService.registerFormatter({34scheme: 'vscode',35formatting: {36label: 'LABEL/${path}/${authority}/END',37separator: '/',38tildify: true,39normalizeDriveLetter: true40}41});4243const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5');44assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'LABEL//1/2/3/4/5/microsoft.com/END');45assert.strictEqual(labelService.getUriBasenameLabel(uri1), 'END');46});4748test('file scheme', function () {49labelService.registerFormatter({50scheme: 'file',51formatting: {52label: '${path}',53separator: sep,54tildify: !isWindows,55normalizeDriveLetter: isWindows56}57});5859const uri1 = TestWorkspace.folders[0].uri.with({ path: TestWorkspace.folders[0].uri.path.concat('/a/b/c/d') });60assert.strictEqual(labelService.getUriLabel(uri1, { relative: true }), isWindows ? 'a\\b\\c\\d' : 'a/b/c/d');61assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), isWindows ? 'C:\\testWorkspace\\a\\b\\c\\d' : '/testWorkspace/a/b/c/d');62assert.strictEqual(labelService.getUriBasenameLabel(uri1), 'd');6364const uri2 = URI.file('c:\\1/2/3');65assert.strictEqual(labelService.getUriLabel(uri2, { relative: false }), isWindows ? 'C:\\1\\2\\3' : '/c:\\1/2/3');66assert.strictEqual(labelService.getUriBasenameLabel(uri2), '3');67});6869test('separator', function () {70labelService.registerFormatter({71scheme: 'vscode',72formatting: {73label: 'LABEL\\${path}\\${authority}\\END',74separator: '\\',75tildify: true,76normalizeDriveLetter: true77}78});7980const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5');81assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'LABEL\\\\1\\2\\3\\4\\5\\microsoft.com\\END');82assert.strictEqual(labelService.getUriBasenameLabel(uri1), 'END');83});8485test('custom authority', function () {86labelService.registerFormatter({87scheme: 'vscode',88authority: 'micro*',89formatting: {90label: 'LABEL/${path}/${authority}/END',91separator: '/'92}93});9495const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5');96assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'LABEL//1/2/3/4/5/microsoft.com/END');97assert.strictEqual(labelService.getUriBasenameLabel(uri1), 'END');98});99100test('mulitple authority', function () {101labelService.registerFormatter({102scheme: 'vscode',103authority: 'not_matching_but_long',104formatting: {105label: 'first',106separator: '/'107}108});109labelService.registerFormatter({110scheme: 'vscode',111authority: 'microsof*',112formatting: {113label: 'second',114separator: '/'115}116});117labelService.registerFormatter({118scheme: 'vscode',119authority: 'mi*',120formatting: {121label: 'third',122separator: '/'123}124});125126// Make sure the most specific authority is picked127const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5');128assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'second');129assert.strictEqual(labelService.getUriBasenameLabel(uri1), 'second');130});131132test('custom query', function () {133labelService.registerFormatter({134scheme: 'vscode',135formatting: {136label: 'LABEL${query.prefix}: ${query.path}/END',137separator: '/',138tildify: true,139normalizeDriveLetter: true140}141});142143const uri1 = URI.parse(`vscode://microsoft.com/1/2/3/4/5?${encodeURIComponent(JSON.stringify({ prefix: 'prefix', path: 'path' }))}`);144assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'LABELprefix: path/END');145});146147test('custom query without value', function () {148labelService.registerFormatter({149scheme: 'vscode',150formatting: {151label: 'LABEL${query.prefix}: ${query.path}/END',152separator: '/',153tildify: true,154normalizeDriveLetter: true155}156});157158const uri1 = URI.parse(`vscode://microsoft.com/1/2/3/4/5?${encodeURIComponent(JSON.stringify({ path: 'path' }))}`);159assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'LABEL: path/END');160});161162test('custom query without query json', function () {163labelService.registerFormatter({164scheme: 'vscode',165formatting: {166label: 'LABEL${query.prefix}: ${query.path}/END',167separator: '/',168tildify: true,169normalizeDriveLetter: true170}171});172173const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5?path=foo');174assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'LABEL: /END');175});176177test('custom query without query', function () {178labelService.registerFormatter({179scheme: 'vscode',180formatting: {181label: 'LABEL${query.prefix}: ${query.path}/END',182separator: '/',183tildify: true,184normalizeDriveLetter: true185}186});187188const uri1 = URI.parse('vscode://microsoft.com/1/2/3/4/5');189assert.strictEqual(labelService.getUriLabel(uri1, { relative: false }), 'LABEL: /END');190});191192193test('label caching', () => {194const m = new Memento('cachedResourceLabelFormatters2', storageService).getMemento(StorageScope.PROFILE, StorageTarget.MACHINE);195const makeFormatter = (scheme: string): ResourceLabelFormatter => ({ formatting: { label: `\${path} (${scheme})`, separator: '/' }, scheme });196assert.deepStrictEqual(m, {});197198// registers a new formatter:199labelService.registerCachedFormatter(makeFormatter('a'));200assert.deepStrictEqual(m, { formatters: [makeFormatter('a')] });201202// registers a 2nd formatter:203labelService.registerCachedFormatter(makeFormatter('b'));204assert.deepStrictEqual(m, { formatters: [makeFormatter('b'), makeFormatter('a')] });205206// promotes a formatter on re-register:207labelService.registerCachedFormatter(makeFormatter('a'));208assert.deepStrictEqual(m, { formatters: [makeFormatter('a'), makeFormatter('b')] });209210// no-ops if already in first place:211labelService.registerCachedFormatter(makeFormatter('a'));212assert.deepStrictEqual(m, { formatters: [makeFormatter('a'), makeFormatter('b')] });213214// limits the cache:215for (let i = 0; i < 100; i++) {216labelService.registerCachedFormatter(makeFormatter(`i${i}`));217}218const expected: ResourceLabelFormatter[] = [];219for (let i = 50; i < 100; i++) {220expected.unshift(makeFormatter(`i${i}`));221}222assert.deepStrictEqual(m, { formatters: expected });223224delete (m as any).formatters;225});226});227228229suite('multi-root workspace', () => {230let labelService: LabelService;231const disposables = new DisposableStore();232233setup(() => {234const sources = URI.file('folder1/src');235const tests = URI.file('folder1/test');236const other = URI.file('folder2');237238labelService = disposables.add(new LabelService(239TestEnvironmentService,240new TestContextService(241new Workspace('test-workspace', [242new WorkspaceFolder({ uri: sources, index: 0, name: 'Sources' }),243new WorkspaceFolder({ uri: tests, index: 1, name: 'Tests' }),244new WorkspaceFolder({ uri: other, index: 2, name: resources.basename(other) }),245])),246new TestPathService(),247new TestRemoteAgentService(),248disposables.add(new TestStorageService()),249disposables.add(new TestLifecycleService())250));251});252253teardown(() => {254disposables.clear();255});256257test('labels of files in multiroot workspaces are the foldername followed by offset from the folder', () => {258labelService.registerFormatter({259scheme: 'file',260formatting: {261label: '${authority}${path}',262separator: '/',263tildify: false,264normalizeDriveLetter: false,265authorityPrefix: '//',266workspaceSuffix: ''267}268});269270const tests = {271'folder1/src/file': 'Sources • file',272'folder1/src/folder/file': 'Sources • folder/file',273'folder1/src': 'Sources',274'folder1/other': '/folder1/other',275'folder2/other': 'folder2 • other',276};277278Object.entries(tests).forEach(([path, label]) => {279const generated = labelService.getUriLabel(URI.file(path), { relative: true });280assert.strictEqual(generated, label);281});282});283284test('labels with context after path', () => {285labelService.registerFormatter({286scheme: 'file',287formatting: {288label: '${path} (${scheme})',289separator: '/',290}291});292293const tests = {294'folder1/src/file': 'Sources • file (file)',295'folder1/src/folder/file': 'Sources • folder/file (file)',296'folder1/src': 'Sources',297'folder1/other': '/folder1/other (file)',298'folder2/other': 'folder2 • other (file)',299};300301Object.entries(tests).forEach(([path, label]) => {302const generated = labelService.getUriLabel(URI.file(path), { relative: true });303assert.strictEqual(generated, label, path);304});305});306307test('stripPathStartingSeparator', () => {308labelService.registerFormatter({309scheme: 'file',310formatting: {311label: '${path}',312separator: '/',313stripPathStartingSeparator: true314}315});316317const tests = {318'folder1/src/file': 'Sources • file',319'other/blah': 'other/blah',320};321322Object.entries(tests).forEach(([path, label]) => {323const generated = labelService.getUriLabel(URI.file(path), { relative: true });324assert.strictEqual(generated, label, path);325});326});327328test('relative label without formatter', () => {329const rootFolder = URI.parse('myscheme://myauthority/');330331labelService = disposables.add(new LabelService(332TestEnvironmentService,333new TestContextService(334new Workspace('test-workspace', [335new WorkspaceFolder({ uri: rootFolder, index: 0, name: 'FSProotFolder' }),336])),337new TestPathService(undefined, rootFolder.scheme),338new TestRemoteAgentService(),339disposables.add(new TestStorageService()),340disposables.add(new TestLifecycleService())341));342343const generated = labelService.getUriLabel(URI.parse('myscheme://myauthority/some/folder/test.txt'), { relative: true });344if (isWindows) {345assert.strictEqual(generated, 'some\\folder\\test.txt');346} else {347assert.strictEqual(generated, 'some/folder/test.txt');348}349});350351ensureNoDisposablesAreLeakedInTestSuite();352});353354suite('workspace at FSP root', () => {355let labelService: LabelService;356357setup(() => {358const rootFolder = URI.parse('myscheme://myauthority/');359360labelService = new LabelService(361TestEnvironmentService,362new TestContextService(363new Workspace('test-workspace', [364new WorkspaceFolder({ uri: rootFolder, index: 0, name: 'FSProotFolder' }),365])),366new TestPathService(),367new TestRemoteAgentService(),368new TestStorageService(),369new TestLifecycleService()370);371labelService.registerFormatter({372scheme: 'myscheme',373formatting: {374label: '${scheme}://${authority}${path}',375separator: '/',376tildify: false,377normalizeDriveLetter: false,378workspaceSuffix: '',379authorityPrefix: '',380stripPathStartingSeparator: false381}382});383});384385test('non-relative label', () => {386387const tests = {388'myscheme://myauthority/myFile1.txt': 'myscheme://myauthority/myFile1.txt',389'myscheme://myauthority/folder/myFile2.txt': 'myscheme://myauthority/folder/myFile2.txt',390};391392Object.entries(tests).forEach(([uriString, label]) => {393const generated = labelService.getUriLabel(URI.parse(uriString), { relative: false });394assert.strictEqual(generated, label);395});396});397398test('relative label', () => {399400const tests = {401'myscheme://myauthority/myFile1.txt': 'myFile1.txt',402'myscheme://myauthority/folder/myFile2.txt': 'folder/myFile2.txt',403};404405Object.entries(tests).forEach(([uriString, label]) => {406const generated = labelService.getUriLabel(URI.parse(uriString), { relative: true });407assert.strictEqual(generated, label);408});409});410411test('relative label with explicit path separator', () => {412let generated = labelService.getUriLabel(URI.parse('myscheme://myauthority/some/folder/test.txt'), { relative: true, separator: '/' });413assert.strictEqual(generated, 'some/folder/test.txt');414415generated = labelService.getUriLabel(URI.parse('myscheme://myauthority/some/folder/test.txt'), { relative: true, separator: '\\' });416assert.strictEqual(generated, 'some\\folder\\test.txt');417});418419ensureNoDisposablesAreLeakedInTestSuite();420});421422423