Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
/*---------------------------------------------------------------------------------------------
* 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 { ActionBar, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action, Separator } from 'vs/base/common/actions';
suite('Actionbar', () => {
test('prepareActions()', function () {
let a1 = new Separator();
let a2 = new Separator();
let a3 = new Action('a3');
let a4 = new Separator();
let a5 = new Separator();
let a6 = new Action('a6');
let a7 = new Separator();
let actions = prepareActions([a1, a2, a3, a4, a5, a6, a7]);
assert.strictEqual(actions.length, 3); // duplicate separators get removed
assert(actions[0] === a3);
assert(actions[1] === a5);
assert(actions[2] === a6);
});
test('hasAction()', function () {
const container = document.createElement('div');
const actionbar = new ActionBar(container);
let a1 = new Action('a1');
let a2 = new Action('a2');
actionbar.push(a1);
assert.equal(actionbar.hasAction(a1), true);
assert.equal(actionbar.hasAction(a2), false);
actionbar.pull(0);
assert.equal(actionbar.hasAction(a1), false);
actionbar.push(a1, { index: 1 });
actionbar.push(a2, { index: 0 });
assert.equal(actionbar.hasAction(a1), true);
assert.equal(actionbar.hasAction(a2), true);
actionbar.pull(0);
assert.equal(actionbar.hasAction(a1), true);
assert.equal(actionbar.hasAction(a2), false);
actionbar.pull(0);
assert.equal(actionbar.hasAction(a1), false);
assert.equal(actionbar.hasAction(a2), false);
actionbar.push(a1);
assert.equal(actionbar.hasAction(a1), true);
actionbar.clear();
assert.equal(actionbar.hasAction(a1), false);
});
});

View File

@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* 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 { isMacintosh, isWindows } from 'vs/base/common/platform';
suite('Browsers', () => {
test('all', () => {
assert(!(isWindows && isMacintosh));
});
});

View File

@@ -0,0 +1,52 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { renderCodicons } from 'vs/base/browser/codicons';
import * as assert from 'assert';
suite('renderCodicons', () => {
test('no codicons', () => {
const result = renderCodicons(' hello World .');
assert.equal(elementsToString(result), ' hello World .');
});
test('codicon only', () => {
const result = renderCodicons('$(alert)');
assert.equal(elementsToString(result), '<span class="codicon codicon-alert"></span>');
});
test('codicon and non-codicon strings', () => {
const result = renderCodicons(` $(alert) Unresponsive`);
assert.equal(elementsToString(result), ' <span class="codicon codicon-alert"></span> Unresponsive');
});
test('multiple codicons', () => {
const result = renderCodicons('$(check)$(error)');
assert.equal(elementsToString(result), '<span class="codicon codicon-check"></span><span class="codicon codicon-error"></span>');
});
test('escaped codicon', () => {
const result = renderCodicons('\\$(escaped)');
assert.equal(elementsToString(result), '$(escaped)');
});
test('codicon with animation', () => {
const result = renderCodicons('$(zip~anim)');
assert.equal(elementsToString(result), '<span class="codicon codicon-zip codicon-animation-anim"></span>');
});
const elementsToString = (elements: Array<HTMLElement | string>): string => {
return elements
.map(elem => elem instanceof HTMLElement ? elem.outerHTML : elem)
.reduce((a, b) => a + b, '');
};
});

View File

@@ -0,0 +1,285 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { compareFileNames, compareFileExtensions, compareFileNamesDefault, compareFileExtensionsDefault } from 'vs/base/common/comparers';
import * as assert from 'assert';
const compareLocale = (a: string, b: string) => a.localeCompare(b);
const compareLocaleNumeric = (a: string, b: string) => a.localeCompare(b, undefined, { numeric: true });
suite('Comparers', () => {
test('compareFileNames', () => {
//
// Comparisons with the same results as compareFileNamesDefault
//
// name-only comparisons
assert(compareFileNames(null, null) === 0, 'null should be equal');
assert(compareFileNames(null, 'abc') < 0, 'null should be come before real values');
assert(compareFileNames('', '') === 0, 'empty should be equal');
assert(compareFileNames('abc', 'abc') === 0, 'equal names should be equal');
assert(compareFileNames('z', 'A') > 0, 'z comes is after A regardless of case');
assert(compareFileNames('Z', 'a') > 0, 'Z comes after a regardless of case');
// name plus extension comparisons
assert(compareFileNames('bbb.aaa', 'aaa.bbb') > 0, 'files with extensions are compared first by filename');
assert(compareFileNames('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole name all at once by locale');
// dotfile comparisons
assert(compareFileNames('.abc', '.abc') === 0, 'equal dotfile names should be equal');
assert(compareFileNames('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly');
assert(compareFileNames('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots');
assert(compareFileNames('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first');
assert(compareFileNames('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot');
// dotfile vs non-dotfile comparisons
assert(compareFileNames(null, '.abc') < 0, 'null should come before dotfiles');
assert(compareFileNames('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions');
assert(compareFileNames('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions');
assert(compareFileNames('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files');
assert(compareFileNames('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files');
// numeric comparisons
assert(compareFileNames('1', '1') === 0, 'numerically equal full names should be equal');
assert(compareFileNames('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
assert(compareFileNames('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
assert(compareFileNames('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long');
assert(compareFileNames('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
assert(compareFileNames('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
//
// Comparisons with different results than compareFileNamesDefault
//
// name-only comparisons
assert(compareFileNames('a', 'A') !== compareLocale('a', 'A'), 'the same letter does not sort by locale');
assert(compareFileNames('â', 'Â') !== compareLocale('â', 'Â'), 'the same accented letter does not sort by locale');
assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNames), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order');
assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNames), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents do not sort in locale order');
// numeric comparisons
assert(compareFileNames('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order');
assert(compareFileNames('abc.txt1', 'abc.txt01') > 0, 'same name plus extensions with equal numbers sort in unicode order');
assert(compareFileNames('art01', 'Art01') !== 'art01'.localeCompare('Art01', undefined, { numeric: true }),
'a numerically equivalent word of a different case does not compare numerically based on locale');
});
test('compareFileExtensions', () => {
//
// Comparisons with the same results as compareFileExtensionsDefault
//
// name-only comparisons
assert(compareFileExtensions(null, null) === 0, 'null should be equal');
assert(compareFileExtensions(null, 'abc') < 0, 'null should come before real files without extension');
assert(compareFileExtensions('', '') === 0, 'empty should be equal');
assert(compareFileExtensions('abc', 'abc') === 0, 'equal names should be equal');
assert(compareFileExtensions('z', 'A') > 0, 'z comes after A');
assert(compareFileExtensions('Z', 'a') > 0, 'Z comes after a');
// name plus extension comparisons
assert(compareFileExtensions('file.ext', 'file.ext') === 0, 'equal full names should be equal');
assert(compareFileExtensions('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared');
assert(compareFileExtensions('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions');
assert(compareFileExtensions('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extensions even if filenames compare differently');
assert(compareFileExtensions('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names');
assert(compareFileExtensions('agg.go', 'agg_repo.go') < 0, 'shorter names short before longer names even when the longer name contains an underscore');
assert(compareFileExtensions('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name');
// dotfile comparisons
assert(compareFileExtensions('.abc', '.abc') === 0, 'equal dotfiles should be equal');
assert(compareFileExtensions('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case');
// dotfile vs non-dotfile comparisons
assert(compareFileExtensions(null, '.abc') < 0, 'null should come before dotfiles');
assert(compareFileExtensions('.env', 'aaa.env') < 0, 'if equal extensions, filenames should be compared, empty filename should come before others');
assert(compareFileExtensions('.MD', 'a.md') < 0, 'if extensions differ in case, files sort by extension in unicode order');
// numeric comparisons
assert(compareFileExtensions('1', '1') === 0, 'numerically equal full names should be equal');
assert(compareFileExtensions('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
assert(compareFileExtensions('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensions('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long');
assert(compareFileExtensions('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
assert(compareFileExtensions('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
assert(compareFileExtensions('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensions('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal');
assert(compareFileExtensions('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensions('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long');
assert(compareFileExtensions('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared');
assert(compareFileExtensions('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically');
//
// Comparisons with different results from compareFileExtensionsDefault
//
// name-only comparisions
assert(compareFileExtensions('a', 'A') !== compareLocale('a', 'A'), 'the same letter of different case does not sort by locale');
assert(compareFileExtensions('â', 'Â') !== compareLocale('â', 'Â'), 'the same accented letter of different case does not sort by locale');
assert.notDeepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensions), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases do not sort in locale order');
assert.notDeepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensions), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents do not sort in locale order');
// name plus extension comparisons
assert(compareFileExtensions('a.MD', 'a.md') !== compareLocale('MD', 'md'), 'case differences in extensions do not sort by locale');
assert(compareFileExtensions('a.md', 'A.md') !== compareLocale('a', 'A'), 'case differences in names do not sort by locale');
assert(compareFileExtensions('aggregate.go', 'aggregate_repo.go') < 0, 'when extensions are equal, names sort in dictionary order');
// dotfile comparisons
assert(compareFileExtensions('.env', '.aaa.env') < 0, 'a dotfile with an extension is treated as a name plus an extension - equal extensions');
assert(compareFileExtensions('.env', '.env.aaa') > 0, 'a dotfile with an extension is treated as a name plus an extension - unequal extensions');
// dotfile vs non-dotfile comparisons
assert(compareFileExtensions('.env', 'aaa') > 0, 'filenames without extensions come before dotfiles');
assert(compareFileExtensions('.md', 'A.MD') > 0, 'a file with an uppercase extension sorts before a dotfile of the same lowercase extension');
// numeric comparisons
assert(compareFileExtensions('abc.txt01', 'abc.txt1') < 0, 'extensions with equal numbers sort in unicode order');
assert(compareFileExtensions('art01', 'Art01') !== compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case does not compare by locale');
assert(compareFileExtensions('abc02.txt', 'abc002.txt') > 0, 'filenames with equivalent numbers and leading zeros sort in unicode order');
assert(compareFileExtensions('txt.abc01', 'txt.abc1') < 0, 'extensions with equivalent numbers sort in unicode order');
});
test('compareFileNamesDefault', () => {
//
// Comparisons with the same results as compareFileNames
//
// name-only comparisons
assert(compareFileNamesDefault(null, null) === 0, 'null should be equal');
assert(compareFileNamesDefault(null, 'abc') < 0, 'null should be come before real values');
assert(compareFileNamesDefault('', '') === 0, 'empty should be equal');
assert(compareFileNamesDefault('abc', 'abc') === 0, 'equal names should be equal');
assert(compareFileNamesDefault('z', 'A') > 0, 'z comes is after A regardless of case');
assert(compareFileNamesDefault('Z', 'a') > 0, 'Z comes after a regardless of case');
// name plus extension comparisons
assert(compareFileNamesDefault('file.ext', 'file.ext') === 0, 'equal full names should be equal');
assert(compareFileNamesDefault('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared');
assert(compareFileNamesDefault('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions');
assert(compareFileNamesDefault('bbb.aaa', 'aaa.bbb') > 0, 'files should be compared by names even if extensions compare differently');
assert(compareFileNamesDefault('aggregate.go', 'aggregate_repo.go') > 0, 'compares the whole filename in locale order');
// dotfile comparisons
assert(compareFileNamesDefault('.abc', '.abc') === 0, 'equal dotfile names should be equal');
assert(compareFileNamesDefault('.env.', '.gitattributes') < 0, 'filenames starting with dots and with extensions should still sort properly');
assert(compareFileNamesDefault('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots');
assert(compareFileNamesDefault('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first');
assert(compareFileNamesDefault('.aaa_env', '.aaa.env') < 0, 'and underscore in a dotfile name will sort before a dot');
// dotfile vs non-dotfile comparisons
assert(compareFileNamesDefault(null, '.abc') < 0, 'null should come before dotfiles');
assert(compareFileNamesDefault('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions');
assert(compareFileNamesDefault('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions');
assert(compareFileNamesDefault('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files');
assert(compareFileNamesDefault('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files');
// numeric comparisons
assert(compareFileNamesDefault('1', '1') === 0, 'numerically equal full names should be equal');
assert(compareFileNamesDefault('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
assert(compareFileNamesDefault('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
assert(compareFileNamesDefault('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order even when they are multiple digits long');
assert(compareFileNamesDefault('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
assert(compareFileNamesDefault('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
//
// Comparisons with different results than compareFileNames
//
// name-only comparisons
assert(compareFileNamesDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter sorts by locale');
assert(compareFileNamesDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter sorts by locale');
assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileNamesDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order');
assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileNamesDefault), ['email', 'Email', 'émail', 'Émail'].sort(compareLocale), 'the same base characters with different case or accents sort in locale order');
// numeric comparisons
assert(compareFileNamesDefault('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest number first');
assert(compareFileNamesDefault('abc.txt1', 'abc.txt01') < 0, 'same name plus extensions with equal numbers sort shortest number first');
assert(compareFileNamesDefault('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale');
});
test('compareFileExtensionsDefault', () => {
//
// Comparisons with the same result as compareFileExtensions
//
// name-only comparisons
assert(compareFileExtensionsDefault(null, null) === 0, 'null should be equal');
assert(compareFileExtensionsDefault(null, 'abc') < 0, 'null should come before real files without extensions');
assert(compareFileExtensionsDefault('', '') === 0, 'empty should be equal');
assert(compareFileExtensionsDefault('abc', 'abc') === 0, 'equal names should be equal');
assert(compareFileExtensionsDefault('z', 'A') > 0, 'z comes after A');
assert(compareFileExtensionsDefault('Z', 'a') > 0, 'Z comes after a');
// name plus extension comparisons
assert(compareFileExtensionsDefault('file.ext', 'file.ext') === 0, 'equal full filenames should be equal');
assert(compareFileExtensionsDefault('a.ext', 'b.ext') < 0, 'if equal extensions, filenames should be compared');
assert(compareFileExtensionsDefault('file.aaa', 'file.bbb') < 0, 'files with equal names should be compared by extensions');
assert(compareFileExtensionsDefault('bbb.aaa', 'aaa.bbb') < 0, 'files should be compared by extension first');
assert(compareFileExtensionsDefault('agg.go', 'aggrepo.go') < 0, 'shorter names sort before longer names');
assert(compareFileExtensionsDefault('a.MD', 'b.md') < 0, 'when extensions are the same except for case, the files sort by name');
// dotfile comparisons
assert(compareFileExtensionsDefault('.abc', '.abc') === 0, 'equal dotfiles should be equal');
assert(compareFileExtensionsDefault('.md', '.Gitattributes') > 0, 'dotfiles sort alphabetically regardless of case');
// dotfile vs non-dotfile comparisons
assert(compareFileExtensionsDefault(null, '.abc') < 0, 'null should come before dotfiles');
assert(compareFileExtensionsDefault('.env', 'aaa.env') < 0, 'dotfiles come before filenames with extensions');
assert(compareFileExtensionsDefault('.MD', 'a.md') < 0, 'dotfiles sort before lowercase files');
// numeric comparisons
assert(compareFileExtensionsDefault('1', '1') === 0, 'numerically equal full names should be equal');
assert(compareFileExtensionsDefault('abc1.txt', 'abc1.txt') === 0, 'equal filenames with numbers should be equal');
assert(compareFileExtensionsDefault('abc1.txt', 'abc2.txt') < 0, 'filenames with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensionsDefault('abc2.txt', 'abc10.txt') < 0, 'filenames with numbers should be in numerical order');
assert(compareFileExtensionsDefault('abc02.txt', 'abc010.txt') < 0, 'filenames with numbers that have leading zeros sort numerically');
assert(compareFileExtensionsDefault('abc1.10.txt', 'abc1.2.txt') > 0, 'numbers with dots between them are treated as two separate numbers, not one decimal number');
assert(compareFileExtensionsDefault('abc2.txt2', 'abc1.txt10') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensionsDefault('txt.abc1', 'txt.abc1') === 0, 'equal extensions with numbers should be equal');
assert(compareFileExtensionsDefault('txt.abc1', 'txt.abc2') < 0, 'extensions with numbers should be in numerical order, not alphabetical order');
assert(compareFileExtensionsDefault('txt.abc2', 'txt.abc10') < 0, 'extensions with numbers should be in numerical order even when they are multiple digits long');
assert(compareFileExtensionsDefault('a.ext1', 'b.ext1') < 0, 'if equal extensions with numbers, filenames should be compared');
assert(compareFileExtensionsDefault('a10.txt', 'A2.txt') > 0, 'filenames with number and case differences compare numerically');
//
// Comparisons with different results than compareFileExtensions
//
// name-only comparisons
assert(compareFileExtensionsDefault('a', 'A') === compareLocale('a', 'A'), 'the same letter of different case sorts by locale');
assert(compareFileExtensionsDefault('â', 'Â') === compareLocale('â', 'Â'), 'the same accented letter of different case sorts by locale');
assert.deepEqual(['artichoke', 'Artichoke', 'art', 'Art'].sort(compareFileExtensionsDefault), ['artichoke', 'Artichoke', 'art', 'Art'].sort(compareLocale), 'words with the same root and different cases sort in locale order');
assert.deepEqual(['email', 'Email', 'émail', 'Émail'].sort(compareFileExtensionsDefault), ['email', 'Email', 'émail', 'Émail'].sort((a, b) => a.localeCompare(b)), 'the same base characters with different case or accents sort in locale order');
// name plus extension comparisons
assert(compareFileExtensionsDefault('a.MD', 'a.md') === compareLocale('MD', 'md'), 'case differences in extensions sort by locale');
assert(compareFileExtensionsDefault('a.md', 'A.md') === compareLocale('a', 'A'), 'case differences in names sort by locale');
assert(compareFileExtensionsDefault('aggregate.go', 'aggregate_repo.go') > 0, 'names with the same extension sort in full filename locale order');
// dotfile comparisons
assert(compareFileExtensionsDefault('.env', '.aaa.env') > 0, 'dotfiles sort alphabetically when they contain multiple dots');
assert(compareFileExtensionsDefault('.env', '.env.aaa') < 0, 'dotfiles with the same root sort shortest first');
// dotfile vs non-dotfile comparisons
assert(compareFileExtensionsDefault('.env', 'aaa') < 0, 'dotfiles come before filenames without extensions');
assert(compareFileExtensionsDefault('.md', 'A.MD') < 0, 'dotfiles sort before uppercase files');
// numeric comparisons
assert(compareFileExtensionsDefault('abc.txt01', 'abc.txt1') > 0, 'extensions with equal numbers should be in shortest-first order');
assert(compareFileExtensionsDefault('art01', 'Art01') === compareLocaleNumeric('art01', 'Art01'), 'a numerically equivalent word of a different case compares numerically based on locale');
assert(compareFileExtensionsDefault('abc02.txt', 'abc002.txt') < 0, 'filenames with equivalent numbers and leading zeros sort shortest string first');
assert(compareFileExtensionsDefault('txt.abc01', 'txt.abc1') > 0, 'extensions with equivalent numbers sort shortest extension first');
});
});

View File

@@ -0,0 +1,131 @@
/*---------------------------------------------------------------------------------------------
* 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 * as dom from 'vs/base/browser/dom';
const $ = dom.$;
suite('dom', () => {
test('hasClass', () => {
let element = document.createElement('div');
element.className = 'foobar boo far';
assert(element.classList.contains('foobar'));
assert(element.classList.contains('boo'));
assert(element.classList.contains('far'));
assert(!element.classList.contains('bar'));
assert(!element.classList.contains('foo'));
assert(!element.classList.contains(''));
});
test('removeClass', () => {
let element = document.createElement('div');
element.className = 'foobar boo far';
element.classList.remove('boo');
assert(element.classList.contains('far'));
assert(!element.classList.contains('boo'));
assert(element.classList.contains('foobar'));
assert.equal(element.className, 'foobar far');
element = document.createElement('div');
element.className = 'foobar boo far';
element.classList.remove('far');
assert(!element.classList.contains('far'));
assert(element.classList.contains('boo'));
assert(element.classList.contains('foobar'));
assert.equal(element.className, 'foobar boo');
element.classList.remove('boo');
assert(!element.classList.contains('far'));
assert(!element.classList.contains('boo'));
assert(element.classList.contains('foobar'));
assert.equal(element.className, 'foobar');
element.classList.remove('foobar');
assert(!element.classList.contains('far'));
assert(!element.classList.contains('boo'));
assert(!element.classList.contains('foobar'));
assert.equal(element.className, '');
});
test('removeClass should consider hyphens', function () {
let element = document.createElement('div');
element.classList.add('foo-bar');
element.classList.add('bar');
assert(element.classList.contains('foo-bar'));
assert(element.classList.contains('bar'));
element.classList.remove('bar');
assert(element.classList.contains('foo-bar'));
assert(!element.classList.contains('bar'));
element.classList.remove('foo-bar');
assert(!element.classList.contains('foo-bar'));
assert(!element.classList.contains('bar'));
});
test('multibyteAwareBtoa', () => {
assert.equal(dom.multibyteAwareBtoa('hello world'), dom.multibyteAwareBtoa('hello world'));
assert.ok(dom.multibyteAwareBtoa('平仮名'));
});
suite('$', () => {
test('should build simple nodes', () => {
const div = $('div');
assert(div);
assert(div instanceof HTMLElement);
assert.equal(div.tagName, 'DIV');
assert(!div.firstChild);
});
test('should buld nodes with id', () => {
const div = $('div#foo');
assert(div);
assert(div instanceof HTMLElement);
assert.equal(div.tagName, 'DIV');
assert.equal(div.id, 'foo');
});
test('should buld nodes with class-name', () => {
const div = $('div.foo');
assert(div);
assert(div instanceof HTMLElement);
assert.equal(div.tagName, 'DIV');
assert.equal(div.className, 'foo');
});
test('should build nodes with attributes', () => {
let div = $('div', { class: 'test' });
assert.equal(div.className, 'test');
div = $('div', undefined);
assert.equal(div.className, '');
});
test('should build nodes with children', () => {
let div = $('div', undefined, $('span', { id: 'demospan' }));
let firstChild = div.firstChild as HTMLElement;
assert.equal(firstChild.tagName, 'SPAN');
assert.equal(firstChild.id, 'demospan');
div = $('div', undefined, 'hello');
assert.equal(div.firstChild && div.firstChild.textContent, 'hello');
});
test('should build nodes with text children', () => {
let div = $('div', undefined, 'foobar');
let firstChild = div.firstChild as HTMLElement;
assert.equal(firstChild.tagName, undefined);
assert.equal(firstChild.textContent, 'foobar');
});
});
});

View File

@@ -0,0 +1,104 @@
/*---------------------------------------------------------------------------------------------
* 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 { renderText, renderFormattedText } from 'vs/base/browser/formattedTextRenderer';
import { DisposableStore } from 'vs/base/common/lifecycle';
suite('FormattedTextRenderer', () => {
const store = new DisposableStore();
setup(() => {
store.clear();
});
teardown(() => {
store.clear();
});
test('render simple element', () => {
let result: HTMLElement = renderText('testing');
assert.strictEqual(result.nodeType, document.ELEMENT_NODE);
assert.strictEqual(result.textContent, 'testing');
assert.strictEqual(result.tagName, 'DIV');
});
test('render element with class', () => {
let result: HTMLElement = renderText('testing', {
className: 'testClass'
});
assert.strictEqual(result.nodeType, document.ELEMENT_NODE);
assert.strictEqual(result.className, 'testClass');
});
test('simple formatting', () => {
let result: HTMLElement = renderFormattedText('**bold**');
assert.strictEqual(result.children.length, 1);
assert.strictEqual(result.firstChild!.textContent, 'bold');
assert.strictEqual((<HTMLElement>result.firstChild).tagName, 'B');
assert.strictEqual(result.innerHTML, '<b>bold</b>');
result = renderFormattedText('__italics__');
assert.strictEqual(result.innerHTML, '<i>italics</i>');
result = renderFormattedText('this string has **bold** and __italics__');
assert.strictEqual(result.innerHTML, 'this string has <b>bold</b> and <i>italics</i>');
});
test('no formatting', () => {
let result: HTMLElement = renderFormattedText('this is just a string');
assert.strictEqual(result.innerHTML, 'this is just a string');
});
test('preserve newlines', () => {
let result: HTMLElement = renderFormattedText('line one\nline two');
assert.strictEqual(result.innerHTML, 'line one<br>line two');
});
test('action', () => {
let callbackCalled = false;
let result: HTMLElement = renderFormattedText('[[action]]', {
actionHandler: {
callback(content) {
assert.strictEqual(content, '0');
callbackCalled = true;
},
disposeables: store
}
});
assert.strictEqual(result.innerHTML, '<a href="#">action</a>');
let event: MouseEvent = <any>document.createEvent('MouseEvent');
event.initEvent('click', true, true);
result.firstChild!.dispatchEvent(event);
assert.strictEqual(callbackCalled, true);
});
test('fancy action', () => {
let callbackCalled = false;
let result: HTMLElement = renderFormattedText('__**[[action]]**__', {
actionHandler: {
callback(content) {
assert.strictEqual(content, '0');
callbackCalled = true;
},
disposeables: store
}
});
assert.strictEqual(result.innerHTML, '<i><b><a href="#">action</a></b></i>');
let event: MouseEvent = <any>document.createEvent('MouseEvent');
event.initEvent('click', true, true);
result.firstChild!.firstChild!.firstChild!.dispatchEvent(event);
assert.strictEqual(callbackCalled, true);
});
test('escaped formatting', () => {
let result: HTMLElement = renderFormattedText('\\*\\*bold\\*\\*');
assert.strictEqual(result.children.length, 0);
assert.strictEqual(result.innerHTML, '**bold**');
});
});

View File

@@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* 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 { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
suite('HighlightedLabel', () => {
let label: HighlightedLabel;
setup(() => {
label = new HighlightedLabel(document.createElement('div'), true);
});
test('empty label', function () {
assert.equal(label.element.innerHTML, '');
});
test('no decorations', function () {
label.set('hello');
assert.equal(label.element.innerHTML, '<span>hello</span>');
});
test('escape html', function () {
label.set('hel<lo');
assert.equal(label.element.innerHTML, '<span>hel&lt;lo</span>');
});
test('everything highlighted', function () {
label.set('hello', [{ start: 0, end: 5 }]);
assert.equal(label.element.innerHTML, '<span class="highlight">hello</span>');
});
test('beginning highlighted', function () {
label.set('hellothere', [{ start: 0, end: 5 }]);
assert.equal(label.element.innerHTML, '<span class="highlight">hello</span><span>there</span>');
});
test('ending highlighted', function () {
label.set('goodbye', [{ start: 4, end: 7 }]);
assert.equal(label.element.innerHTML, '<span>good</span><span class="highlight">bye</span>');
});
test('middle highlighted', function () {
label.set('foobarfoo', [{ start: 3, end: 6 }]);
assert.equal(label.element.innerHTML, '<span>foo</span><span class="highlight">bar</span><span>foo</span>');
});
test('escapeNewLines', () => {
let highlights = [{ start: 0, end: 5 }, { start: 7, end: 9 }, { start: 11, end: 12 }];// before,after,after
let escaped = HighlightedLabel.escapeNewLines('ACTION\r\n_TYPE2', highlights);
assert.equal(escaped, 'ACTION\u23CE_TYPE2');
assert.deepEqual(highlights, [{ start: 0, end: 5 }, { start: 6, end: 8 }, { start: 10, end: 11 }]);
highlights = [{ start: 5, end: 9 }, { start: 11, end: 12 }];//overlap,after
escaped = HighlightedLabel.escapeNewLines('ACTION\r\n_TYPE2', highlights);
assert.equal(escaped, 'ACTION\u23CE_TYPE2');
assert.deepEqual(highlights, [{ start: 5, end: 8 }, { start: 10, end: 11 }]);
});
});

View File

@@ -0,0 +1,118 @@
/*---------------------------------------------------------------------------------------------
* 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 * as marked from 'vs/base/common/marked/marked';
import { renderMarkdown } from 'vs/base/browser/markdownRenderer';
import { MarkdownString, IMarkdownString } from 'vs/base/common/htmlContent';
import { URI } from 'vs/base/common/uri';
import { parse } from 'vs/base/common/marshalling';
suite('MarkdownRenderer', () => {
suite('Images', () => {
test('image rendering conforms to default', () => {
const markdown = { value: `![image](someimageurl 'caption')` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
const imageFromMarked = marked(markdown.value, {
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image rendering conforms to default without title', () => {
const markdown = { value: `![image](someimageurl)` };
const result: HTMLElement = renderMarkdown(markdown);
const renderer = new marked.Renderer();
const imageFromMarked = marked(markdown.value, {
renderer
}).trim();
assert.strictEqual(result.innerHTML, imageFromMarked);
});
test('image width from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" width="100"></p>`);
});
test('image height from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" height="100"></p>`);
});
test('image width and height from title params', () => {
let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` });
assert.strictEqual(result.innerHTML, `<p><img src="someimageurl" alt="image" title="caption" width="100" height="200"></p>`);
});
});
suite('ThemeIcons Support On', () => {
test('render appendText', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: true });
mds.appendText('$(zap) $(not a theme icon) $(add)');
let result: HTMLElement = renderMarkdown(mds);
assert.strictEqual(result.innerHTML, `<p>$(zap) $(not a theme icon) $(add)</p>`);
});
test('render appendMarkdown', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: true });
mds.appendMarkdown('$(zap) $(not a theme icon) $(add)');
let result: HTMLElement = renderMarkdown(mds);
assert.strictEqual(result.innerHTML, `<p><span class="codicon codicon-zap"></span> $(not a theme icon) <span class="codicon codicon-add"></span></p>`);
});
test('render appendMarkdown with escaped icon', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: true });
mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)');
let result: HTMLElement = renderMarkdown(mds);
assert.strictEqual(result.innerHTML, `<p>$(zap) $(not a theme icon) <span class="codicon codicon-add"></span></p>`);
});
});
suite('ThemeIcons Support Off', () => {
test('render appendText', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: false });
mds.appendText('$(zap) $(not a theme icon) $(add)');
let result: HTMLElement = renderMarkdown(mds);
assert.strictEqual(result.innerHTML, `<p>$(zap) $(not a theme icon) $(add)</p>`);
});
test('render appendMarkdown with escaped icon', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: false });
mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)');
let result: HTMLElement = renderMarkdown(mds);
assert.strictEqual(result.innerHTML, `<p>$(zap) $(not a theme icon) $(add)</p>`);
});
});
test('npm Hover Run Script not working #90855', function () {
const md: IMarkdownString = JSON.parse('{"value":"[Run Script](command:npm.runScriptFromHover?%7B%22documentUri%22%3A%7B%22%24mid%22%3A1%2C%22fsPath%22%3A%22c%3A%5C%5CUsers%5C%5Cjrieken%5C%5CCode%5C%5C_sample%5C%5Cfoo%5C%5Cpackage.json%22%2C%22_sep%22%3A1%2C%22external%22%3A%22file%3A%2F%2F%2Fc%253A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22path%22%3A%22%2Fc%3A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22scheme%22%3A%22file%22%7D%2C%22script%22%3A%22echo%22%7D \\"Run the script as a task\\")","supportThemeIcons":false,"isTrusted":true,"uris":{"__uri_e49443":{"$mid":1,"fsPath":"c:\\\\Users\\\\jrieken\\\\Code\\\\_sample\\\\foo\\\\package.json","_sep":1,"external":"file:///c%3A/Users/jrieken/Code/_sample/foo/package.json","path":"/c:/Users/jrieken/Code/_sample/foo/package.json","scheme":"file"},"command:npm.runScriptFromHover?%7B%22documentUri%22%3A%7B%22%24mid%22%3A1%2C%22fsPath%22%3A%22c%3A%5C%5CUsers%5C%5Cjrieken%5C%5CCode%5C%5C_sample%5C%5Cfoo%5C%5Cpackage.json%22%2C%22_sep%22%3A1%2C%22external%22%3A%22file%3A%2F%2F%2Fc%253A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22path%22%3A%22%2Fc%3A%2FUsers%2Fjrieken%2FCode%2F_sample%2Ffoo%2Fpackage.json%22%2C%22scheme%22%3A%22file%22%7D%2C%22script%22%3A%22echo%22%7D":{"$mid":1,"path":"npm.runScriptFromHover","scheme":"command","query":"{\\"documentUri\\":\\"__uri_e49443\\",\\"script\\":\\"echo\\"}"}}}');
const element = renderMarkdown(md);
const anchor = element.querySelector('a')!;
assert.ok(anchor);
assert.ok(anchor.dataset['href']);
const uri = URI.parse(anchor.dataset['href']!);
const data = <{ script: string, documentUri: URI }>parse(decodeURIComponent(uri.query));
assert.ok(data);
assert.equal(data.script, 'echo');
assert.ok(data.documentUri.toString().startsWith('file:///c%3A/'));
});
});

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* 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 { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
suite('ProgressBar', () => {
let fixture: HTMLElement;
setup(() => {
fixture = document.createElement('div');
document.body.appendChild(fixture);
});
teardown(() => {
document.body.removeChild(fixture);
});
test('Progress Bar', function () {
const bar = new ProgressBar(fixture);
assert(bar.infinite());
assert(bar.total(100));
assert(bar.worked(50));
assert(bar.setWorked(70));
assert(bar.worked(30));
assert(bar.done());
bar.dispose();
});
});

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* 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 { layout, LayoutAnchorPosition } from 'vs/base/browser/ui/contextview/contextview';
suite('Contextview', function () {
test('layout', () => {
assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.Before }), 0);
assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.Before }), 50);
assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.Before }), 180);
assert.equal(layout(200, 20, { offset: 0, size: 0, position: LayoutAnchorPosition.After }), 0);
assert.equal(layout(200, 20, { offset: 50, size: 0, position: LayoutAnchorPosition.After }), 30);
assert.equal(layout(200, 20, { offset: 200, size: 0, position: LayoutAnchorPosition.After }), 180);
assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.Before }), 50);
assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.Before }), 100);
assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.Before }), 130);
assert.equal(layout(200, 20, { offset: 0, size: 50, position: LayoutAnchorPosition.After }), 50);
assert.equal(layout(200, 20, { offset: 50, size: 50, position: LayoutAnchorPosition.After }), 30);
assert.equal(layout(200, 20, { offset: 150, size: 50, position: LayoutAnchorPosition.After }), 130);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,225 @@
/*---------------------------------------------------------------------------------------------
* 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 { $ } from 'vs/base/browser/dom';
import { GridView, IView, Sizing } from 'vs/base/browser/ui/grid/gridview';
import { nodesToArrays, TestView } from './util';
suite('Gridview', function () {
let gridview: GridView;
setup(function () {
gridview = new GridView();
const container = $('.container');
container.style.position = 'absolute';
container.style.width = `${200}px`;
container.style.height = `${200}px`;
container.appendChild(gridview.element);
});
test('empty gridview is empty', function () {
assert.deepEqual(nodesToArrays(gridview.getView()), []);
gridview.dispose();
});
test('gridview addView', function () {
const view = new TestView(20, 20, 20, 20);
assert.throws(() => gridview.addView(view, 200, []), 'empty location');
assert.throws(() => gridview.addView(view, 200, [1]), 'index overflow');
assert.throws(() => gridview.addView(view, 200, [0, 0]), 'hierarchy overflow');
const views = [
new TestView(20, 20, 20, 20),
new TestView(20, 20, 20, 20),
new TestView(20, 20, 20, 20)
];
gridview.addView(views[0], 200, [0]);
gridview.addView(views[1], 200, [1]);
gridview.addView(views[2], 200, [2]);
assert.deepEqual(nodesToArrays(gridview.getView()), views);
gridview.dispose();
});
test('gridview addView nested', function () {
const views = [
new TestView(20, 20, 20, 20),
[
new TestView(20, 20, 20, 20),
new TestView(20, 20, 20, 20)
]
];
gridview.addView(views[0] as IView, 200, [0]);
gridview.addView((views[1] as TestView[])[0] as IView, 200, [1]);
gridview.addView((views[1] as TestView[])[1] as IView, 200, [1, 1]);
assert.deepEqual(nodesToArrays(gridview.getView()), views);
gridview.dispose();
});
test('gridview addView deep nested', function () {
const view1 = new TestView(20, 20, 20, 20);
gridview.addView(view1 as IView, 200, [0]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1]);
const view2 = new TestView(20, 20, 20, 20);
gridview.addView(view2 as IView, 200, [1]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, view2]);
const view3 = new TestView(20, 20, 20, 20);
gridview.addView(view3 as IView, 200, [1, 0]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view3, view2]]);
const view4 = new TestView(20, 20, 20, 20);
gridview.addView(view4 as IView, 200, [1, 0, 0]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [[view4, view3], view2]]);
const view5 = new TestView(20, 20, 20, 20);
gridview.addView(view5 as IView, 200, [1, 0]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view4, view3], view2]]);
const view6 = new TestView(20, 20, 20, 20);
gridview.addView(view6 as IView, 200, [2]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view4, view3], view2], view6]);
const view7 = new TestView(20, 20, 20, 20);
gridview.addView(view7 as IView, 200, [1, 1]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, view7, [view4, view3], view2], view6]);
const view8 = new TestView(20, 20, 20, 20);
gridview.addView(view8 as IView, 200, [1, 1, 0]);
assert.deepEqual(nodesToArrays(gridview.getView()), [view1, [view5, [view8, view7], [view4, view3], view2], view6]);
gridview.dispose();
});
test('simple layout', function () {
gridview.layout(800, 600);
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, 200, [0]);
assert.deepEqual(view1.size, [800, 600]);
assert.deepEqual(gridview.getViewSize([0]), { width: 800, height: 600 });
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, 200, [0]);
assert.deepEqual(view1.size, [800, 400]);
assert.deepEqual(gridview.getViewSize([1]), { width: 800, height: 400 });
assert.deepEqual(view2.size, [800, 200]);
assert.deepEqual(gridview.getViewSize([0]), { width: 800, height: 200 });
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, 200, [1, 1]);
assert.deepEqual(view1.size, [600, 400]);
assert.deepEqual(gridview.getViewSize([1, 0]), { width: 600, height: 400 });
assert.deepEqual(view2.size, [800, 200]);
assert.deepEqual(gridview.getViewSize([0]), { width: 800, height: 200 });
assert.deepEqual(view3.size, [200, 400]);
assert.deepEqual(gridview.getViewSize([1, 1]), { width: 200, height: 400 });
const view4 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view4, 200, [0, 0]);
assert.deepEqual(view1.size, [600, 400]);
assert.deepEqual(gridview.getViewSize([1, 0]), { width: 600, height: 400 });
assert.deepEqual(view2.size, [600, 200]);
assert.deepEqual(gridview.getViewSize([0, 1]), { width: 600, height: 200 });
assert.deepEqual(view3.size, [200, 400]);
assert.deepEqual(gridview.getViewSize([1, 1]), { width: 200, height: 400 });
assert.deepEqual(view4.size, [200, 200]);
assert.deepEqual(gridview.getViewSize([0, 0]), { width: 200, height: 200 });
const view5 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view5, 100, [1, 0, 1]);
assert.deepEqual(view1.size, [600, 300]);
assert.deepEqual(gridview.getViewSize([1, 0, 0]), { width: 600, height: 300 });
assert.deepEqual(view2.size, [600, 200]);
assert.deepEqual(gridview.getViewSize([0, 1]), { width: 600, height: 200 });
assert.deepEqual(view3.size, [200, 400]);
assert.deepEqual(gridview.getViewSize([1, 1]), { width: 200, height: 400 });
assert.deepEqual(view4.size, [200, 200]);
assert.deepEqual(gridview.getViewSize([0, 0]), { width: 200, height: 200 });
assert.deepEqual(view5.size, [600, 100]);
assert.deepEqual(gridview.getViewSize([1, 0, 1]), { width: 600, height: 100 });
});
test('simple layout with automatic size distribution', function () {
gridview.layout(800, 600);
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, Sizing.Distribute, [0]);
assert.deepEqual(view1.size, [800, 600]);
assert.deepEqual(gridview.getViewSize([0]), { width: 800, height: 600 });
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, Sizing.Distribute, [0]);
assert.deepEqual(view1.size, [800, 300]);
assert.deepEqual(view2.size, [800, 300]);
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, Sizing.Distribute, [1, 1]);
assert.deepEqual(view1.size, [400, 300]);
assert.deepEqual(view2.size, [800, 300]);
assert.deepEqual(view3.size, [400, 300]);
const view4 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view4, Sizing.Distribute, [0, 0]);
assert.deepEqual(view1.size, [400, 300]);
assert.deepEqual(view2.size, [400, 300]);
assert.deepEqual(view3.size, [400, 300]);
assert.deepEqual(view4.size, [400, 300]);
const view5 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view5, Sizing.Distribute, [1, 0, 1]);
assert.deepEqual(view1.size, [400, 150]);
assert.deepEqual(view2.size, [400, 300]);
assert.deepEqual(view3.size, [400, 300]);
assert.deepEqual(view4.size, [400, 300]);
assert.deepEqual(view5.size, [400, 150]);
});
test('addviews before layout call 1', function () {
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, 200, [0]);
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, 200, [0]);
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, 200, [1, 1]);
gridview.layout(800, 600);
assert.deepEqual(view1.size, [400, 300]);
assert.deepEqual(view2.size, [800, 300]);
assert.deepEqual(view3.size, [400, 300]);
});
test('addviews before layout call 2', function () {
const view1 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view1, 200, [0]);
const view2 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view2, 200, [0]);
const view3 = new TestView(50, Number.POSITIVE_INFINITY, 50, Number.POSITIVE_INFINITY);
gridview.addView(view3, 200, [0, 0]);
gridview.layout(800, 600);
assert.deepEqual(view1.size, [800, 300]);
assert.deepEqual(view2.size, [400, 300]);
assert.deepEqual(view3.size, [400, 300]);
});
});

View File

@@ -0,0 +1,82 @@
/*---------------------------------------------------------------------------------------------
* 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 { Emitter, Event } from 'vs/base/common/event';
import { GridNode, isGridBranchNode } from 'vs/base/browser/ui/grid/gridview';
import { IView } from 'vs/base/browser/ui/grid/grid';
export class TestView implements IView {
private readonly _onDidChange = new Emitter<{ width: number; height: number; } | undefined>();
readonly onDidChange = this._onDidChange.event;
get minimumWidth(): number { return this._minimumWidth; }
set minimumWidth(size: number) { this._minimumWidth = size; this._onDidChange.fire(undefined); }
get maximumWidth(): number { return this._maximumWidth; }
set maximumWidth(size: number) { this._maximumWidth = size; this._onDidChange.fire(undefined); }
get minimumHeight(): number { return this._minimumHeight; }
set minimumHeight(size: number) { this._minimumHeight = size; this._onDidChange.fire(undefined); }
get maximumHeight(): number { return this._maximumHeight; }
set maximumHeight(size: number) { this._maximumHeight = size; this._onDidChange.fire(undefined); }
private _element: HTMLElement = document.createElement('div');
get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; }
private readonly _onDidGetElement = new Emitter<void>();
readonly onDidGetElement = this._onDidGetElement.event;
private _width = 0;
get width(): number { return this._width; }
private _height = 0;
get height(): number { return this._height; }
get size(): [number, number] { return [this.width, this.height]; }
private readonly _onDidLayout = new Emitter<{ width: number; height: number; }>();
readonly onDidLayout: Event<{ width: number; height: number; }> = this._onDidLayout.event;
private readonly _onDidFocus = new Emitter<void>();
readonly onDidFocus: Event<void> = this._onDidFocus.event;
constructor(
private _minimumWidth: number,
private _maximumWidth: number,
private _minimumHeight: number,
private _maximumHeight: number
) {
assert(_minimumWidth <= _maximumWidth, 'gridview view minimum width must be <= maximum width');
assert(_minimumHeight <= _maximumHeight, 'gridview view minimum height must be <= maximum height');
}
layout(width: number, height: number): void {
this._width = width;
this._height = height;
this._onDidLayout.fire({ width, height });
}
focus(): void {
this._onDidFocus.fire();
}
dispose(): void {
this._onDidChange.dispose();
this._onDidGetElement.dispose();
this._onDidLayout.dispose();
this._onDidFocus.dispose();
}
}
export function nodesToArrays(node: GridNode): any {
if (isGridBranchNode(node)) {
return node.children.map(nodesToArrays);
} else {
return node.view;
}
}

View File

@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* 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 { ListView } from 'vs/base/browser/ui/list/listView';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { range } from 'vs/base/common/arrays';
suite('ListView', function () {
test('all rows get disposed', function () {
const element = document.createElement('div');
element.style.height = '200px';
element.style.width = '200px';
const delegate: IListVirtualDelegate<number> = {
getHeight() { return 20; },
getTemplateId() { return 'template'; }
};
let templatesCount = 0;
const renderer: IListRenderer<number, void> = {
templateId: 'template',
renderTemplate() { templatesCount++; },
renderElement() { },
disposeTemplate() { templatesCount--; }
};
const listView = new ListView<number>(element, delegate, [renderer]);
listView.layout(200);
assert.equal(templatesCount, 0, 'no templates have been allocated');
listView.splice(0, 0, range(100));
assert.equal(templatesCount, 10, 'some templates have been allocated');
listView.dispose();
assert.equal(templatesCount, 0, 'all templates have been disposed');
});
});

View File

@@ -0,0 +1,345 @@
/*---------------------------------------------------------------------------------------------
* 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 { RangeMap, groupIntersect, consolidate } from 'vs/base/browser/ui/list/rangeMap';
import { Range } from 'vs/base/common/range';
suite('RangeMap', () => {
let rangeMap: RangeMap;
setup(() => {
rangeMap = new RangeMap();
});
test('intersection', () => {
assert.deepEqual(Range.intersect({ start: 0, end: 0 }, { start: 0, end: 0 }), { start: 0, end: 0 });
assert.deepEqual(Range.intersect({ start: 0, end: 0 }, { start: 5, end: 5 }), { start: 0, end: 0 });
assert.deepEqual(Range.intersect({ start: 0, end: 1 }, { start: 5, end: 6 }), { start: 0, end: 0 });
assert.deepEqual(Range.intersect({ start: 5, end: 6 }, { start: 0, end: 1 }), { start: 0, end: 0 });
assert.deepEqual(Range.intersect({ start: 0, end: 5 }, { start: 2, end: 2 }), { start: 0, end: 0 });
assert.deepEqual(Range.intersect({ start: 0, end: 1 }, { start: 0, end: 1 }), { start: 0, end: 1 });
assert.deepEqual(Range.intersect({ start: 0, end: 10 }, { start: 0, end: 5 }), { start: 0, end: 5 });
assert.deepEqual(Range.intersect({ start: 0, end: 5 }, { start: 0, end: 10 }), { start: 0, end: 5 });
assert.deepEqual(Range.intersect({ start: 0, end: 10 }, { start: 5, end: 10 }), { start: 5, end: 10 });
assert.deepEqual(Range.intersect({ start: 5, end: 10 }, { start: 0, end: 10 }), { start: 5, end: 10 });
assert.deepEqual(Range.intersect({ start: 0, end: 10 }, { start: 2, end: 8 }), { start: 2, end: 8 });
assert.deepEqual(Range.intersect({ start: 2, end: 8 }, { start: 0, end: 10 }), { start: 2, end: 8 });
assert.deepEqual(Range.intersect({ start: 0, end: 10 }, { start: 5, end: 15 }), { start: 5, end: 10 });
assert.deepEqual(Range.intersect({ start: 5, end: 15 }, { start: 0, end: 10 }), { start: 5, end: 10 });
});
test('multiIntersect', () => {
assert.deepEqual(
groupIntersect(
{ start: 0, end: 0 },
[{ range: { start: 0, end: 10 }, size: 1 }]
),
[]
);
assert.deepEqual(
groupIntersect(
{ start: 10, end: 20 },
[{ range: { start: 0, end: 10 }, size: 1 }]
),
[]
);
assert.deepEqual(
groupIntersect(
{ start: 2, end: 8 },
[{ range: { start: 0, end: 10 }, size: 1 }]
),
[{ range: { start: 2, end: 8 }, size: 1 }]
);
assert.deepEqual(
groupIntersect(
{ start: 2, end: 8 },
[{ range: { start: 0, end: 10 }, size: 1 }, { range: { start: 10, end: 20 }, size: 5 }]
),
[{ range: { start: 2, end: 8 }, size: 1 }]
);
assert.deepEqual(
groupIntersect(
{ start: 12, end: 18 },
[{ range: { start: 0, end: 10 }, size: 1 }, { range: { start: 10, end: 20 }, size: 5 }]
),
[{ range: { start: 12, end: 18 }, size: 5 }]
);
assert.deepEqual(
groupIntersect(
{ start: 2, end: 18 },
[{ range: { start: 0, end: 10 }, size: 1 }, { range: { start: 10, end: 20 }, size: 5 }]
),
[{ range: { start: 2, end: 10 }, size: 1 }, { range: { start: 10, end: 18 }, size: 5 }]
);
assert.deepEqual(
groupIntersect(
{ start: 2, end: 28 },
[{ range: { start: 0, end: 10 }, size: 1 }, { range: { start: 10, end: 20 }, size: 5 }, { range: { start: 20, end: 30 }, size: 10 }]
),
[{ range: { start: 2, end: 10 }, size: 1 }, { range: { start: 10, end: 20 }, size: 5 }, { range: { start: 20, end: 28 }, size: 10 }]
);
});
test('consolidate', () => {
assert.deepEqual(consolidate([]), []);
assert.deepEqual(
consolidate([{ range: { start: 0, end: 10 }, size: 1 }]),
[{ range: { start: 0, end: 10 }, size: 1 }]
);
assert.deepEqual(
consolidate([
{ range: { start: 0, end: 10 }, size: 1 },
{ range: { start: 10, end: 20 }, size: 1 }
]),
[{ range: { start: 0, end: 20 }, size: 1 }]
);
assert.deepEqual(
consolidate([
{ range: { start: 0, end: 10 }, size: 1 },
{ range: { start: 10, end: 20 }, size: 1 },
{ range: { start: 20, end: 100 }, size: 1 }
]),
[{ range: { start: 0, end: 100 }, size: 1 }]
);
assert.deepEqual(
consolidate([
{ range: { start: 0, end: 10 }, size: 1 },
{ range: { start: 10, end: 20 }, size: 5 },
{ range: { start: 20, end: 30 }, size: 10 }
]),
[
{ range: { start: 0, end: 10 }, size: 1 },
{ range: { start: 10, end: 20 }, size: 5 },
{ range: { start: 20, end: 30 }, size: 10 }
]
);
assert.deepEqual(
consolidate([
{ range: { start: 0, end: 10 }, size: 1 },
{ range: { start: 10, end: 20 }, size: 2 },
{ range: { start: 20, end: 100 }, size: 2 }
]),
[
{ range: { start: 0, end: 10 }, size: 1 },
{ range: { start: 10, end: 100 }, size: 2 }
]
);
});
test('empty', () => {
assert.equal(rangeMap.size, 0);
assert.equal(rangeMap.count, 0);
});
const one = { size: 1 };
const two = { size: 2 };
const three = { size: 3 };
const five = { size: 5 };
const ten = { size: 10 };
test('length & count', () => {
rangeMap.splice(0, 0, [one]);
assert.equal(rangeMap.size, 1);
assert.equal(rangeMap.count, 1);
});
test('length & count #2', () => {
rangeMap.splice(0, 0, [one, one, one, one, one]);
assert.equal(rangeMap.size, 5);
assert.equal(rangeMap.count, 5);
});
test('length & count #3', () => {
rangeMap.splice(0, 0, [five]);
assert.equal(rangeMap.size, 5);
assert.equal(rangeMap.count, 1);
});
test('length & count #4', () => {
rangeMap.splice(0, 0, [five, five, five, five, five]);
assert.equal(rangeMap.size, 25);
assert.equal(rangeMap.count, 5);
});
test('insert', () => {
rangeMap.splice(0, 0, [five, five, five, five, five]);
assert.equal(rangeMap.size, 25);
assert.equal(rangeMap.count, 5);
rangeMap.splice(0, 0, [five, five, five, five, five]);
assert.equal(rangeMap.size, 50);
assert.equal(rangeMap.count, 10);
rangeMap.splice(5, 0, [ten, ten]);
assert.equal(rangeMap.size, 70);
assert.equal(rangeMap.count, 12);
rangeMap.splice(12, 0, [{ size: 200 }]);
assert.equal(rangeMap.size, 270);
assert.equal(rangeMap.count, 13);
});
test('delete', () => {
rangeMap.splice(0, 0, [five, five, five, five, five,
five, five, five, five, five,
five, five, five, five, five,
five, five, five, five, five]);
assert.equal(rangeMap.size, 100);
assert.equal(rangeMap.count, 20);
rangeMap.splice(10, 5);
assert.equal(rangeMap.size, 75);
assert.equal(rangeMap.count, 15);
rangeMap.splice(0, 1);
assert.equal(rangeMap.size, 70);
assert.equal(rangeMap.count, 14);
rangeMap.splice(1, 13);
assert.equal(rangeMap.size, 5);
assert.equal(rangeMap.count, 1);
rangeMap.splice(1, 1);
assert.equal(rangeMap.size, 5);
assert.equal(rangeMap.count, 1);
});
test('insert & delete', () => {
assert.equal(rangeMap.size, 0);
assert.equal(rangeMap.count, 0);
rangeMap.splice(0, 0, [one]);
assert.equal(rangeMap.size, 1);
assert.equal(rangeMap.count, 1);
rangeMap.splice(0, 1);
assert.equal(rangeMap.size, 0);
assert.equal(rangeMap.count, 0);
});
test('insert & delete #2', () => {
rangeMap.splice(0, 0, [one, one, one, one, one,
one, one, one, one, one]);
rangeMap.splice(2, 6);
assert.equal(rangeMap.count, 4);
assert.equal(rangeMap.size, 4);
});
test('insert & delete #3', () => {
rangeMap.splice(0, 0, [one, one, one, one, one,
one, one, one, one, one,
two, two, two, two, two,
two, two, two, two, two]);
rangeMap.splice(8, 4);
assert.equal(rangeMap.count, 16);
assert.equal(rangeMap.size, 24);
});
test('insert & delete #3', () => {
rangeMap.splice(0, 0, [one, one, one, one, one,
one, one, one, one, one,
two, two, two, two, two,
two, two, two, two, two]);
rangeMap.splice(5, 0, [three, three, three, three, three]);
assert.equal(rangeMap.count, 25);
assert.equal(rangeMap.size, 45);
rangeMap.splice(4, 7);
assert.equal(rangeMap.count, 18);
assert.equal(rangeMap.size, 28);
});
suite('indexAt, positionAt', () => {
test('empty', () => {
assert.equal(rangeMap.indexAt(0), 0);
assert.equal(rangeMap.indexAt(10), 0);
assert.equal(rangeMap.indexAt(-1), -1);
assert.equal(rangeMap.positionAt(0), -1);
assert.equal(rangeMap.positionAt(10), -1);
assert.equal(rangeMap.positionAt(-1), -1);
});
test('simple', () => {
rangeMap.splice(0, 0, [one]);
assert.equal(rangeMap.indexAt(0), 0);
assert.equal(rangeMap.indexAt(1), 1);
assert.equal(rangeMap.positionAt(0), 0);
assert.equal(rangeMap.positionAt(1), -1);
});
test('simple #2', () => {
rangeMap.splice(0, 0, [ten]);
assert.equal(rangeMap.indexAt(0), 0);
assert.equal(rangeMap.indexAt(5), 0);
assert.equal(rangeMap.indexAt(9), 0);
assert.equal(rangeMap.indexAt(10), 1);
assert.equal(rangeMap.positionAt(0), 0);
assert.equal(rangeMap.positionAt(1), -1);
});
test('insert', () => {
rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]);
assert.equal(rangeMap.indexAt(0), 0);
assert.equal(rangeMap.indexAt(1), 1);
assert.equal(rangeMap.indexAt(5), 5);
assert.equal(rangeMap.indexAt(9), 9);
assert.equal(rangeMap.indexAt(10), 10);
assert.equal(rangeMap.indexAt(11), 10);
rangeMap.splice(10, 0, [one, one, one, one, one, one, one, one, one, one]);
assert.equal(rangeMap.indexAt(10), 10);
assert.equal(rangeMap.indexAt(19), 19);
assert.equal(rangeMap.indexAt(20), 20);
assert.equal(rangeMap.indexAt(21), 20);
assert.equal(rangeMap.positionAt(0), 0);
assert.equal(rangeMap.positionAt(1), 1);
assert.equal(rangeMap.positionAt(19), 19);
assert.equal(rangeMap.positionAt(20), -1);
});
test('delete', () => {
rangeMap.splice(0, 0, [one, one, one, one, one, one, one, one, one, one]);
rangeMap.splice(2, 6);
assert.equal(rangeMap.indexAt(0), 0);
assert.equal(rangeMap.indexAt(1), 1);
assert.equal(rangeMap.indexAt(3), 3);
assert.equal(rangeMap.indexAt(4), 4);
assert.equal(rangeMap.indexAt(5), 4);
assert.equal(rangeMap.positionAt(0), 0);
assert.equal(rangeMap.positionAt(1), 1);
assert.equal(rangeMap.positionAt(3), 3);
assert.equal(rangeMap.positionAt(4), -1);
});
test('delete #2', () => {
rangeMap.splice(0, 0, [ten, ten, ten, ten, ten, ten, ten, ten, ten, ten]);
rangeMap.splice(2, 6);
assert.equal(rangeMap.indexAt(0), 0);
assert.equal(rangeMap.indexAt(1), 0);
assert.equal(rangeMap.indexAt(30), 3);
assert.equal(rangeMap.indexAt(40), 4);
assert.equal(rangeMap.indexAt(50), 4);
assert.equal(rangeMap.positionAt(0), 0);
assert.equal(rangeMap.positionAt(1), 10);
assert.equal(rangeMap.positionAt(2), 20);
assert.equal(rangeMap.positionAt(3), 30);
assert.equal(rangeMap.positionAt(4), -1);
});
});
});

View File

@@ -0,0 +1,81 @@
/*---------------------------------------------------------------------------------------------
* 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 { $ } from 'vs/base/browser/dom';
import { MenuBar } from 'vs/base/browser/ui/menu/menubar';
function getButtonElementByAriaLabel(menubarElement: HTMLElement, ariaLabel: string): HTMLElement | null {
let i;
for (i = 0; i < menubarElement.childElementCount; i++) {
if (menubarElement.children[i].getAttribute('aria-label') === ariaLabel) {
return menubarElement.children[i] as HTMLElement;
}
}
return null;
}
function getTitleDivFromButtonDiv(menuButtonElement: HTMLElement): HTMLElement | null {
let i;
for (i = 0; i < menuButtonElement.childElementCount; i++) {
if (menuButtonElement.children[i].classList.contains('menubar-menu-title')) {
return menuButtonElement.children[i] as HTMLElement;
}
}
return null;
}
function getMnemonicFromTitleDiv(menuTitleDiv: HTMLElement): string | null {
let i;
for (i = 0; i < menuTitleDiv.childElementCount; i++) {
if (menuTitleDiv.children[i].tagName.toLocaleLowerCase() === 'mnemonic') {
return menuTitleDiv.children[i].textContent;
}
}
return null;
}
function validateMenuBarItem(menubar: MenuBar, menubarContainer: HTMLElement, label: string, readableLabel: string, mnemonic: string) {
menubar.push([
{
actions: [],
label: label
}
]);
const buttonElement = getButtonElementByAriaLabel(menubarContainer, readableLabel);
assert(buttonElement !== null, `Button element not found for ${readableLabel} button.`);
const titleDiv = getTitleDivFromButtonDiv(buttonElement!);
assert(titleDiv !== null, `Title div not found for ${readableLabel} button.`);
const mnem = getMnemonicFromTitleDiv(titleDiv!);
assert.equal(mnem, mnemonic, 'Mnemonic not correct');
}
suite('Menubar', () => {
const container = $('.container');
const menubar = new MenuBar(container, {
enableMnemonics: true,
visibility: 'visible'
});
test('English File menu renders mnemonics', function () {
validateMenuBarItem(menubar, container, '&File', 'File', 'F');
});
test('Russian File menu renders mnemonics', function () {
validateMenuBarItem(menubar, container, '&Файл', 'Файл', 'Ф');
});
test('Chinese File menu renders mnemonics', function () {
validateMenuBarItem(menubar, container, '文件(&F)', '文件', 'F');
});
});

View File

@@ -0,0 +1,524 @@
/*---------------------------------------------------------------------------------------------
* 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 { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement';
export type IMouseWheelEvent = [number, number, number];
suite('MouseWheelClassifier', () => {
test('OSX - Apple Magic Mouse', () => {
const testData: IMouseWheelEvent[] = [
[1503409622410, -0.025, 0],
[1503409622435, -0.175, 0],
[1503409622446, -0.225, 0],
[1503409622489, -0.65, 0],
[1503409622514, -1.225, 0],
[1503409622537, -1.025, 0],
[1503409622543, -0.55, 0],
[1503409622587, -0.75, 0],
[1503409622623, -1.45, 0],
[1503409622641, -1.325, 0],
[1503409622663, -0.6, 0],
[1503409622681, -1.125, 0],
[1503409622703, -0.5166666666666667, 0],
[1503409622721, -0.475, 0],
[1503409622822, -0.425, 0],
[1503409622871, -1.9916666666666667, 0],
[1503409622933, -0.7, 0],
[1503409622991, -0.725, 0],
[1503409623032, -0.45, 0],
[1503409623083, -0.25, 0],
[1503409623122, -0.4, 0],
[1503409623176, -0.2, 0],
[1503409623197, -0.225, 0],
[1503409623219, -0.05, 0],
[1503409623249, -0.1, 0],
[1503409623278, -0.1, 0],
[1503409623292, -0.025, 0],
[1503409623315, -0.025, 0],
[1503409623324, -0.05, 0],
[1503409623356, -0.025, 0],
[1503409623415, -0.025, 0],
[1503409623443, -0.05, 0],
[1503409623452, -0.025, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, false);
}
});
test('OSX - Apple Touch Pad', () => {
const testData: IMouseWheelEvent[] = [
[1503409780792, 0.025, 0],
[1503409780808, 0.175, -0.025],
[1503409780811, 0.35, -0.05],
[1503409780816, 0.55, -0.075],
[1503409780836, 0.825, -0.1],
[1503409780840, 0.725, -0.075],
[1503409780842, 1.5, -0.125],
[1503409780848, 1.1, -0.1],
[1503409780877, 2.05, -0.1],
[1503409780882, 3.9, 0],
[1503409780908, 3.825, 0],
[1503409780915, 3.65, 0],
[1503409780940, 3.45, 0],
[1503409780949, 3.25, 0],
[1503409780979, 3.075, 0],
[1503409780982, 2.9, 0],
[1503409781016, 2.75, 0],
[1503409781018, 2.625, 0],
[1503409781051, 2.5, 0],
[1503409781071, 2.4, 0],
[1503409781089, 2.3, 0],
[1503409781111, 2.175, 0],
[1503409781140, 3.975, 0],
[1503409781165, 1.8, 0],
[1503409781183, 3.3, 0],
[1503409781202, 1.475, 0],
[1503409781223, 1.375, 0],
[1503409781244, 1.275, 0],
[1503409781269, 2.25, 0],
[1503409781285, 1.025, 0],
[1503409781300, 0.925, 0],
[1503409781303, 0.875, 0],
[1503409781321, 0.8, 0],
[1503409781333, 0.725, 0],
[1503409781355, 0.65, 0],
[1503409781370, 0.6, 0],
[1503409781384, 0.55, 0],
[1503409781410, 0.5, 0],
[1503409781422, 0.475, 0],
[1503409781435, 0.425, 0],
[1503409781454, 0.4, 0],
[1503409781470, 0.35, 0],
[1503409781486, 0.325, 0],
[1503409781501, 0.3, 0],
[1503409781519, 0.275, 0],
[1503409781534, 0.25, 0],
[1503409781553, 0.225, 0],
[1503409781569, 0.2, 0],
[1503409781589, 0.2, 0],
[1503409781601, 0.175, 0],
[1503409781621, 0.15, 0],
[1503409781631, 0.15, 0],
[1503409781652, 0.125, 0],
[1503409781667, 0.125, 0],
[1503409781685, 0.125, 0],
[1503409781703, 0.1, 0],
[1503409781715, 0.1, 0],
[1503409781734, 0.1, 0],
[1503409781753, 0.075, 0],
[1503409781768, 0.075, 0],
[1503409781783, 0.075, 0],
[1503409781801, 0.075, 0],
[1503409781815, 0.05, 0],
[1503409781836, 0.05, 0],
[1503409781850, 0.05, 0],
[1503409781865, 0.05, 0],
[1503409781880, 0.05, 0],
[1503409781899, 0.025, 0],
[1503409781916, 0.025, 0],
[1503409781933, 0.025, 0],
[1503409781952, 0.025, 0],
[1503409781965, 0.025, 0],
[1503409781996, 0.025, 0],
[1503409782015, 0.025, 0],
[1503409782045, 0.025, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, false);
}
});
test('OSX - Razer Physical Mouse Wheel', () => {
const testData: IMouseWheelEvent[] = [
[1503409880776, -1, 0],
[1503409880791, -1, 0],
[1503409880810, -4, 0],
[1503409880820, -5, 0],
[1503409880848, -6, 0],
[1503409880876, -7, 0],
[1503409881319, -1, 0],
[1503409881387, -1, 0],
[1503409881407, -2, 0],
[1503409881443, -4, 0],
[1503409881444, -5, 0],
[1503409881470, -6, 0],
[1503409881496, -7, 0],
[1503409881812, -1, 0],
[1503409881829, -1, 0],
[1503409881850, -4, 0],
[1503409881871, -5, 0],
[1503409881896, -13, 0],
[1503409881914, -16, 0],
[1503409882551, -1, 0],
[1503409882589, -1, 0],
[1503409882625, -2, 0],
[1503409883035, -1, 0],
[1503409883098, -1, 0],
[1503409883143, -2, 0],
[1503409883217, -2, 0],
[1503409883270, -3, 0],
[1503409883388, -3, 0],
[1503409883531, -3, 0],
[1503409884095, -1, 0],
[1503409884122, -1, 0],
[1503409884160, -3, 0],
[1503409884208, -4, 0],
[1503409884292, -4, 0],
[1503409884447, -1, 0],
[1503409884788, -1, 0],
[1503409884835, -1, 0],
[1503409884898, -2, 0],
[1503409884965, -3, 0],
[1503409885085, -2, 0],
[1503409885552, -1, 0],
[1503409885619, -1, 0],
[1503409885670, -1, 0],
[1503409885733, -2, 0],
[1503409885784, -4, 0],
[1503409885916, -3, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, true);
}
});
test('Windows - Microsoft Arc Touch', () => {
const testData: IMouseWheelEvent[] = [
[1503418316909, -2, 0],
[1503418316985, -2, 0],
[1503418316988, -4, 0],
[1503418317034, -2, 0],
[1503418317071, -2, 0],
[1503418317094, -2, 0],
[1503418317133, -2, 0],
[1503418317170, -2, 0],
[1503418317192, -2, 0],
[1503418317265, -2, 0],
[1503418317289, -2, 0],
[1503418317365, -2, 0],
[1503418317414, -2, 0],
[1503418317458, -2, 0],
[1503418317513, -2, 0],
[1503418317583, -2, 0],
[1503418317637, -2, 0],
[1503418317720, -2, 0],
[1503418317786, -2, 0],
[1503418317832, -2, 0],
[1503418317933, -2, 0],
[1503418318037, -2, 0],
[1503418318134, -2, 0],
[1503418318267, -2, 0],
[1503418318411, -2, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, true);
}
});
test('Windows - SurfaceBook TouchPad', () => {
const testData: IMouseWheelEvent[] = [
[1503418499174, -3.35, 0],
[1503418499177, -0.9333333333333333, 0],
[1503418499222, -2.091666666666667, 0],
[1503418499238, -1.5666666666666667, 0],
[1503418499242, -1.8, 0],
[1503418499271, -2.5166666666666666, 0],
[1503418499283, -0.7666666666666667, 0],
[1503418499308, -2.033333333333333, 0],
[1503418499320, -2.85, 0],
[1503418499372, -1.5333333333333334, 0],
[1503418499373, -2.8, 0],
[1503418499411, -1.6166666666666667, 0],
[1503418499413, -1.9166666666666667, 0],
[1503418499443, -0.9333333333333333, 0],
[1503418499446, -0.9833333333333333, 0],
[1503418499458, -0.7666666666666667, 0],
[1503418499482, -0.9666666666666667, 0],
[1503418499485, -0.36666666666666664, 0],
[1503418499508, -0.5833333333333334, 0],
[1503418499532, -0.48333333333333334, 0],
[1503418499541, -0.6333333333333333, 0],
[1503418499571, -0.18333333333333332, 0],
[1503418499573, -0.4, 0],
[1503418499595, -0.15, 0],
[1503418499608, -0.23333333333333334, 0],
[1503418499625, -0.18333333333333332, 0],
[1503418499657, -0.13333333333333333, 0],
[1503418499674, -0.15, 0],
[1503418499676, -0.03333333333333333, 0],
[1503418499691, -0.016666666666666666, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, false);
}
});
test('Windows - Razer physical wheel', () => {
const testData: IMouseWheelEvent[] = [
[1503418638271, -2, 0],
[1503418638317, -2, 0],
[1503418638336, -2, 0],
[1503418638350, -2, 0],
[1503418638360, -2, 0],
[1503418638366, -2, 0],
[1503418638407, -2, 0],
[1503418638694, -2, 0],
[1503418638742, -2, 0],
[1503418638744, -2, 0],
[1503418638746, -2, 0],
[1503418638780, -2, 0],
[1503418638782, -2, 0],
[1503418638810, -2, 0],
[1503418639127, -2, 0],
[1503418639168, -2, 0],
[1503418639194, -2, 0],
[1503418639197, -4, 0],
[1503418639244, -2, 0],
[1503418639248, -2, 0],
[1503418639586, -2, 0],
[1503418639653, -2, 0],
[1503418639667, -4, 0],
[1503418639677, -2, 0],
[1503418639681, -2, 0],
[1503418639728, -2, 0],
[1503418639997, -2, 0],
[1503418640034, -2, 0],
[1503418640039, -2, 0],
[1503418640065, -2, 0],
[1503418640080, -2, 0],
[1503418640097, -2, 0],
[1503418640141, -2, 0],
[1503418640413, -2, 0],
[1503418640456, -2, 0],
[1503418640490, -2, 0],
[1503418640492, -4, 0],
[1503418640494, -2, 0],
[1503418640546, -2, 0],
[1503418640781, -2, 0],
[1503418640823, -2, 0],
[1503418640824, -2, 0],
[1503418640829, -2, 0],
[1503418640864, -2, 0],
[1503418640874, -2, 0],
[1503418640876, -2, 0],
[1503418641168, -2, 0],
[1503418641203, -2, 0],
[1503418641224, -2, 0],
[1503418641240, -2, 0],
[1503418641254, -4, 0],
[1503418641270, -2, 0],
[1503418641546, -2, 0],
[1503418641612, -2, 0],
[1503418641625, -6, 0],
[1503418641634, -2, 0],
[1503418641680, -2, 0],
[1503418641961, -2, 0],
[1503418642004, -2, 0],
[1503418642016, -4, 0],
[1503418642044, -2, 0],
[1503418642065, -2, 0],
[1503418642083, -2, 0],
[1503418642349, -2, 0],
[1503418642378, -2, 0],
[1503418642390, -2, 0],
[1503418642408, -2, 0],
[1503418642413, -2, 0],
[1503418642448, -2, 0],
[1503418642468, -2, 0],
[1503418642746, -2, 0],
[1503418642800, -2, 0],
[1503418642814, -4, 0],
[1503418642816, -2, 0],
[1503418642857, -2, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, true);
}
});
test('Windows - Logitech physical wheel', () => {
const testData: IMouseWheelEvent[] = [
[1503418872930, -2, 0],
[1503418872952, -2, 0],
[1503418872969, -2, 0],
[1503418873022, -2, 0],
[1503418873042, -2, 0],
[1503418873076, -2, 0],
[1503418873368, -2, 0],
[1503418873393, -2, 0],
[1503418873404, -2, 0],
[1503418873425, -2, 0],
[1503418873479, -2, 0],
[1503418873520, -2, 0],
[1503418873758, -2, 0],
[1503418873759, -2, 0],
[1503418873762, -2, 0],
[1503418873807, -2, 0],
[1503418873830, -4, 0],
[1503418873850, -2, 0],
[1503418874076, -2, 0],
[1503418874116, -2, 0],
[1503418874136, -4, 0],
[1503418874148, -2, 0],
[1503418874150, -2, 0],
[1503418874409, -2, 0],
[1503418874452, -2, 0],
[1503418874472, -2, 0],
[1503418874474, -4, 0],
[1503418874543, -2, 0],
[1503418874566, -2, 0],
[1503418874778, -2, 0],
[1503418874780, -2, 0],
[1503418874801, -2, 0],
[1503418874822, -2, 0],
[1503418874832, -2, 0],
[1503418874845, -2, 0],
[1503418875122, -2, 0],
[1503418875158, -2, 0],
[1503418875180, -2, 0],
[1503418875195, -4, 0],
[1503418875239, -2, 0],
[1503418875260, -2, 0],
[1503418875490, -2, 0],
[1503418875525, -2, 0],
[1503418875547, -4, 0],
[1503418875556, -4, 0],
[1503418875630, -2, 0],
[1503418875852, -2, 0],
[1503418875895, -2, 0],
[1503418875935, -2, 0],
[1503418875941, -4, 0],
[1503418876198, -2, 0],
[1503418876242, -2, 0],
[1503418876270, -4, 0],
[1503418876279, -2, 0],
[1503418876333, -2, 0],
[1503418876342, -2, 0],
[1503418876585, -2, 0],
[1503418876609, -2, 0],
[1503418876623, -2, 0],
[1503418876644, -2, 0],
[1503418876646, -2, 0],
[1503418876678, -2, 0],
[1503418877330, -2, 0],
[1503418877354, -2, 0],
[1503418877368, -2, 0],
[1503418877397, -2, 0],
[1503418877411, -2, 0],
[1503418877748, -2, 0],
[1503418877756, -2, 0],
[1503418877778, -2, 0],
[1503418877793, -2, 0],
[1503418877807, -2, 0],
[1503418878091, -2, 0],
[1503418878133, -2, 0],
[1503418878137, -4, 0],
[1503418878181, -2, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, true);
}
});
test('Windows - Microsoft basic v2 physical wheel', () => {
const testData: IMouseWheelEvent[] = [
[1503418994564, -2, 0],
[1503418994643, -2, 0],
[1503418994676, -2, 0],
[1503418994691, -2, 0],
[1503418994727, -2, 0],
[1503418994799, -2, 0],
[1503418994850, -2, 0],
[1503418995259, -2, 0],
[1503418995321, -2, 0],
[1503418995328, -2, 0],
[1503418995343, -2, 0],
[1503418995402, -2, 0],
[1503418995454, -2, 0],
[1503418996052, -2, 0],
[1503418996095, -2, 0],
[1503418996107, -2, 0],
[1503418996120, -2, 0],
[1503418996146, -2, 0],
[1503418996471, -2, 0],
[1503418996530, -2, 0],
[1503418996549, -2, 0],
[1503418996561, -2, 0],
[1503418996571, -2, 0],
[1503418996636, -2, 0],
[1503418996936, -2, 0],
[1503418997002, -2, 0],
[1503418997006, -2, 0],
[1503418997043, -2, 0],
[1503418997045, -2, 0],
[1503418997092, -2, 0],
[1503418997357, -2, 0],
[1503418997394, -2, 0],
[1503418997410, -2, 0],
[1503418997426, -2, 0],
[1503418997442, -2, 0],
[1503418997486, -2, 0],
[1503418997757, -2, 0],
[1503418997807, -2, 0],
[1503418997813, -2, 0],
[1503418997850, -2, 0],
];
const classifier = new MouseWheelClassifier();
for (let i = 0, len = testData.length; i < len; i++) {
const [timestamp, deltaY, deltaX] = testData[i];
classifier.accept(timestamp, deltaX, deltaY);
const actual = classifier.isPhysicalMouseWheel();
assert.equal(actual, true);
}
});
});

View File

@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* 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 { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState';
suite('ScrollbarState', () => {
test('inflates slider size', () => {
let actual = new ScrollbarState(0, 14, 0, 339, 42423, 32787);
assert.equal(actual.getArrowSize(), 0);
assert.equal(actual.getScrollPosition(), 32787);
assert.equal(actual.getRectangleLargeSize(), 339);
assert.equal(actual.getRectangleSmallSize(), 14);
assert.equal(actual.isNeeded(), true);
assert.equal(actual.getSliderSize(), 20);
assert.equal(actual.getSliderPosition(), 249);
assert.equal(actual.getDesiredScrollPositionFromOffset(259), 32849);
actual.setScrollPosition(32849);
assert.equal(actual.getArrowSize(), 0);
assert.equal(actual.getScrollPosition(), 32849);
assert.equal(actual.getRectangleLargeSize(), 339);
assert.equal(actual.getRectangleSmallSize(), 14);
assert.equal(actual.isNeeded(), true);
assert.equal(actual.getSliderSize(), 20);
assert.equal(actual.getSliderPosition(), 249);
});
test('inflates slider size with arrows', () => {
let actual = new ScrollbarState(12, 14, 0, 339, 42423, 32787);
assert.equal(actual.getArrowSize(), 12);
assert.equal(actual.getScrollPosition(), 32787);
assert.equal(actual.getRectangleLargeSize(), 339);
assert.equal(actual.getRectangleSmallSize(), 14);
assert.equal(actual.isNeeded(), true);
assert.equal(actual.getSliderSize(), 20);
assert.equal(actual.getSliderPosition(), 230);
assert.equal(actual.getDesiredScrollPositionFromOffset(240 + 12), 32811);
actual.setScrollPosition(32811);
assert.equal(actual.getArrowSize(), 12);
assert.equal(actual.getScrollPosition(), 32811);
assert.equal(actual.getRectangleLargeSize(), 339);
assert.equal(actual.getRectangleSmallSize(), 14);
assert.equal(actual.isNeeded(), true);
assert.equal(actual.getSliderSize(), 20);
assert.equal(actual.getSliderPosition(), 230);
});
});

View File

@@ -0,0 +1,549 @@
/*---------------------------------------------------------------------------------------------
* 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 { Emitter } from 'vs/base/common/event';
import { SplitView, IView, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview';
import { Sash, SashState } from 'vs/base/browser/ui/sash/sash';
class TestView implements IView<number> {
private readonly _onDidChange = new Emitter<number | undefined>();
readonly onDidChange = this._onDidChange.event;
get minimumSize(): number { return this._minimumSize; }
set minimumSize(size: number) { this._minimumSize = size; this._onDidChange.fire(undefined); }
get maximumSize(): number { return this._maximumSize; }
set maximumSize(size: number) { this._maximumSize = size; this._onDidChange.fire(undefined); }
private _element: HTMLElement = document.createElement('div');
get element(): HTMLElement { this._onDidGetElement.fire(); return this._element; }
private readonly _onDidGetElement = new Emitter<void>();
readonly onDidGetElement = this._onDidGetElement.event;
private _size = 0;
get size(): number { return this._size; }
private _orthogonalSize: number | undefined = 0;
get orthogonalSize(): number | undefined { return this._orthogonalSize; }
private readonly _onDidLayout = new Emitter<{ size: number; orthogonalSize: number | undefined }>();
readonly onDidLayout = this._onDidLayout.event;
private readonly _onDidFocus = new Emitter<void>();
readonly onDidFocus = this._onDidFocus.event;
constructor(
private _minimumSize: number,
private _maximumSize: number,
readonly priority: LayoutPriority = LayoutPriority.Normal
) {
assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size');
}
layout(size: number, _offset: number, orthogonalSize: number | undefined): void {
this._size = size;
this._orthogonalSize = orthogonalSize;
this._onDidLayout.fire({ size, orthogonalSize });
}
focus(): void {
this._onDidFocus.fire();
}
dispose(): void {
this._onDidChange.dispose();
this._onDidGetElement.dispose();
this._onDidLayout.dispose();
this._onDidFocus.dispose();
}
}
function getSashes(splitview: SplitView): Sash[] {
return (splitview as any).sashItems.map((i: any) => i.sash) as Sash[];
}
suite('Splitview', () => {
let container: HTMLElement;
setup(() => {
container = document.createElement('div');
container.style.position = 'absolute';
container.style.width = `${200}px`;
container.style.height = `${200}px`;
});
test('empty splitview has empty DOM', () => {
const splitview = new SplitView(container);
assert.equal(container.firstElementChild!.firstElementChild!.childElementCount, 0, 'split view should be empty');
splitview.dispose();
});
test('has views and sashes as children', () => {
const view1 = new TestView(20, 20);
const view2 = new TestView(20, 20);
const view3 = new TestView(20, 20);
const splitview = new SplitView(container);
splitview.addView(view1, 20);
splitview.addView(view2, 20);
splitview.addView(view3, 20);
let viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 3, 'split view should have 3 views');
let sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
assert.equal(sashQuery.length, 2, 'split view should have 2 sashes');
splitview.removeView(2);
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 2, 'split view should have 2 views');
sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
assert.equal(sashQuery.length, 1, 'split view should have 1 sash');
splitview.removeView(0);
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 1, 'split view should have 1 view');
sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
assert.equal(sashQuery.length, 0, 'split view should have no sashes');
splitview.removeView(0);
viewQuery = container.querySelectorAll('.monaco-split-view2 > .split-view-container > .split-view-view');
assert.equal(viewQuery.length, 0, 'split view should have no views');
sashQuery = container.querySelectorAll('.monaco-split-view2 > .sash-container > .monaco-sash');
assert.equal(sashQuery.length, 0, 'split view should have no sashes');
splitview.dispose();
view1.dispose();
view2.dispose();
view3.dispose();
});
test('calls view methods on addView and removeView', () => {
const view = new TestView(20, 20);
const splitview = new SplitView(container);
let didLayout = false;
const layoutDisposable = view.onDidLayout(() => didLayout = true);
const renderDisposable = view.onDidGetElement(() => undefined);
splitview.addView(view, 20);
assert.equal(view.size, 20, 'view has right size');
assert(didLayout, 'layout is called');
assert(didLayout, 'render is called');
splitview.dispose();
layoutDisposable.dispose();
renderDisposable.dispose();
view.dispose();
});
test('stretches view to viewport', () => {
const view = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view, 20);
assert.equal(view.size, 200, 'view is stretched');
splitview.layout(200);
assert.equal(view.size, 200, 'view stayed the same');
splitview.layout(100);
assert.equal(view.size, 100, 'view is collapsed');
splitview.layout(20);
assert.equal(view.size, 20, 'view is collapsed');
splitview.layout(10);
assert.equal(view.size, 20, 'view is clamped');
splitview.layout(200);
assert.equal(view.size, 200, 'view is stretched');
splitview.dispose();
view.dispose();
});
test('can resize views', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view1, 20);
splitview.addView(view2, 20);
splitview.addView(view3, 20);
assert.equal(view1.size, 160, 'view1 is stretched');
assert.equal(view2.size, 20, 'view2 size is 20');
assert.equal(view3.size, 20, 'view3 size is 20');
splitview.resizeView(1, 40);
assert.equal(view1.size, 140, 'view1 is collapsed');
assert.equal(view2.size, 40, 'view2 is stretched');
assert.equal(view3.size, 20, 'view3 stays the same');
splitview.resizeView(0, 70);
assert.equal(view1.size, 70, 'view1 is collapsed');
assert.equal(view2.size, 40, 'view2 stays the same');
assert.equal(view3.size, 90, 'view3 is stretched');
splitview.resizeView(2, 40);
assert.equal(view1.size, 70, 'view1 stays the same');
assert.equal(view2.size, 90, 'view2 is collapsed');
assert.equal(view3.size, 40, 'view3 is stretched');
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('reacts to view changes', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view1, 20);
splitview.addView(view2, 20);
splitview.addView(view3, 20);
assert.equal(view1.size, 160, 'view1 is stretched');
assert.equal(view2.size, 20, 'view2 size is 20');
assert.equal(view3.size, 20, 'view3 size is 20');
view1.maximumSize = 20;
assert.equal(view1.size, 20, 'view1 is collapsed');
assert.equal(view2.size, 20, 'view2 stays the same');
assert.equal(view3.size, 160, 'view3 is stretched');
view3.maximumSize = 40;
assert.equal(view1.size, 20, 'view1 stays the same');
assert.equal(view2.size, 140, 'view2 is stretched');
assert.equal(view3.size, 40, 'view3 is collapsed');
view2.maximumSize = 200;
assert.equal(view1.size, 20, 'view1 stays the same');
assert.equal(view2.size, 140, 'view2 stays the same');
assert.equal(view3.size, 40, 'view3 stays the same');
view3.maximumSize = Number.POSITIVE_INFINITY;
view3.minimumSize = 100;
assert.equal(view1.size, 20, 'view1 is collapsed');
assert.equal(view2.size, 80, 'view2 is collapsed');
assert.equal(view3.size, 100, 'view3 is stretched');
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('sashes are properly enabled/disabled', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
splitview.addView(view3, Sizing.Distribute);
let sashes = getSashes(splitview);
assert.equal(sashes.length, 2, 'there are two sashes');
assert.equal(sashes[0].state, SashState.Enabled, 'first sash is enabled');
assert.equal(sashes[1].state, SashState.Enabled, 'second sash is enabled');
splitview.layout(60);
assert.equal(sashes[0].state, SashState.Disabled, 'first sash is disabled');
assert.equal(sashes[1].state, SashState.Disabled, 'second sash is disabled');
splitview.layout(20);
assert.equal(sashes[0].state, SashState.Disabled, 'first sash is disabled');
assert.equal(sashes[1].state, SashState.Disabled, 'second sash is disabled');
splitview.layout(200);
assert.equal(sashes[0].state, SashState.Enabled, 'first sash is enabled');
assert.equal(sashes[1].state, SashState.Enabled, 'second sash is enabled');
view1.maximumSize = 20;
assert.equal(sashes[0].state, SashState.Disabled, 'first sash is disabled');
assert.equal(sashes[1].state, SashState.Enabled, 'second sash is enabled');
view2.maximumSize = 20;
assert.equal(sashes[0].state, SashState.Disabled, 'first sash is disabled');
assert.equal(sashes[1].state, SashState.Disabled, 'second sash is disabled');
view1.maximumSize = 300;
assert.equal(sashes[0].state, SashState.Minimum, 'first sash is enabled');
assert.equal(sashes[1].state, SashState.Minimum, 'second sash is enabled');
view2.maximumSize = 200;
assert.equal(sashes[0].state, SashState.Minimum, 'first sash is enabled');
assert.equal(sashes[1].state, SashState.Minimum, 'second sash is enabled');
splitview.resizeView(0, 40);
assert.equal(sashes[0].state, SashState.Enabled, 'first sash is enabled');
assert.equal(sashes[1].state, SashState.Enabled, 'second sash is enabled');
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('issue #35497', () => {
const view1 = new TestView(160, Number.POSITIVE_INFINITY);
const view2 = new TestView(66, 66);
const splitview = new SplitView(container);
splitview.layout(986);
splitview.addView(view1, 142, 0);
assert.equal(view1.size, 986, 'first view is stretched');
view2.onDidGetElement(() => {
assert.throws(() => splitview.resizeView(1, 922));
assert.throws(() => splitview.resizeView(1, 922));
});
splitview.addView(view2, 66, 0);
assert.equal(view2.size, 66, 'second view is fixed');
assert.equal(view1.size, 986 - 66, 'first view is collapsed');
const viewContainers = container.querySelectorAll('.split-view-view');
assert.equal(viewContainers.length, 2, 'there are two view containers');
assert.equal((viewContainers.item(0) as HTMLElement).style.height, '66px', 'second view container is 66px');
assert.equal((viewContainers.item(1) as HTMLElement).style.height, `${986 - 66}px`, 'first view container is 66px');
splitview.dispose();
view2.dispose();
view1.dispose();
});
test('automatic size distribution', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
assert.equal(view1.size, 200);
splitview.addView(view2, 50);
assert.deepEqual([view1.size, view2.size], [150, 50]);
splitview.addView(view3, Sizing.Distribute);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 66, 68]);
splitview.removeView(1, Sizing.Distribute);
assert.deepEqual([view1.size, view3.size], [100, 100]);
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('add views before layout', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.addView(view1, 100);
splitview.addView(view2, 75);
splitview.addView(view3, 25);
splitview.layout(200);
assert.deepEqual([view1.size, view2.size, view3.size], [67, 67, 66]);
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('split sizing', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
assert.equal(view1.size, 200);
splitview.addView(view2, Sizing.Split(0));
assert.deepEqual([view1.size, view2.size], [100, 100]);
splitview.addView(view3, Sizing.Split(1));
assert.deepEqual([view1.size, view2.size, view3.size], [100, 50, 50]);
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('split sizing 2', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
assert.equal(view1.size, 200);
splitview.addView(view2, Sizing.Split(0));
assert.deepEqual([view1.size, view2.size], [100, 100]);
splitview.addView(view3, Sizing.Split(0));
assert.deepEqual([view1.size, view2.size, view3.size], [50, 100, 50]);
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('proportional layout', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container);
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
assert.deepEqual([view1.size, view2.size], [100, 100]);
splitview.layout(100);
assert.deepEqual([view1.size, view2.size], [50, 50]);
splitview.dispose();
view2.dispose();
view1.dispose();
});
test('disable proportional layout', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container, { proportionalLayout: false });
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
assert.deepEqual([view1.size, view2.size], [100, 100]);
splitview.layout(100);
assert.deepEqual([view1.size, view2.size], [80, 20]);
splitview.dispose();
view2.dispose();
view1.dispose();
});
test('high layout priority', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.High);
const view3 = new TestView(20, Number.POSITIVE_INFINITY);
const splitview = new SplitView(container, { proportionalLayout: false });
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
splitview.addView(view3, Sizing.Distribute);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 68, 66]);
splitview.layout(180);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 48, 66]);
splitview.layout(124);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 20, 38]);
splitview.layout(60);
assert.deepEqual([view1.size, view2.size, view3.size], [20, 20, 20]);
splitview.layout(200);
assert.deepEqual([view1.size, view2.size, view3.size], [20, 160, 20]);
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('low layout priority', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low);
const splitview = new SplitView(container, { proportionalLayout: false });
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
splitview.addView(view3, Sizing.Distribute);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 68, 66]);
splitview.layout(180);
assert.deepEqual([view1.size, view2.size, view3.size], [66, 48, 66]);
splitview.layout(132);
assert.deepEqual([view1.size, view2.size, view3.size], [46, 20, 66]);
splitview.layout(60);
assert.deepEqual([view1.size, view2.size, view3.size], [20, 20, 20]);
splitview.layout(200);
assert.deepEqual([view1.size, view2.size, view3.size], [20, 160, 20]);
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
test('context propagates to views', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low);
const splitview = new SplitView<number>(container, { proportionalLayout: false });
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);
splitview.addView(view2, Sizing.Distribute);
splitview.addView(view3, Sizing.Distribute);
splitview.layout(200, 100);
assert.deepEqual([view1.orthogonalSize, view2.orthogonalSize, view3.orthogonalSize], [100, 100, 100]);
splitview.dispose();
view3.dispose();
view2.dispose();
view1.dispose();
});
});

View File

@@ -0,0 +1,438 @@
/*---------------------------------------------------------------------------------------------
* 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 { ITreeNode, ITreeRenderer, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { timeout } from 'vs/base/common/async';
interface Element {
id: string;
suffix?: string;
children?: Element[];
}
function find(element: Element, id: string): Element | undefined {
if (element.id === id) {
return element;
}
if (!element.children) {
return undefined;
}
for (const child of element.children) {
const result = find(child, id);
if (result) {
return result;
}
}
return undefined;
}
class Renderer implements ITreeRenderer<Element, void, HTMLElement> {
readonly templateId = 'default';
renderTemplate(container: HTMLElement): HTMLElement {
return container;
}
renderElement(element: ITreeNode<Element, void>, index: number, templateData: HTMLElement): void {
templateData.textContent = element.element.id + (element.element.suffix || '');
}
disposeTemplate(templateData: HTMLElement): void {
// noop
}
}
class IdentityProvider implements IIdentityProvider<Element> {
getId(element: Element) {
return element.id;
}
}
class VirtualDelegate implements IListVirtualDelegate<Element> {
getHeight() { return 20; }
getTemplateId(element: Element): string { return 'default'; }
}
class DataSource implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
return Promise.resolve(element.children || []);
}
}
class Model {
constructor(readonly root: Element) { }
get(id: string): Element {
const result = find(this.root, id);
if (!result) {
throw new Error('element not found');
}
return result;
}
}
suite('AsyncDataTree', function () {
test('Collapse state should be preserved across refresh calls', async () => {
const container = document.createElement('div');
const model = new Model({
id: 'root',
children: [{
id: 'a'
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
tree.layout(200);
assert.equal(container.querySelectorAll('.monaco-list-row').length, 0);
await tree.setInput(model.root);
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(!twistie.classList.contains('collapsible'));
assert(!twistie.classList.contains('collapsed'));
model.get('a').children = [
{ id: 'aa' },
{ id: 'ab' },
{ id: 'ac' }
];
await tree.updateChildren(model.root);
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
await tree.expand(model.get('a'));
assert.equal(container.querySelectorAll('.monaco-list-row').length, 4);
model.get('a').children = [];
await tree.updateChildren(model.root);
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
});
test('issue #68648', async () => {
const container = document.createElement('div');
const getChildrenCalls: string[] = [];
const dataSource = new class implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
getChildrenCalls.push(element.id);
return Promise.resolve(element.children || []);
}
};
const model = new Model({
id: 'root',
children: [{
id: 'a'
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
tree.layout(200);
await tree.setInput(model.root);
assert.deepStrictEqual(getChildrenCalls, ['root']);
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(!twistie.classList.contains('collapsible'));
assert(!twistie.classList.contains('collapsed'));
assert(tree.getNode().children[0].collapsed);
model.get('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }];
await tree.updateChildren(model.root);
assert.deepStrictEqual(getChildrenCalls, ['root', 'root']);
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(twistie.classList.contains('collapsible'));
assert(twistie.classList.contains('collapsed'));
assert(tree.getNode().children[0].collapsed);
model.get('a').children = [];
await tree.updateChildren(model.root);
assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root']);
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(!twistie.classList.contains('collapsible'));
assert(!twistie.classList.contains('collapsed'));
assert(tree.getNode().children[0].collapsed);
model.get('a').children = [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }];
await tree.updateChildren(model.root);
assert.deepStrictEqual(getChildrenCalls, ['root', 'root', 'root', 'root']);
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(twistie.classList.contains('collapsible'));
assert(twistie.classList.contains('collapsed'));
assert(tree.getNode().children[0].collapsed);
});
test('issue #67722 - once resolved, refreshed collapsed nodes should only get children when expanded', async () => {
const container = document.createElement('div');
const getChildrenCalls: string[] = [];
const dataSource = new class implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
getChildrenCalls.push(element.id);
return Promise.resolve(element.children || []);
}
};
const model = new Model({
id: 'root',
children: [{
id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
tree.layout(200);
await tree.setInput(model.root);
assert(tree.getNode(model.get('a')).collapsed);
assert.deepStrictEqual(getChildrenCalls, ['root']);
await tree.expand(model.get('a'));
assert(!tree.getNode(model.get('a')).collapsed);
assert.deepStrictEqual(getChildrenCalls, ['root', 'a']);
tree.collapse(model.get('a'));
assert(tree.getNode(model.get('a')).collapsed);
assert.deepStrictEqual(getChildrenCalls, ['root', 'a']);
await tree.updateChildren();
assert(tree.getNode(model.get('a')).collapsed);
assert.deepStrictEqual(getChildrenCalls, ['root', 'a', 'root'], 'a should not be refreshed, since it\' collapsed');
});
test('resolved collapsed nodes which lose children should lose twistie as well', async () => {
const container = document.createElement('div');
const model = new Model({
id: 'root',
children: [{
id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
tree.layout(200);
await tree.setInput(model.root);
await tree.expand(model.get('a'));
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(twistie.classList.contains('collapsible'));
assert(!twistie.classList.contains('collapsed'));
assert(!tree.getNode(model.get('a')).collapsed);
tree.collapse(model.get('a'));
model.get('a').children = [];
await tree.updateChildren(model.root);
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(!twistie.classList.contains('collapsible'));
assert(!twistie.classList.contains('collapsed'));
assert(tree.getNode(model.get('a')).collapsed);
});
test('support default collapse state per element', async () => {
const container = document.createElement('div');
const getChildrenCalls: string[] = [];
const dataSource = new class implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
getChildrenCalls.push(element.id);
return Promise.resolve(element.children || []);
}
};
const model = new Model({
id: 'root',
children: [{
id: 'a', children: [{ id: 'aa' }, { id: 'ab' }, { id: 'ac' }]
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, {
collapseByDefault: el => el.id !== 'a'
});
tree.layout(200);
await tree.setInput(model.root);
assert(!tree.getNode(model.get('a')).collapsed);
assert.deepStrictEqual(getChildrenCalls, ['root', 'a']);
});
test('issue #80098 - concurrent refresh and expand', async () => {
const container = document.createElement('div');
const calls: Function[] = [];
const dataSource = new class implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
return new Promise(c => calls.push(() => c(element.children || [])));
}
};
const model = new Model({
id: 'root',
children: [{
id: 'a', children: [{
id: 'aa'
}]
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
tree.layout(200);
const pSetInput = tree.setInput(model.root);
calls.pop()!(); // resolve getChildren(root)
await pSetInput;
const pUpdateChildrenA = tree.updateChildren(model.get('a'));
const pExpandA = tree.expand(model.get('a'));
assert.equal(calls.length, 1, 'expand(a) still hasn\'t called getChildren(a)');
calls.pop()!();
assert.equal(calls.length, 0, 'no pending getChildren calls');
await pUpdateChildrenA;
assert.equal(calls.length, 0, 'expand(a) should not have forced a second refresh');
const result = await pExpandA;
assert.equal(result, true, 'expand(a) should be done');
});
test('issue #80098 - first expand should call getChildren', async () => {
const container = document.createElement('div');
const calls: Function[] = [];
const dataSource = new class implements IAsyncDataSource<Element, Element> {
hasChildren(element: Element): boolean {
return !!element.children && element.children.length > 0;
}
getChildren(element: Element): Promise<Element[]> {
return new Promise(c => calls.push(() => c(element.children || [])));
}
};
const model = new Model({
id: 'root',
children: [{
id: 'a', children: [{
id: 'aa'
}]
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() });
tree.layout(200);
const pSetInput = tree.setInput(model.root);
calls.pop()!(); // resolve getChildren(root)
await pSetInput;
const pExpandA = tree.expand(model.get('a'));
assert.equal(calls.length, 1, 'expand(a) should\'ve called getChildren(a)');
let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
assert.equal(race, 'timeout', 'expand(a) should not be yet done');
calls.pop()!();
assert.equal(calls.length, 0, 'no pending getChildren calls');
race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]);
assert.equal(race, 'expand', 'expand(a) should now be done');
});
test('issue #78388 - tree should react to hasChildren toggles', async () => {
const container = document.createElement('div');
const model = new Model({
id: 'root',
children: [{
id: 'a'
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
tree.layout(200);
await tree.setInput(model.root);
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
let twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(!twistie.classList.contains('collapsible'));
assert(!twistie.classList.contains('collapsed'));
model.get('a').children = [{ id: 'aa' }];
await tree.updateChildren(model.get('a'), false);
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(twistie.classList.contains('collapsible'));
assert(twistie.classList.contains('collapsed'));
model.get('a').children = [];
await tree.updateChildren(model.get('a'), false);
assert.equal(container.querySelectorAll('.monaco-list-row').length, 1);
twistie = container.querySelector('.monaco-list-row:first-child .monaco-tl-twistie') as HTMLElement;
assert(!twistie.classList.contains('collapsible'));
assert(!twistie.classList.contains('collapsed'));
});
test('issues #84569, #82629 - rerender', async () => {
const container = document.createElement('div');
const model = new Model({
id: 'root',
children: [{
id: 'a',
children: [{
id: 'b',
suffix: '1'
}]
}]
});
const tree = new AsyncDataTree<Element, Element>('test', container, new VirtualDelegate(), [new Renderer()], new DataSource(), { identityProvider: new IdentityProvider() });
tree.layout(200);
await tree.setInput(model.root);
await tree.expand(model.get('a'));
assert.deepEqual(Array.from(container.querySelectorAll('.monaco-list-row')).map(e => e.textContent), ['a', 'b1']);
const a = model.get('a');
const b = model.get('b');
a.children?.splice(0, 1, { id: 'b', suffix: '2' });
await Promise.all([
tree.updateChildren(a, true, true),
tree.updateChildren(b, true, true)
]);
assert.deepEqual(Array.from(container.querySelectorAll('.monaco-list-row')).map(e => e.textContent), ['a', 'b2']);
});
});

View File

@@ -0,0 +1,431 @@
/*---------------------------------------------------------------------------------------------
* 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 { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { Iterable } from 'vs/base/common/iterator';
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
interface IResolvedCompressedTreeElement<T> extends ICompressedTreeElement<T> {
readonly element: T;
readonly children?: ICompressedTreeElement<T>[];
}
function resolve<T>(treeElement: ICompressedTreeElement<T>): IResolvedCompressedTreeElement<T> {
const result: any = { element: treeElement.element };
const children = [...Iterable.map(Iterable.from(treeElement.children), resolve)];
if (treeElement.incompressible) {
result.incompressible = true;
}
if (children.length > 0) {
result.children = children;
}
return result;
}
suite('CompressedObjectTree', function () {
suite('compress & decompress', function () {
test('small', function () {
const decompressed: ICompressedTreeElement<number> = { element: 1 };
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> =
{ element: { elements: [1], incompressible: false } };
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('no compression', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{ element: 11 },
{ element: 12 },
{ element: 13 }
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1], incompressible: false },
children: [
{ element: { elements: [11], incompressible: false } },
{ element: { elements: [12], incompressible: false } },
{ element: { elements: [13], incompressible: false } }
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('single hierarchy', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1111 }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11, 111, 1111], incompressible: false }
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('deep compression', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1111 },
{ element: 1112 },
{ element: 1113 },
{ element: 1114 },
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11, 111], incompressible: false },
children: [
{ element: { elements: [1111], incompressible: false } },
{ element: { elements: [1112], incompressible: false } },
{ element: { elements: [1113], incompressible: false } },
{ element: { elements: [1114], incompressible: false } },
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('double deep compression', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1112 },
{ element: 1113 },
]
}
]
},
{
element: 12, children: [
{
element: 121, children: [
{ element: 1212 },
{ element: 1213 },
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1], incompressible: false },
children: [
{
element: { elements: [11, 111], incompressible: false },
children: [
{ element: { elements: [1112], incompressible: false } },
{ element: { elements: [1113], incompressible: false } },
]
},
{
element: { elements: [12, 121], incompressible: false },
children: [
{ element: { elements: [1212], incompressible: false } },
{ element: { elements: [1213], incompressible: false } },
]
}
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible leaf', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, children: [
{ element: 1111, incompressible: true }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11, 111], incompressible: false },
children: [
{ element: { elements: [1111], incompressible: true } }
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible branch', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, incompressible: true, children: [
{ element: 1111 }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11], incompressible: false },
children: [
{ element: { elements: [111, 1111], incompressible: true } }
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible chain', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, children: [
{
element: 111, incompressible: true, children: [
{ element: 1111, incompressible: true }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1, 11], incompressible: false },
children: [
{
element: { elements: [111], incompressible: true },
children: [
{ element: { elements: [1111], incompressible: true } }
]
}
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
test('incompressible tree', function () {
const decompressed: ICompressedTreeElement<number> = {
element: 1, children: [
{
element: 11, incompressible: true, children: [
{
element: 111, incompressible: true, children: [
{ element: 1111, incompressible: true }
]
}
]
}
]
};
const compressed: IResolvedCompressedTreeElement<ICompressedTreeNode<number>> = {
element: { elements: [1], incompressible: false },
children: [
{
element: { elements: [11], incompressible: true },
children: [
{
element: { elements: [111], incompressible: true },
children: [
{ element: { elements: [1111], incompressible: true } }
]
}
]
}
]
};
assert.deepEqual(resolve(compress(decompressed)), compressed);
assert.deepEqual(resolve(decompress(compressed)), decompressed);
});
});
function toList<T>(arr: T[]): IList<T> {
return {
splice(start: number, deleteCount: number, elements: T[]): void {
arr.splice(start, deleteCount, ...elements);
},
updateElementHeight() { }
};
}
function toArray<T>(list: ITreeNode<ICompressedTreeNode<T>>[]): T[][] {
return list.map(i => i.element.elements);
}
suite('CompressedObjectTreeModel', function () {
test('ctor', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toList(list));
assert(model);
assert.equal(list.length, 0);
assert.equal(model.size, 0);
});
test('flat', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{ element: 0 },
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(toArray(list), [[0], [1], [2]]);
assert.equal(model.size, 3);
model.setChildren(null, [
{ element: 3 },
{ element: 4 },
{ element: 5 },
]);
assert.deepEqual(toArray(list), [[3], [4], [5]]);
assert.equal(model.size, 3);
model.setChildren(null);
assert.deepEqual(toArray(list), []);
assert.equal(model.size, 0);
});
test('nested', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(toArray(list), [[0], [10], [11], [12], [1], [2]]);
assert.equal(model.size, 6);
model.setChildren(12, [
{ element: 120 },
{ element: 121 }
]);
assert.deepEqual(toArray(list), [[0], [10], [11], [12], [120], [121], [1], [2]]);
assert.equal(model.size, 8);
model.setChildren(0);
assert.deepEqual(toArray(list), [[0], [1], [2]]);
assert.equal(model.size, 3);
model.setChildren(null);
assert.deepEqual(toArray(list), []);
assert.equal(model.size, 0);
});
test('compressed', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{
element: 1, children: [{
element: 11, children: [{
element: 111, children: [
{ element: 1111 },
{ element: 1112 },
{ element: 1113 },
]
}]
}]
}
]);
assert.deepEqual(toArray(list), [[1, 11, 111], [1111], [1112], [1113]]);
assert.equal(model.size, 6);
model.setChildren(11, [
{ element: 111 },
{ element: 112 },
{ element: 113 },
]);
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113]]);
assert.equal(model.size, 5);
model.setChildren(113, [
{ element: 1131 }
]);
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131]]);
assert.equal(model.size, 6);
model.setChildren(1131, [
{ element: 1132 }
]);
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131, 1132]]);
assert.equal(model.size, 7);
model.setChildren(1131, [
{ element: 1132 },
{ element: 1133 },
]);
assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131], [1132], [1133]]);
assert.equal(model.size, 8);
});
});
});

View File

@@ -0,0 +1,148 @@
/*---------------------------------------------------------------------------------------------
* 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 { ITreeNode, ITreeRenderer, IDataSource } from 'vs/base/browser/ui/tree/tree';
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
interface E {
value: number;
children?: E[];
}
suite('DataTree', function () {
let tree: DataTree<E, E>;
const root: E = {
value: -1,
children: [
{ value: 0, children: [{ value: 10 }, { value: 11 }, { value: 12 }] },
{ value: 1 },
{ value: 2 },
]
};
const empty: E = {
value: -1,
children: []
};
setup(() => {
const container = document.createElement('div');
container.style.width = '200px';
container.style.height = '200px';
const delegate = new class implements IListVirtualDelegate<E> {
getHeight() { return 20; }
getTemplateId(): string { return 'default'; }
};
const renderer = new class implements ITreeRenderer<E, void, HTMLElement> {
readonly templateId = 'default';
renderTemplate(container: HTMLElement): HTMLElement {
return container;
}
renderElement(element: ITreeNode<E, void>, index: number, templateData: HTMLElement): void {
templateData.textContent = `${element.element.value}`;
}
disposeTemplate(): void { }
};
const dataSource = new class implements IDataSource<E, E> {
getChildren(element: E): E[] {
return element.children || [];
}
};
const identityProvider = new class implements IIdentityProvider<E> {
getId(element: E): { toString(): string; } {
return `${element.value}`;
}
};
tree = new DataTree<E, E>('test', container, delegate, [renderer], dataSource, {
identityProvider
});
tree.layout(200);
});
teardown(() => {
tree.dispose();
});
test('view state is lost implicitly', () => {
tree.setInput(root);
let navigator = tree.navigate();
assert.equal(navigator.next()!.value, 0);
assert.equal(navigator.next()!.value, 10);
assert.equal(navigator.next()!.value, 11);
assert.equal(navigator.next()!.value, 12);
assert.equal(navigator.next()!.value, 1);
assert.equal(navigator.next()!.value, 2);
assert.equal(navigator.next()!, null);
tree.collapse(root.children![0]);
navigator = tree.navigate();
assert.equal(navigator.next()!.value, 0);
assert.equal(navigator.next()!.value, 1);
assert.equal(navigator.next()!.value, 2);
assert.equal(navigator.next()!, null);
tree.setSelection([root.children![1]]);
tree.setFocus([root.children![2]]);
tree.setInput(empty);
tree.setInput(root);
navigator = tree.navigate();
assert.equal(navigator.next()!.value, 0);
assert.equal(navigator.next()!.value, 10);
assert.equal(navigator.next()!.value, 11);
assert.equal(navigator.next()!.value, 12);
assert.equal(navigator.next()!.value, 1);
assert.equal(navigator.next()!.value, 2);
assert.equal(navigator.next()!, null);
assert.deepEqual(tree.getSelection(), []);
assert.deepEqual(tree.getFocus(), []);
});
test('view state can be preserved', () => {
tree.setInput(root);
let navigator = tree.navigate();
assert.equal(navigator.next()!.value, 0);
assert.equal(navigator.next()!.value, 10);
assert.equal(navigator.next()!.value, 11);
assert.equal(navigator.next()!.value, 12);
assert.equal(navigator.next()!.value, 1);
assert.equal(navigator.next()!.value, 2);
assert.equal(navigator.next()!, null);
tree.collapse(root.children![0]);
navigator = tree.navigate();
assert.equal(navigator.next()!.value, 0);
assert.equal(navigator.next()!.value, 1);
assert.equal(navigator.next()!.value, 2);
assert.equal(navigator.next()!, null);
tree.setSelection([root.children![1]]);
tree.setFocus([root.children![2]]);
const viewState = tree.getViewState();
tree.setInput(empty);
tree.setInput(root, viewState);
navigator = tree.navigate();
assert.equal(navigator.next()!.value, 0);
assert.equal(navigator.next()!.value, 1);
assert.equal(navigator.next()!.value, 2);
assert.equal(navigator.next()!, null);
assert.deepEqual(tree.getSelection(), [root.children![1]]);
assert.deepEqual(tree.getFocus(), [root.children![2]]);
});
});

View File

@@ -0,0 +1,759 @@
/*---------------------------------------------------------------------------------------------
* 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 { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
import { IndexTreeModel, IIndexTreeNode, IList } from 'vs/base/browser/ui/tree/indexTreeModel';
function toList<T>(arr: T[]): IList<T> {
return {
splice(start: number, deleteCount: number, elements: T[]): void {
arr.splice(start, deleteCount, ...elements);
},
updateElementHeight() { }
};
}
function toArray<T>(list: ITreeNode<T>[]): T[] {
return list.map(i => i.element);
}
suite('IndexTreeModel', function () {
test('ctor', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
assert(model);
assert.equal(list.length, 0);
});
test('insert', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{ element: 0 },
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 3);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 1);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 1);
assert.deepEqual(list[2].element, 2);
assert.deepEqual(list[2].collapsed, false);
assert.deepEqual(list[2].depth, 1);
});
test('deep insert', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 6);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 2);
assert.deepEqual(list[2].element, 11);
assert.deepEqual(list[2].collapsed, false);
assert.deepEqual(list[2].depth, 2);
assert.deepEqual(list[3].element, 12);
assert.deepEqual(list[3].collapsed, false);
assert.deepEqual(list[3].depth, 2);
assert.deepEqual(list[4].element, 1);
assert.deepEqual(list[4].collapsed, false);
assert.deepEqual(list[4].depth, 1);
assert.deepEqual(list[5].element, 2);
assert.deepEqual(list[5].collapsed, false);
assert.deepEqual(list[5].depth, 1);
});
test('deep insert collapsed', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, collapsed: true, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 3);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsed, true);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 1);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 1);
assert.deepEqual(list[2].element, 2);
assert.deepEqual(list[2].collapsed, false);
assert.deepEqual(list[2].depth, 1);
});
test('delete', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{ element: 0 },
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 3);
model.splice([1], 1);
assert.deepEqual(list.length, 2);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 2);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 1);
model.splice([0], 2);
assert.deepEqual(list.length, 0);
});
test('nested delete', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 6);
model.splice([1], 2);
assert.deepEqual(list.length, 4);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 2);
assert.deepEqual(list[2].element, 11);
assert.deepEqual(list[2].collapsed, false);
assert.deepEqual(list[2].depth, 2);
assert.deepEqual(list[3].element, 12);
assert.deepEqual(list[3].collapsed, false);
assert.deepEqual(list[3].depth, 2);
});
test('deep delete', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 6);
model.splice([0], 1);
assert.deepEqual(list.length, 2);
assert.deepEqual(list[0].element, 1);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 2);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 1);
});
test('hidden delete', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, collapsed: true, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 3);
model.splice([0, 1], 1);
assert.deepEqual(list.length, 3);
model.splice([0, 0], 2);
assert.deepEqual(list.length, 3);
});
test('collapse', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 6);
model.setCollapsed([0], true);
assert.deepEqual(list.length, 3);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsed, true);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 1);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 1);
assert.deepEqual(list[2].element, 2);
assert.deepEqual(list[2].collapsed, false);
assert.deepEqual(list[2].depth, 1);
});
test('expand', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, collapsed: true, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(list.length, 3);
model.setCollapsed([0], false);
assert.deepEqual(list.length, 6);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[0].depth, 1);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(list[1].depth, 2);
assert.deepEqual(list[2].element, 11);
assert.deepEqual(list[2].collapsed, false);
assert.deepEqual(list[2].depth, 2);
assert.deepEqual(list[3].element, 12);
assert.deepEqual(list[3].collapsed, false);
assert.deepEqual(list[3].depth, 2);
assert.deepEqual(list[4].element, 1);
assert.deepEqual(list[4].collapsed, false);
assert.deepEqual(list[4].depth, 1);
assert.deepEqual(list[5].element, 2);
assert.deepEqual(list[5].collapsed, false);
assert.deepEqual(list[5].depth, 1);
});
test('collapse should recursively adjust visible count', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 1, children: [
{
element: 11, children: [
{ element: 111 }
]
}
]
},
{
element: 2, children: [
{ element: 21 }
]
}
]);
assert.deepEqual(list.length, 5);
assert.deepEqual(toArray(list), [1, 11, 111, 2, 21]);
model.setCollapsed([0, 0], true);
assert.deepEqual(list.length, 4);
assert.deepEqual(toArray(list), [1, 11, 2, 21]);
model.setCollapsed([1], true);
assert.deepEqual(list.length, 3);
assert.deepEqual(toArray(list), [1, 11, 2]);
});
test('setCollapsible', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, children: [
{ element: 10 }
]
}
]);
assert.deepEqual(list.length, 2);
model.setCollapsible([0], false);
assert.deepEqual(list.length, 2);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, false);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(model.setCollapsed([0], true), false);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, false);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(model.setCollapsed([0], false), false);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, false);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
model.setCollapsible([0], true);
assert.deepEqual(list.length, 2);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, true);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
assert.deepEqual(model.setCollapsed([0], true), true);
assert.deepEqual(list.length, 1);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, true);
assert.deepEqual(list[0].collapsed, true);
assert.deepEqual(model.setCollapsed([0], false), true);
assert.deepEqual(list[0].element, 0);
assert.deepEqual(list[0].collapsible, true);
assert.deepEqual(list[0].collapsed, false);
assert.deepEqual(list[1].element, 10);
assert.deepEqual(list[1].collapsible, false);
assert.deepEqual(list[1].collapsed, false);
});
test('simple filter', function () {
const list: ITreeNode<number>[] = [];
const filter = new class implements ITreeFilter<number> {
filter(element: number): TreeVisibility {
return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden;
}
};
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
element: 0, children: [
{ element: 1 },
{ element: 2 },
{ element: 3 },
{ element: 4 },
{ element: 5 },
{ element: 6 },
{ element: 7 }
]
}
]);
assert.deepEqual(list.length, 4);
assert.deepEqual(toArray(list), [0, 2, 4, 6]);
model.setCollapsed([0], true);
assert.deepEqual(toArray(list), [0]);
model.setCollapsed([0], false);
assert.deepEqual(toArray(list), [0, 2, 4, 6]);
});
test('recursive filter on initial model', function () {
const list: ITreeNode<number>[] = [];
const filter = new class implements ITreeFilter<number> {
filter(element: number): TreeVisibility {
return element === 0 ? TreeVisibility.Recurse : TreeVisibility.Hidden;
}
};
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
element: 0, children: [
{ element: 1 },
{ element: 2 }
]
}
]);
assert.deepEqual(toArray(list), []);
});
test('refilter', function () {
const list: ITreeNode<number>[] = [];
let shouldFilter = false;
const filter = new class implements ITreeFilter<number> {
filter(element: number): TreeVisibility {
return (!shouldFilter || element % 2 === 0) ? TreeVisibility.Visible : TreeVisibility.Hidden;
}
};
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
element: 0, children: [
{ element: 1 },
{ element: 2 },
{ element: 3 },
{ element: 4 },
{ element: 5 },
{ element: 6 },
{ element: 7 }
]
},
]);
assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]);
model.refilter();
assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]);
shouldFilter = true;
model.refilter();
assert.deepEqual(toArray(list), [0, 2, 4, 6]);
shouldFilter = false;
model.refilter();
assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]);
});
test('recursive filter', function () {
const list: ITreeNode<string>[] = [];
let query = new RegExp('');
const filter = new class implements ITreeFilter<string> {
filter(element: string): TreeVisibility {
return query.test(element) ? TreeVisibility.Visible : TreeVisibility.Recurse;
}
};
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{
element: 'vscode', children: [
{ element: '.build' },
{ element: 'git' },
{
element: 'github', children: [
{ element: 'calendar.yml' },
{ element: 'endgame' },
{ element: 'build.js' },
]
},
{
element: 'build', children: [
{ element: 'lib' },
{ element: 'gulpfile.js' }
]
}
]
},
]);
assert.deepEqual(list.length, 10);
query = /build/;
model.refilter();
assert.deepEqual(toArray(list), ['vscode', '.build', 'github', 'build.js', 'build']);
model.setCollapsed([0], true);
assert.deepEqual(toArray(list), ['vscode']);
model.setCollapsed([0], false);
assert.deepEqual(toArray(list), ['vscode', '.build', 'github', 'build.js', 'build']);
});
test('recursive filter with collapse', function () {
const list: ITreeNode<string>[] = [];
let query = new RegExp('');
const filter = new class implements ITreeFilter<string> {
filter(element: string): TreeVisibility {
return query.test(element) ? TreeVisibility.Visible : TreeVisibility.Recurse;
}
};
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{
element: 'vscode', children: [
{ element: '.build' },
{ element: 'git' },
{
element: 'github', children: [
{ element: 'calendar.yml' },
{ element: 'endgame' },
{ element: 'build.js' },
]
},
{
element: 'build', children: [
{ element: 'lib' },
{ element: 'gulpfile.js' }
]
}
]
},
]);
assert.deepEqual(list.length, 10);
query = /gulp/;
model.refilter();
assert.deepEqual(toArray(list), ['vscode', 'build', 'gulpfile.js']);
model.setCollapsed([0, 3], true);
assert.deepEqual(toArray(list), ['vscode', 'build']);
model.setCollapsed([0], true);
assert.deepEqual(toArray(list), ['vscode']);
});
test('recursive filter while collapsed', function () {
const list: ITreeNode<string>[] = [];
let query = new RegExp('');
const filter = new class implements ITreeFilter<string> {
filter(element: string): TreeVisibility {
return query.test(element) ? TreeVisibility.Visible : TreeVisibility.Recurse;
}
};
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{
element: 'vscode', collapsed: true, children: [
{ element: '.build' },
{ element: 'git' },
{
element: 'github', children: [
{ element: 'calendar.yml' },
{ element: 'endgame' },
{ element: 'build.js' },
]
},
{
element: 'build', children: [
{ element: 'lib' },
{ element: 'gulpfile.js' }
]
}
]
},
]);
assert.deepEqual(toArray(list), ['vscode']);
query = /gulp/;
model.refilter();
assert.deepEqual(toArray(list), ['vscode']);
model.setCollapsed([0], false);
assert.deepEqual(toArray(list), ['vscode', 'build', 'gulpfile.js']);
model.setCollapsed([0], true);
assert.deepEqual(toArray(list), ['vscode']);
query = new RegExp('');
model.refilter();
assert.deepEqual(toArray(list), ['vscode']);
model.setCollapsed([0], false);
assert.deepEqual(list.length, 10);
});
suite('getNodeLocation', function () {
test('simple', function () {
const list: IIndexTreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(model.getNodeLocation(list[0]), [0]);
assert.deepEqual(model.getNodeLocation(list[1]), [0, 0]);
assert.deepEqual(model.getNodeLocation(list[2]), [0, 1]);
assert.deepEqual(model.getNodeLocation(list[3]), [0, 2]);
assert.deepEqual(model.getNodeLocation(list[4]), [1]);
assert.deepEqual(model.getNodeLocation(list[5]), [2]);
});
test('with filter', function () {
const list: IIndexTreeNode<number>[] = [];
const filter = new class implements ITreeFilter<number> {
filter(element: number): TreeVisibility {
return element % 2 === 0 ? TreeVisibility.Visible : TreeVisibility.Hidden;
}
};
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
element: 0, children: [
{ element: 1 },
{ element: 2 },
{ element: 3 },
{ element: 4 },
{ element: 5 },
{ element: 6 },
{ element: 7 }
]
}
]);
assert.deepEqual(model.getNodeLocation(list[0]), [0]);
assert.deepEqual(model.getNodeLocation(list[1]), [0, 1]);
assert.deepEqual(model.getNodeLocation(list[2]), [0, 3]);
assert.deepEqual(model.getNodeLocation(list[3]), [0, 5]);
});
});
test('refilter with filtered out nodes', function () {
const list: ITreeNode<string>[] = [];
let query = new RegExp('');
const filter = new class implements ITreeFilter<string> {
filter(element: string): boolean {
return query.test(element);
}
};
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{ element: 'silver' },
{ element: 'gold' },
{ element: 'platinum' }
]);
assert.deepEqual(toArray(list), ['silver', 'gold', 'platinum']);
query = /platinum/;
model.refilter();
assert.deepEqual(toArray(list), ['platinum']);
model.splice([0], Number.POSITIVE_INFINITY, [
{ element: 'silver' },
{ element: 'gold' },
{ element: 'platinum' }
]);
assert.deepEqual(toArray(list), ['platinum']);
model.refilter();
assert.deepEqual(toArray(list), ['platinum']);
});
test('explicit hidden nodes should have renderNodeCount == 0, issue #83211', function () {
const list: ITreeNode<string>[] = [];
let query = new RegExp('');
const filter = new class implements ITreeFilter<string> {
filter(element: string): boolean {
return query.test(element);
}
};
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{ element: 'a', children: [{ element: 'aa' }] },
{ element: 'b', children: [{ element: 'bb' }] }
]);
assert.deepEqual(toArray(list), ['a', 'aa', 'b', 'bb']);
assert.deepEqual(model.getListIndex([0]), 0);
assert.deepEqual(model.getListIndex([0, 0]), 1);
assert.deepEqual(model.getListIndex([1]), 2);
assert.deepEqual(model.getListIndex([1, 0]), 3);
query = /b/;
model.refilter();
assert.deepEqual(toArray(list), ['b', 'bb']);
assert.deepEqual(model.getListIndex([0]), -1);
assert.deepEqual(model.getListIndex([0, 0]), -1);
assert.deepEqual(model.getListIndex([1]), 0);
assert.deepEqual(model.getListIndex([1, 0]), 1);
});
});

View File

@@ -0,0 +1,375 @@
/*---------------------------------------------------------------------------------------------
* 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 { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { ObjectTree, CompressibleObjectTree, ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree';
import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
suite('ObjectTree', function () {
suite('TreeNavigator', function () {
let tree: ObjectTree<number>;
let filter = (_: number) => true;
setup(() => {
const container = document.createElement('div');
container.style.width = '200px';
container.style.height = '200px';
const delegate = new class implements IListVirtualDelegate<number> {
getHeight() { return 20; }
getTemplateId(): string { return 'default'; }
};
const renderer = new class implements ITreeRenderer<number, void, HTMLElement> {
readonly templateId = 'default';
renderTemplate(container: HTMLElement): HTMLElement {
return container;
}
renderElement(element: ITreeNode<number, void>, index: number, templateData: HTMLElement): void {
templateData.textContent = `${element.element}`;
}
disposeTemplate(): void { }
};
tree = new ObjectTree<number>('test', container, delegate, [renderer], { filter: { filter: (el) => filter(el) } });
tree.layout(200);
});
teardown(() => {
tree.dispose();
filter = (_: number) => true;
});
test('should be able to navigate', () => {
tree.setChildren(null, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
const navigator = tree.navigate();
assert.equal(navigator.current(), null);
assert.equal(navigator.next(), 0);
assert.equal(navigator.current(), 0);
assert.equal(navigator.next(), 10);
assert.equal(navigator.current(), 10);
assert.equal(navigator.next(), 11);
assert.equal(navigator.current(), 11);
assert.equal(navigator.next(), 12);
assert.equal(navigator.current(), 12);
assert.equal(navigator.next(), 1);
assert.equal(navigator.current(), 1);
assert.equal(navigator.next(), 2);
assert.equal(navigator.current(), 2);
assert.equal(navigator.previous(), 1);
assert.equal(navigator.current(), 1);
assert.equal(navigator.previous(), 12);
assert.equal(navigator.previous(), 11);
assert.equal(navigator.previous(), 10);
assert.equal(navigator.previous(), 0);
assert.equal(navigator.previous(), null);
assert.equal(navigator.next(), 0);
assert.equal(navigator.next(), 10);
assert.equal(navigator.first(), 0);
assert.equal(navigator.last(), 2);
});
test('should skip collapsed nodes', () => {
tree.setChildren(null, [
{
element: 0, collapsed: true, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
const navigator = tree.navigate();
assert.equal(navigator.current(), null);
assert.equal(navigator.next(), 0);
assert.equal(navigator.next(), 1);
assert.equal(navigator.next(), 2);
assert.equal(navigator.next(), null);
assert.equal(navigator.previous(), 2);
assert.equal(navigator.previous(), 1);
assert.equal(navigator.previous(), 0);
assert.equal(navigator.previous(), null);
assert.equal(navigator.next(), 0);
assert.equal(navigator.first(), 0);
assert.equal(navigator.last(), 2);
});
test('should skip filtered elements', () => {
filter = el => el % 2 === 0;
tree.setChildren(null, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
const navigator = tree.navigate();
assert.equal(navigator.current(), null);
assert.equal(navigator.next(), 0);
assert.equal(navigator.next(), 10);
assert.equal(navigator.next(), 12);
assert.equal(navigator.next(), 2);
assert.equal(navigator.next(), null);
assert.equal(navigator.previous(), 2);
assert.equal(navigator.previous(), 12);
assert.equal(navigator.previous(), 10);
assert.equal(navigator.previous(), 0);
assert.equal(navigator.previous(), null);
assert.equal(navigator.next(), 0);
assert.equal(navigator.next(), 10);
assert.equal(navigator.first(), 0);
assert.equal(navigator.last(), 2);
});
test('should be able to start from node', () => {
tree.setChildren(null, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
const navigator = tree.navigate(1);
assert.equal(navigator.current(), 1);
assert.equal(navigator.next(), 2);
assert.equal(navigator.current(), 2);
assert.equal(navigator.previous(), 1);
assert.equal(navigator.current(), 1);
assert.equal(navigator.previous(), 12);
assert.equal(navigator.previous(), 11);
assert.equal(navigator.previous(), 10);
assert.equal(navigator.previous(), 0);
assert.equal(navigator.previous(), null);
assert.equal(navigator.next(), 0);
assert.equal(navigator.next(), 10);
assert.equal(navigator.first(), 0);
assert.equal(navigator.last(), 2);
});
});
test('traits are preserved according to string identity', function () {
const container = document.createElement('div');
container.style.width = '200px';
container.style.height = '200px';
const delegate = new class implements IListVirtualDelegate<number> {
getHeight() { return 20; }
getTemplateId(): string { return 'default'; }
};
const renderer = new class implements ITreeRenderer<number, void, HTMLElement> {
readonly templateId = 'default';
renderTemplate(container: HTMLElement): HTMLElement {
return container;
}
renderElement(element: ITreeNode<number, void>, index: number, templateData: HTMLElement): void {
templateData.textContent = `${element.element}`;
}
disposeTemplate(): void { }
};
const identityProvider = new class implements IIdentityProvider<number> {
getId(element: number): { toString(): string; } {
return `${element % 100}`;
}
};
const tree = new ObjectTree<number>('test', container, delegate, [renderer], { identityProvider });
tree.layout(200);
tree.setChildren(null, [{ element: 0 }, { element: 1 }, { element: 2 }, { element: 3 }]);
tree.setFocus([1]);
assert.deepStrictEqual(tree.getFocus(), [1]);
tree.setChildren(null, [{ element: 100 }, { element: 101 }, { element: 102 }, { element: 103 }]);
assert.deepStrictEqual(tree.getFocus(), [101]);
});
});
function toArray(list: NodeList): Node[] {
const result: Node[] = [];
list.forEach(node => result.push(node));
return result;
}
suite('CompressibleObjectTree', function () {
class Delegate implements IListVirtualDelegate<number> {
getHeight() { return 20; }
getTemplateId(): string { return 'default'; }
}
class Renderer implements ICompressibleTreeRenderer<number, void, HTMLElement> {
readonly templateId = 'default';
renderTemplate(container: HTMLElement): HTMLElement {
return container;
}
renderElement(node: ITreeNode<number, void>, _: number, templateData: HTMLElement): void {
templateData.textContent = `${node.element}`;
}
renderCompressedElements(node: ITreeNode<ICompressedTreeNode<number>, void>, _: number, templateData: HTMLElement): void {
templateData.textContent = `${node.element.elements.join('/')}`;
}
disposeTemplate(): void { }
}
test('empty', function () {
const container = document.createElement('div');
container.style.width = '200px';
container.style.height = '200px';
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
tree.layout(200);
const rows = toArray(container.querySelectorAll('.monaco-tl-contents'));
assert.equal(rows.length, 0);
});
test('simple', function () {
const container = document.createElement('div');
container.style.width = '200px';
container.style.height = '200px';
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
tree.layout(200);
tree.setChildren(null, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
const rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['0', '10', '11', '12', '1', '2']);
});
test('compressed', () => {
const container = document.createElement('div');
container.style.width = '200px';
container.style.height = '200px';
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
tree.layout(200);
tree.setChildren(null, [
{
element: 1, children: [{
element: 11, children: [{
element: 111, children: [
{ element: 1111 },
{ element: 1112 },
{ element: 1113 },
]
}]
}]
}
]);
let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']);
tree.setChildren(11, [
{ element: 111 },
{ element: 112 },
{ element: 113 },
]);
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1/11', '111', '112', '113']);
tree.setChildren(113, [
{ element: 1131 }
]);
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1/11', '111', '112', '113/1131']);
tree.setChildren(1131, [
{ element: 1132 }
]);
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1/11', '111', '112', '113/1131/1132']);
tree.setChildren(1131, [
{ element: 1132 },
{ element: 1133 },
]);
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1/11', '111', '112', '113/1131', '1132', '1133']);
});
test('enableCompression', () => {
const container = document.createElement('div');
container.style.width = '200px';
container.style.height = '200px';
const tree = new CompressibleObjectTree<number>('test', container, new Delegate(), [new Renderer()]);
tree.layout(200);
tree.setChildren(null, [
{
element: 1, children: [{
element: 11, children: [{
element: 111, children: [
{ element: 1111 },
{ element: 1112 },
{ element: 1113 },
]
}]
}]
}
]);
let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']);
tree.updateOptions({ compressionEnabled: false });
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1', '11', '111', '1111', '1112', '1113']);
tree.updateOptions({ compressionEnabled: true });
rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent);
assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']);
});
});

View File

@@ -0,0 +1,276 @@
/*---------------------------------------------------------------------------------------------
* 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 { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
function toList<T>(arr: T[]): IList<T> {
return {
splice(start: number, deleteCount: number, elements: T[]): void {
// console.log(`splice (${start}, ${deleteCount}, ${elements.length} [${elements.join(', ')}] )`); // debugging
arr.splice(start, deleteCount, ...elements);
},
updateElementHeight() { }
};
}
function toArray<T>(list: ITreeNode<T>[]): T[] {
return list.map(i => i.element);
}
suite('ObjectTreeModel', function () {
test('ctor', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toList(list));
assert(model);
assert.equal(list.length, 0);
assert.equal(model.size, 0);
});
test('flat', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{ element: 0 },
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(toArray(list), [0, 1, 2]);
assert.equal(model.size, 3);
model.setChildren(null, [
{ element: 3 },
{ element: 4 },
{ element: 5 },
]);
assert.deepEqual(toArray(list), [3, 4, 5]);
assert.equal(model.size, 3);
model.setChildren(null);
assert.deepEqual(toArray(list), []);
assert.equal(model.size, 0);
});
test('nested', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{
element: 0, children: [
{ element: 10 },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(toArray(list), [0, 10, 11, 12, 1, 2]);
assert.equal(model.size, 6);
model.setChildren(12, [
{ element: 120 },
{ element: 121 }
]);
assert.deepEqual(toArray(list), [0, 10, 11, 12, 120, 121, 1, 2]);
assert.equal(model.size, 8);
model.setChildren(0);
assert.deepEqual(toArray(list), [0, 1, 2]);
assert.equal(model.size, 3);
model.setChildren(null);
assert.deepEqual(toArray(list), []);
assert.equal(model.size, 0);
});
test('setChildren on collapsed node', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{ element: 0, collapsed: true }
]);
assert.deepEqual(toArray(list), [0]);
model.setChildren(0, [
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(toArray(list), [0]);
model.setCollapsed(0, false);
assert.deepEqual(toArray(list), [0, 1, 2]);
});
test('setChildren on expanded, unrevealed node', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{
element: 1, collapsed: true, children: [
{ element: 11, collapsed: false }
]
},
{ element: 2 }
]);
assert.deepEqual(toArray(list), [1, 2]);
model.setChildren(11, [
{ element: 111 },
{ element: 112 }
]);
assert.deepEqual(toArray(list), [1, 2]);
model.setCollapsed(1, false);
assert.deepEqual(toArray(list), [1, 11, 111, 112, 2]);
});
test('collapse state is preserved with strict identity', () => {
const list: ITreeNode<string>[] = [];
const model = new ObjectTreeModel<string>('test', toList(list), { collapseByDefault: true });
const data = [{ element: 'father', children: [{ element: 'child' }] }];
model.setChildren(null, data);
assert.deepEqual(toArray(list), ['father']);
model.setCollapsed('father', false);
assert.deepEqual(toArray(list), ['father', 'child']);
model.setChildren(null, data);
assert.deepEqual(toArray(list), ['father', 'child']);
const data2 = [{ element: 'father', children: [{ element: 'child' }] }, { element: 'uncle' }];
model.setChildren(null, data2);
assert.deepEqual(toArray(list), ['father', 'child', 'uncle']);
model.setChildren(null, [{ element: 'uncle' }]);
assert.deepEqual(toArray(list), ['uncle']);
model.setChildren(null, data2);
assert.deepEqual(toArray(list), ['father', 'uncle']);
model.setChildren(null, data);
assert.deepEqual(toArray(list), ['father']);
});
test('sorter', () => {
let compare: (a: string, b: string) => number = (a, b) => a < b ? -1 : 1;
const list: ITreeNode<string>[] = [];
const model = new ObjectTreeModel<string>('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } });
const data = [
{ element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] },
{ element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] },
{ element: 'bicycles', children: [{ element: 'dutch' }, { element: 'mountain' }, { element: 'electric' }] },
];
model.setChildren(null, data);
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']);
});
test('resort', () => {
let compare: (a: string, b: string) => number = () => 0;
const list: ITreeNode<string>[] = [];
const model = new ObjectTreeModel<string>('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } });
const data = [
{ element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] },
{ element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] },
{ element: 'bicycles', children: [{ element: 'dutch' }, { element: 'mountain' }, { element: 'electric' }] },
];
model.setChildren(null, data);
assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric']);
// lexicographical
compare = (a, b) => a < b ? -1 : 1;
// non-recursive
model.resort(null, false);
assert.deepEqual(toArray(list), ['airplanes', 'passenger', 'jet', 'bicycles', 'dutch', 'mountain', 'electric', 'cars', 'sedan', 'convertible', 'compact']);
// recursive
model.resort();
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'compact', 'convertible', 'sedan']);
// reverse
compare = (a, b) => a < b ? 1 : -1;
// scoped
model.resort('cars');
assert.deepEqual(toArray(list), ['airplanes', 'jet', 'passenger', 'bicycles', 'dutch', 'electric', 'mountain', 'cars', 'sedan', 'convertible', 'compact']);
// recursive
model.resort();
assert.deepEqual(toArray(list), ['cars', 'sedan', 'convertible', 'compact', 'bicycles', 'mountain', 'electric', 'dutch', 'airplanes', 'passenger', 'jet']);
});
test('expandTo', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toList(list), { collapseByDefault: true });
model.setChildren(null, [
{
element: 0, children: [
{ element: 10, children: [{ element: 100, children: [{ element: 1000 }] }] },
{ element: 11 },
{ element: 12 },
]
},
{ element: 1 },
{ element: 2 }
]);
assert.deepEqual(toArray(list), [0, 1, 2]);
model.expandTo(1000);
assert.deepEqual(toArray(list), [0, 10, 100, 1000, 11, 12, 1, 2]);
});
test('issue #95641', () => {
const list: ITreeNode<string>[] = [];
let fn = (_: string) => true;
const filter = new class implements ITreeFilter<string> {
filter(element: string, parentVisibility: TreeVisibility): TreeVisibility {
if (element === 'file') {
return TreeVisibility.Recurse;
}
return fn(element) ? TreeVisibility.Visible : parentVisibility;
}
};
const model = new ObjectTreeModel<string>('test', toList(list), { filter });
model.setChildren(null, [{ element: 'file', children: [{ element: 'hello' }] }]);
assert.deepEqual(toArray(list), ['file', 'hello']);
fn = (el: string) => el === 'world';
model.refilter();
assert.deepEqual(toArray(list), []);
model.setChildren('file', [{ element: 'world' }]);
assert.deepEqual(toArray(list), ['file', 'world']);
model.setChildren('file', [{ element: 'hello' }]);
assert.deepEqual(toArray(list), []);
model.setChildren('file', [{ element: 'world' }]);
assert.deepEqual(toArray(list), ['file', 'world']);
});
});

View File

@@ -0,0 +1,372 @@
/*---------------------------------------------------------------------------------------------
* 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 * as arrays from 'vs/base/common/arrays';
suite('Arrays', () => {
test('findFirst', () => {
const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69];
let idx = arrays.findFirstInSorted(array, e => e >= 0);
assert.equal(array[idx], 1);
idx = arrays.findFirstInSorted(array, e => e > 1);
assert.equal(array[idx], 4);
idx = arrays.findFirstInSorted(array, e => e >= 8);
assert.equal(array[idx], 55);
idx = arrays.findFirstInSorted(array, e => e >= 61);
assert.equal(array[idx], 61);
idx = arrays.findFirstInSorted(array, e => e >= 69);
assert.equal(array[idx], 69);
idx = arrays.findFirstInSorted(array, e => e >= 70);
assert.equal(idx, array.length);
idx = arrays.findFirstInSorted([], e => e >= 0);
assert.equal(array[idx], 1);
});
test('quickSelect', () => {
function assertMedian(expexted: number, data: number[], nth: number = Math.floor(data.length / 2)) {
const compare = (a: number, b: number) => a - b;
let actual1 = arrays.quickSelect(nth, data, compare);
assert.equal(actual1, expexted);
let actual2 = data.slice().sort(compare)[nth];
assert.equal(actual2, expexted);
}
assertMedian(5, [9, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5]);
assertMedian(8, [9, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5], 8);
assertMedian(8, [13, 4, 8]);
assertMedian(4, [13, 4, 8, 4, 4]);
assertMedian(13, [13, 4, 8], 2);
});
test('stableSort', () => {
function fill<T>(num: number, valueFn: () => T, arr: T[] = []): T[] {
for (let i = 0; i < num; i++) {
arr[i] = valueFn();
}
return arr;
}
let counter = 0;
let data = fill(10000, () => ({ n: 1, m: counter++ }));
arrays.mergeSort(data, (a, b) => a.n - b.n);
let lastM = -1;
for (const element of data) {
assert.ok(lastM < element.m);
lastM = element.m;
}
});
test('mergeSort', () => {
let data = arrays.mergeSort([6, 5, 3, 1, 8, 7, 2, 4], (a, b) => a - b);
assert.deepEqual(data, [1, 2, 3, 4, 5, 6, 7, 8]);
});
test('mergeSort, sorted array', function () {
let data = arrays.mergeSort([1, 2, 3, 4, 5, 6], (a, b) => a - b);
assert.deepEqual(data, [1, 2, 3, 4, 5, 6]);
});
test('mergeSort, is stable', function () {
let numbers = arrays.mergeSort([33, 22, 11, 4, 99, 1], (a, b) => 0);
assert.deepEqual(numbers, [33, 22, 11, 4, 99, 1]);
});
test('mergeSort, many random numbers', function () {
function compare(a: number, b: number) {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
}
function assertSorted(array: number[]) {
let last = array[0];
for (let i = 1; i < array.length; i++) {
let n = array[i];
if (last > n) {
assert.fail(JSON.stringify(array.slice(i - 10, i + 10)));
}
}
}
const MAX = 101;
const data: number[][] = [];
for (let i = 1; i < MAX; i++) {
let array: number[] = [];
for (let j = 0; j < 10 + i; j++) {
array.push(Math.random() * 10e8 | 0);
}
data.push(array);
}
for (const array of data) {
arrays.mergeSort(array, compare);
assertSorted(array);
}
});
test('sortedDiff', () => {
function compare(a: number, b: number): number {
return a - b;
}
let d = arrays.sortedDiff([1, 2, 4], [], compare);
assert.deepEqual(d, [
{ start: 0, deleteCount: 3, toInsert: [] }
]);
d = arrays.sortedDiff([], [1, 2, 4], compare);
assert.deepEqual(d, [
{ start: 0, deleteCount: 0, toInsert: [1, 2, 4] }
]);
d = arrays.sortedDiff([1, 2, 4], [1, 2, 4], compare);
assert.deepEqual(d, []);
d = arrays.sortedDiff([1, 2, 4], [2, 3, 4, 5], compare);
assert.deepEqual(d, [
{ start: 0, deleteCount: 1, toInsert: [] },
{ start: 2, deleteCount: 0, toInsert: [3] },
{ start: 3, deleteCount: 0, toInsert: [5] },
]);
d = arrays.sortedDiff([2, 3, 4, 5], [1, 2, 4], compare);
assert.deepEqual(d, [
{ start: 0, deleteCount: 0, toInsert: [1] },
{ start: 1, deleteCount: 1, toInsert: [] },
{ start: 3, deleteCount: 1, toInsert: [] },
]);
d = arrays.sortedDiff([1, 3, 5, 7], [5, 9, 11], compare);
assert.deepEqual(d, [
{ start: 0, deleteCount: 2, toInsert: [] },
{ start: 3, deleteCount: 1, toInsert: [9, 11] }
]);
d = arrays.sortedDiff([1, 3, 7], [5, 9, 11], compare);
assert.deepEqual(d, [
{ start: 0, deleteCount: 3, toInsert: [5, 9, 11] }
]);
});
test('delta sorted arrays', function () {
function compare(a: number, b: number): number {
return a - b;
}
let d = arrays.delta([1, 2, 4], [], compare);
assert.deepEqual(d.removed, [1, 2, 4]);
assert.deepEqual(d.added, []);
d = arrays.delta([], [1, 2, 4], compare);
assert.deepEqual(d.removed, []);
assert.deepEqual(d.added, [1, 2, 4]);
d = arrays.delta([1, 2, 4], [1, 2, 4], compare);
assert.deepEqual(d.removed, []);
assert.deepEqual(d.added, []);
d = arrays.delta([1, 2, 4], [2, 3, 4, 5], compare);
assert.deepEqual(d.removed, [1]);
assert.deepEqual(d.added, [3, 5]);
d = arrays.delta([2, 3, 4, 5], [1, 2, 4], compare);
assert.deepEqual(d.removed, [3, 5]);
assert.deepEqual(d.added, [1]);
d = arrays.delta([1, 3, 5, 7], [5, 9, 11], compare);
assert.deepEqual(d.removed, [1, 3, 7]);
assert.deepEqual(d.added, [9, 11]);
d = arrays.delta([1, 3, 7], [5, 9, 11], compare);
assert.deepEqual(d.removed, [1, 3, 7]);
assert.deepEqual(d.added, [5, 9, 11]);
});
test('binarySearch', () => {
function compare(a: number, b: number): number {
return a - b;
}
const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69];
assert.equal(arrays.binarySearch(array, 1, compare), 0);
assert.equal(arrays.binarySearch(array, 5, compare), 2);
// insertion point
assert.equal(arrays.binarySearch(array, 0, compare), ~0);
assert.equal(arrays.binarySearch(array, 6, compare), ~3);
assert.equal(arrays.binarySearch(array, 70, compare), ~10);
});
test('distinct', () => {
function compare(a: string): string {
return a;
}
assert.deepEqual(arrays.distinct(['32', '4', '5'], compare), ['32', '4', '5']);
assert.deepEqual(arrays.distinct(['32', '4', '5', '4'], compare), ['32', '4', '5']);
assert.deepEqual(arrays.distinct(['32', 'constructor', '5', '1'], compare), ['32', 'constructor', '5', '1']);
assert.deepEqual(arrays.distinct(['32', 'constructor', 'proto', 'proto', 'constructor'], compare), ['32', 'constructor', 'proto']);
assert.deepEqual(arrays.distinct(['32', '4', '5', '32', '4', '5', '32', '4', '5', '5'], compare), ['32', '4', '5']);
});
test('top', () => {
const cmp = (a: number, b: number) => {
assert.strictEqual(typeof a, 'number', 'typeof a');
assert.strictEqual(typeof b, 'number', 'typeof b');
return a - b;
};
assert.deepEqual(arrays.top([], cmp, 1), []);
assert.deepEqual(arrays.top([1], cmp, 0), []);
assert.deepEqual(arrays.top([1, 2], cmp, 1), [1]);
assert.deepEqual(arrays.top([2, 1], cmp, 1), [1]);
assert.deepEqual(arrays.top([1, 3, 2], cmp, 2), [1, 2]);
assert.deepEqual(arrays.top([3, 2, 1], cmp, 3), [1, 2, 3]);
assert.deepEqual(arrays.top([4, 6, 2, 7, 8, 3, 5, 1], cmp, 3), [1, 2, 3]);
});
test('topAsync', async () => {
const cmp = (a: number, b: number) => {
assert.strictEqual(typeof a, 'number', 'typeof a');
assert.strictEqual(typeof b, 'number', 'typeof b');
return a - b;
};
await testTopAsync(cmp, 1);
return testTopAsync(cmp, 2);
});
async function testTopAsync(cmp: any, m: number) {
{
const result = await arrays.topAsync([], cmp, 1, m);
assert.deepEqual(result, []);
}
{
const result = await arrays.topAsync([1], cmp, 0, m);
assert.deepEqual(result, []);
}
{
const result = await arrays.topAsync([1, 2], cmp, 1, m);
assert.deepEqual(result, [1]);
}
{
const result = await arrays.topAsync([2, 1], cmp, 1, m);
assert.deepEqual(result, [1]);
}
{
const result = await arrays.topAsync([1, 3, 2], cmp, 2, m);
assert.deepEqual(result, [1, 2]);
}
{
const result = await arrays.topAsync([3, 2, 1], cmp, 3, m);
assert.deepEqual(result, [1, 2, 3]);
}
{
const result = await arrays.topAsync([4, 6, 2, 7, 8, 3, 5, 1], cmp, 3, m);
assert.deepEqual(result, [1, 2, 3]);
}
}
test('coalesce', () => {
let a: Array<number | null> = arrays.coalesce([null, 1, null, 2, 3]);
assert.equal(a.length, 3);
assert.equal(a[0], 1);
assert.equal(a[1], 2);
assert.equal(a[2], 3);
arrays.coalesce([null, 1, null, undefined, undefined, 2, 3]);
assert.equal(a.length, 3);
assert.equal(a[0], 1);
assert.equal(a[1], 2);
assert.equal(a[2], 3);
let b: number[] = [];
b[10] = 1;
b[20] = 2;
b[30] = 3;
b = arrays.coalesce(b);
assert.equal(b.length, 3);
assert.equal(b[0], 1);
assert.equal(b[1], 2);
assert.equal(b[2], 3);
let sparse: number[] = [];
sparse[0] = 1;
sparse[1] = 1;
sparse[17] = 1;
sparse[1000] = 1;
sparse[1001] = 1;
assert.equal(sparse.length, 1002);
sparse = arrays.coalesce(sparse);
assert.equal(sparse.length, 5);
});
test('coalesce - inplace', function () {
let a: Array<number | null> = [null, 1, null, 2, 3];
arrays.coalesceInPlace(a);
assert.equal(a.length, 3);
assert.equal(a[0], 1);
assert.equal(a[1], 2);
assert.equal(a[2], 3);
a = [null, 1, null, undefined!, undefined!, 2, 3];
arrays.coalesceInPlace(a);
assert.equal(a.length, 3);
assert.equal(a[0], 1);
assert.equal(a[1], 2);
assert.equal(a[2], 3);
let b: number[] = [];
b[10] = 1;
b[20] = 2;
b[30] = 3;
arrays.coalesceInPlace(b);
assert.equal(b.length, 3);
assert.equal(b[0], 1);
assert.equal(b[1], 2);
assert.equal(b[2], 3);
let sparse: number[] = [];
sparse[0] = 1;
sparse[1] = 1;
sparse[17] = 1;
sparse[1000] = 1;
sparse[1001] = 1;
assert.equal(sparse.length, 1002);
arrays.coalesceInPlace(sparse);
assert.equal(sparse.length, 5);
});
test('insert, remove', function () {
const array: string[] = [];
const remove = arrays.insert(array, 'foo');
assert.equal(array[0], 'foo');
remove();
assert.equal(array.length, 0);
});
});

View File

@@ -0,0 +1,34 @@
/*---------------------------------------------------------------------------------------------
* 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 { ok } from 'vs/base/common/assert';
suite('Assert', () => {
test('ok', () => {
assert.throws(function () {
ok(false);
});
assert.throws(function () {
ok(null);
});
assert.throws(function () {
ok();
});
assert.throws(function () {
ok(null, 'Foo Bar');
}, function (e: Error) {
return e.message.indexOf('Foo Bar') >= 0;
});
ok(true);
ok('foo');
ok({});
ok(5);
});
});

View File

@@ -0,0 +1,722 @@
/*---------------------------------------------------------------------------------------------
* 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 * as async from 'vs/base/common/async';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
suite('Async', () => {
test('cancelablePromise - set token, don\'t wait for inner promise', function () {
let canceled = 0;
let promise = async.createCancelablePromise(token => {
token.onCancellationRequested(_ => { canceled += 1; });
return new Promise(resolve => { /*never*/ });
});
let result = promise.then(_ => assert.ok(false), err => {
assert.equal(canceled, 1);
assert.ok(isPromiseCanceledError(err));
});
promise.cancel();
promise.cancel(); // cancel only once
return result;
});
test('cancelablePromise - cancel despite inner promise being resolved', function () {
let canceled = 0;
let promise = async.createCancelablePromise(token => {
token.onCancellationRequested(_ => { canceled += 1; });
return Promise.resolve(1234);
});
let result = promise.then(_ => assert.ok(false), err => {
assert.equal(canceled, 1);
assert.ok(isPromiseCanceledError(err));
});
promise.cancel();
return result;
});
// Cancelling a sync cancelable promise will fire the cancelled token.
// Also, every `then` callback runs in another execution frame.
test('CancelablePromise execution order (sync)', function () {
const order: string[] = [];
const cancellablePromise = async.createCancelablePromise(token => {
order.push('in callback');
token.onCancellationRequested(_ => order.push('cancelled'));
return Promise.resolve(1234);
});
order.push('afterCreate');
const promise = cancellablePromise
.then(undefined, err => null)
.then(() => order.push('finally'));
cancellablePromise.cancel();
order.push('afterCancel');
return promise.then(() => assert.deepEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally']));
});
// Cancelling an async cancelable promise is just the same as a sync cancellable promise.
test('CancelablePromise execution order (async)', function () {
const order: string[] = [];
const cancellablePromise = async.createCancelablePromise(token => {
order.push('in callback');
token.onCancellationRequested(_ => order.push('cancelled'));
return new Promise(c => setTimeout(c.bind(1234), 0));
});
order.push('afterCreate');
const promise = cancellablePromise
.then(undefined, err => null)
.then(() => order.push('finally'));
cancellablePromise.cancel();
order.push('afterCancel');
return promise.then(() => assert.deepEqual(order, ['in callback', 'afterCreate', 'cancelled', 'afterCancel', 'finally']));
});
test('cancelablePromise - get inner result', async function () {
let promise = async.createCancelablePromise(token => {
return async.timeout(12).then(_ => 1234);
});
let result = await promise;
assert.equal(result, 1234);
});
test('Throttler - non async', function () {
let count = 0;
let factory = () => {
return Promise.resolve(++count);
};
let throttler = new async.Throttler();
return Promise.all([
throttler.queue(factory).then((result) => { assert.equal(result, 1); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); })
]).then(() => assert.equal(count, 2));
});
test('Throttler', () => {
let count = 0;
let factory = () => async.timeout(0).then(() => ++count);
let throttler = new async.Throttler();
return Promise.all([
throttler.queue(factory).then((result) => { assert.equal(result, 1); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); }),
throttler.queue(factory).then((result) => { assert.equal(result, 2); })
]).then(() => {
return Promise.all([
throttler.queue(factory).then((result) => { assert.equal(result, 3); }),
throttler.queue(factory).then((result) => { assert.equal(result, 4); }),
throttler.queue(factory).then((result) => { assert.equal(result, 4); }),
throttler.queue(factory).then((result) => { assert.equal(result, 4); }),
throttler.queue(factory).then((result) => { assert.equal(result, 4); })
]);
});
});
test('Throttler - last factory should be the one getting called', function () {
let factoryFactory = (n: number) => () => {
return async.timeout(0).then(() => n);
};
let throttler = new async.Throttler();
let promises: Promise<any>[] = [];
promises.push(throttler.queue(factoryFactory(1)).then((n) => { assert.equal(n, 1); }));
promises.push(throttler.queue(factoryFactory(2)).then((n) => { assert.equal(n, 3); }));
promises.push(throttler.queue(factoryFactory(3)).then((n) => { assert.equal(n, 3); }));
return Promise.all(promises);
});
test('Delayer', () => {
let count = 0;
let factory = () => {
return Promise.resolve(++count);
};
let delayer = new async.Delayer(0);
let promises: Promise<any>[] = [];
assert(!delayer.isTriggered());
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()); }));
assert(delayer.isTriggered());
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()); }));
assert(delayer.isTriggered());
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()); }));
assert(delayer.isTriggered());
return Promise.all(promises).then(() => {
assert(!delayer.isTriggered());
});
});
test('Delayer - simple cancel', function () {
let count = 0;
let factory = () => {
return Promise.resolve(++count);
};
let delayer = new async.Delayer(0);
assert(!delayer.isTriggered());
const p = delayer.trigger(factory).then(() => {
assert(false);
}, () => {
assert(true, 'yes, it was cancelled');
});
assert(delayer.isTriggered());
delayer.cancel();
assert(!delayer.isTriggered());
return p;
});
test('Delayer - cancel should cancel all calls to trigger', function () {
let count = 0;
let factory = () => {
return Promise.resolve(++count);
};
let delayer = new async.Delayer(0);
let promises: Promise<any>[] = [];
assert(!delayer.isTriggered());
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
assert(delayer.isTriggered());
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
assert(delayer.isTriggered());
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
assert(delayer.isTriggered());
delayer.cancel();
return Promise.all(promises).then(() => {
assert(!delayer.isTriggered());
});
});
test('Delayer - trigger, cancel, then trigger again', function () {
let count = 0;
let factory = () => {
return Promise.resolve(++count);
};
let delayer = new async.Delayer(0);
let promises: Promise<any>[] = [];
assert(!delayer.isTriggered());
const p = delayer.trigger(factory).then((result) => {
assert.equal(result, 1);
assert(!delayer.isTriggered());
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
assert(delayer.isTriggered());
promises.push(delayer.trigger(factory).then(undefined, () => { assert(true, 'yes, it was cancelled'); }));
assert(delayer.isTriggered());
delayer.cancel();
const p = Promise.all(promises).then(() => {
promises = [];
assert(!delayer.isTriggered());
promises.push(delayer.trigger(factory).then(() => { assert.equal(result, 1); assert(!delayer.isTriggered()); }));
assert(delayer.isTriggered());
promises.push(delayer.trigger(factory).then(() => { assert.equal(result, 1); assert(!delayer.isTriggered()); }));
assert(delayer.isTriggered());
const p = Promise.all(promises).then(() => {
assert(!delayer.isTriggered());
});
assert(delayer.isTriggered());
return p;
});
return p;
});
assert(delayer.isTriggered());
return p;
});
test('Delayer - last task should be the one getting called', function () {
let factoryFactory = (n: number) => () => {
return Promise.resolve(n);
};
let delayer = new async.Delayer(0);
let promises: Promise<any>[] = [];
assert(!delayer.isTriggered());
promises.push(delayer.trigger(factoryFactory(1)).then((n) => { assert.equal(n, 3); }));
promises.push(delayer.trigger(factoryFactory(2)).then((n) => { assert.equal(n, 3); }));
promises.push(delayer.trigger(factoryFactory(3)).then((n) => { assert.equal(n, 3); }));
const p = Promise.all(promises).then(() => {
assert(!delayer.isTriggered());
});
assert(delayer.isTriggered());
return p;
});
test('Sequence', () => {
let factoryFactory = (n: number) => () => {
return Promise.resolve(n);
};
return async.sequence([
factoryFactory(1),
factoryFactory(2),
factoryFactory(3),
factoryFactory(4),
factoryFactory(5),
]).then((result) => {
assert.equal(5, result.length);
assert.equal(1, result[0]);
assert.equal(2, result[1]);
assert.equal(3, result[2]);
assert.equal(4, result[3]);
assert.equal(5, result[4]);
});
});
test('Limiter - sync', function () {
let factoryFactory = (n: number) => () => {
return Promise.resolve(n);
};
let limiter = new async.Limiter(1);
let promises: Promise<any>[] = [];
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n))));
return Promise.all(promises).then((res) => {
assert.equal(10, res.length);
limiter = new async.Limiter(100);
promises = [];
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n))));
return Promise.all(promises).then((res) => {
assert.equal(10, res.length);
});
});
});
test('Limiter - async', function () {
let factoryFactory = (n: number) => () => async.timeout(0).then(() => n);
let limiter = new async.Limiter(1);
let promises: Promise<any>[] = [];
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n))));
return Promise.all(promises).then((res) => {
assert.equal(10, res.length);
limiter = new async.Limiter(100);
promises = [];
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n))));
return Promise.all(promises).then((res) => {
assert.equal(10, res.length);
});
});
});
test('Limiter - assert degree of paralellism', function () {
let activePromises = 0;
let factoryFactory = (n: number) => () => {
activePromises++;
assert(activePromises < 6);
return async.timeout(0).then(() => { activePromises--; return n; });
};
let limiter = new async.Limiter(5);
let promises: Promise<any>[] = [];
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].forEach(n => promises.push(limiter.queue(factoryFactory(n))));
return Promise.all(promises).then((res) => {
assert.equal(10, res.length);
assert.deepEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], res);
});
});
test('Queue - simple', function () {
let queue = new async.Queue();
let syncPromise = false;
let f1 = () => Promise.resolve(true).then(() => syncPromise = true);
let asyncPromise = false;
let f2 = () => async.timeout(10).then(() => asyncPromise = true);
assert.equal(queue.size, 0);
queue.queue(f1);
assert.equal(queue.size, 1);
const p = queue.queue(f2);
assert.equal(queue.size, 2);
return p.then(() => {
assert.equal(queue.size, 0);
assert.ok(syncPromise);
assert.ok(asyncPromise);
});
});
test('Queue - order is kept', function () {
let queue = new async.Queue();
let res: number[] = [];
let f1 = () => Promise.resolve(true).then(() => res.push(1));
let f2 = () => async.timeout(10).then(() => res.push(2));
let f3 = () => Promise.resolve(true).then(() => res.push(3));
let f4 = () => async.timeout(20).then(() => res.push(4));
let f5 = () => async.timeout(0).then(() => res.push(5));
queue.queue(f1);
queue.queue(f2);
queue.queue(f3);
queue.queue(f4);
return queue.queue(f5).then(() => {
assert.equal(res[0], 1);
assert.equal(res[1], 2);
assert.equal(res[2], 3);
assert.equal(res[3], 4);
assert.equal(res[4], 5);
});
});
test('Queue - errors bubble individually but not cause stop', function () {
let queue = new async.Queue();
let res: number[] = [];
let error = false;
let f1 = () => Promise.resolve(true).then(() => res.push(1));
let f2 = () => async.timeout(10).then(() => res.push(2));
let f3 = () => Promise.resolve(true).then(() => Promise.reject(new Error('error')));
let f4 = () => async.timeout(20).then(() => res.push(4));
let f5 = () => async.timeout(0).then(() => res.push(5));
queue.queue(f1);
queue.queue(f2);
queue.queue(f3).then(undefined, () => error = true);
queue.queue(f4);
return queue.queue(f5).then(() => {
assert.equal(res[0], 1);
assert.equal(res[1], 2);
assert.ok(error);
assert.equal(res[2], 4);
assert.equal(res[3], 5);
});
});
test('Queue - order is kept (chained)', function () {
let queue = new async.Queue();
let res: number[] = [];
let f1 = () => Promise.resolve(true).then(() => res.push(1));
let f2 = () => async.timeout(10).then(() => res.push(2));
let f3 = () => Promise.resolve(true).then(() => res.push(3));
let f4 = () => async.timeout(20).then(() => res.push(4));
let f5 = () => async.timeout(0).then(() => res.push(5));
return queue.queue(f1).then(() => {
return queue.queue(f2).then(() => {
return queue.queue(f3).then(() => {
return queue.queue(f4).then(() => {
return queue.queue(f5).then(() => {
assert.equal(res[0], 1);
assert.equal(res[1], 2);
assert.equal(res[2], 3);
assert.equal(res[3], 4);
assert.equal(res[4], 5);
});
});
});
});
});
});
test('Queue - events', function (done) {
let queue = new async.Queue();
let finished = false;
queue.onFinished(() => {
done();
});
let res: number[] = [];
let f1 = () => async.timeout(10).then(() => res.push(2));
let f2 = () => async.timeout(20).then(() => res.push(4));
let f3 = () => async.timeout(0).then(() => res.push(5));
const q1 = queue.queue(f1);
const q2 = queue.queue(f2);
queue.queue(f3);
q1.then(() => {
assert.ok(!finished);
q2.then(() => {
assert.ok(!finished);
});
});
});
test('ResourceQueue - simple', function () {
let queue = new async.ResourceQueue();
const r1Queue = queue.queueFor(URI.file('/some/path'));
r1Queue.onFinished(() => console.log('DONE'));
const r2Queue = queue.queueFor(URI.file('/some/other/path'));
assert.ok(r1Queue);
assert.ok(r2Queue);
assert.equal(r1Queue, queue.queueFor(URI.file('/some/path'))); // same queue returned
let syncPromiseFactory = () => Promise.resolve(undefined);
r1Queue.queue(syncPromiseFactory);
return new Promise<void>(c => setTimeout(() => c(), 0)).then(() => {
const r1Queue2 = queue.queueFor(URI.file('/some/path'));
assert.notEqual(r1Queue, r1Queue2); // previous one got disposed after finishing
});
});
test('retry - success case', async () => {
let counter = 0;
const res = await async.retry(() => {
counter++;
if (counter < 2) {
return Promise.reject(new Error('fail'));
}
return Promise.resolve(true);
}, 10, 3);
assert.equal(res, true);
});
test('retry - error case', async () => {
let expectedError = new Error('fail');
try {
await async.retry(() => {
return Promise.reject(expectedError);
}, 10, 3);
} catch (error) {
assert.equal(error, error);
}
});
test('TaskSequentializer - pending basics', async function () {
const sequentializer = new async.TaskSequentializer();
assert.ok(!sequentializer.hasPending());
assert.ok(!sequentializer.hasPending(2323));
assert.ok(!sequentializer.pending);
// pending removes itself after done
await sequentializer.setPending(1, Promise.resolve());
assert.ok(!sequentializer.hasPending());
assert.ok(!sequentializer.hasPending(1));
assert.ok(!sequentializer.pending);
// pending removes itself after done (use async.timeout)
sequentializer.setPending(2, async.timeout(1));
assert.ok(sequentializer.hasPending());
assert.ok(sequentializer.hasPending(2));
assert.ok(!sequentializer.hasPending(1));
assert.ok(sequentializer.pending);
await async.timeout(2);
assert.ok(!sequentializer.hasPending());
assert.ok(!sequentializer.hasPending(2));
assert.ok(!sequentializer.pending);
});
test('TaskSequentializer - pending and next (finishes instantly)', async function () {
const sequentializer = new async.TaskSequentializer();
let pendingDone = false;
sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; }));
// next finishes instantly
let nextDone = false;
const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; }));
await res;
assert.ok(pendingDone);
assert.ok(nextDone);
});
test('TaskSequentializer - pending and next (finishes after timeout)', async function () {
const sequentializer = new async.TaskSequentializer();
let pendingDone = false;
sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; }));
// next finishes after async.timeout
let nextDone = false;
const res = sequentializer.setNext(() => async.timeout(1).then(() => { nextDone = true; return; }));
await res;
assert.ok(pendingDone);
assert.ok(nextDone);
});
test('TaskSequentializer - pending and multiple next (last one wins)', async function () {
const sequentializer = new async.TaskSequentializer();
let pendingDone = false;
sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; }));
// next finishes after async.timeout
let firstDone = false;
let firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; }));
let secondDone = false;
let secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; }));
let thirdDone = false;
let thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; }));
await Promise.all([firstRes, secondRes, thirdRes]);
assert.ok(pendingDone);
assert.ok(!firstDone);
assert.ok(!secondDone);
assert.ok(thirdDone);
});
test('TaskSequentializer - cancel pending', async function () {
const sequentializer = new async.TaskSequentializer();
let pendingCancelled = false;
sequentializer.setPending(1, async.timeout(1), () => pendingCancelled = true);
sequentializer.cancelPending();
assert.ok(pendingCancelled);
});
test('raceCancellation', async () => {
const cts = new CancellationTokenSource();
const now = Date.now();
const p = async.raceCancellation(async.timeout(100), cts.token);
cts.cancel();
await p;
assert.ok(Date.now() - now < 100);
});
test('raceTimeout', async () => {
const cts = new CancellationTokenSource();
// timeout wins
let now = Date.now();
let timedout = false;
const p1 = async.raceTimeout(async.timeout(100), 1, () => timedout = true);
cts.cancel();
await p1;
assert.ok(Date.now() - now < 100);
assert.equal(timedout, true);
// promise wins
now = Date.now();
timedout = false;
const p2 = async.raceTimeout(async.timeout(1), 100, () => timedout = true);
cts.cancel();
await p2;
assert.ok(Date.now() - now < 100);
assert.equal(timedout, false);
});
test('SequencerByKey', async () => {
const s = new async.SequencerByKey<string>();
const r1 = await s.queue('key1', () => Promise.resolve('hello'));
assert.equal(r1, 'hello');
await s.queue('key2', () => Promise.reject(new Error('failed'))).then(() => {
throw new Error('should not be resolved');
}, err => {
// Expected error
assert.equal(err.message, 'failed');
});
// Still works after a queued promise is rejected
const r3 = await s.queue('key2', () => Promise.resolve('hello'));
assert.equal(r3, 'hello');
});
test('IntervalCounter', async () => {
const counter = new async.IntervalCounter(10);
assert.equal(counter.increment(), 1);
assert.equal(counter.increment(), 2);
assert.equal(counter.increment(), 3);
await async.timeout(20);
assert.equal(counter.increment(), 1);
assert.equal(counter.increment(), 2);
assert.equal(counter.increment(), 3);
});
});

View File

@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* 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 { Cache } from 'vs/base/common/cache';
import { timeout } from 'vs/base/common/async';
suite('Cache', () => {
test('simple value', () => {
let counter = 0;
const cache = new Cache(_ => Promise.resolve(counter++));
return cache.get().promise
.then(c => assert.equal(c, 0), () => assert.fail('Unexpected assertion error'))
.then(() => cache.get().promise)
.then(c => assert.equal(c, 0), () => assert.fail('Unexpected assertion error'));
});
test('simple error', () => {
let counter = 0;
const cache = new Cache(_ => Promise.reject(new Error(String(counter++))));
return cache.get().promise
.then(() => assert.fail('Unexpected assertion error'), err => assert.equal(err.message, 0))
.then(() => cache.get().promise)
.then(() => assert.fail('Unexpected assertion error'), err => assert.equal(err.message, 0));
});
test('should retry cancellations', () => {
let counter1 = 0, counter2 = 0;
const cache = new Cache(token => {
counter1++;
return Promise.resolve(timeout(2, token).then(() => counter2++));
});
assert.equal(counter1, 0);
assert.equal(counter2, 0);
let result = cache.get();
assert.equal(counter1, 1);
assert.equal(counter2, 0);
result.promise.then(undefined, () => assert(true));
result.dispose();
assert.equal(counter1, 1);
assert.equal(counter2, 0);
result = cache.get();
assert.equal(counter1, 2);
assert.equal(counter2, 0);
return result.promise
.then(c => {
assert.equal(counter1, 2);
assert.equal(counter2, 1);
})
.then(() => cache.get().promise)
.then(c => {
assert.equal(counter1, 2);
assert.equal(counter2, 1);
});
});
});

View File

@@ -0,0 +1,126 @@
/*---------------------------------------------------------------------------------------------
* 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 { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
suite('CancellationToken', function () {
test('None', () => {
assert.equal(CancellationToken.None.isCancellationRequested, false);
assert.equal(typeof CancellationToken.None.onCancellationRequested, 'function');
});
test('cancel before token', function (done) {
const source = new CancellationTokenSource();
assert.equal(source.token.isCancellationRequested, false);
source.cancel();
assert.equal(source.token.isCancellationRequested, true);
source.token.onCancellationRequested(function () {
assert.ok(true);
done();
});
});
test('cancel happens only once', function () {
let source = new CancellationTokenSource();
assert.equal(source.token.isCancellationRequested, false);
let cancelCount = 0;
function onCancel() {
cancelCount += 1;
}
source.token.onCancellationRequested(onCancel);
source.cancel();
source.cancel();
assert.equal(cancelCount, 1);
});
test('cancel calls all listeners', function () {
let count = 0;
let source = new CancellationTokenSource();
source.token.onCancellationRequested(function () {
count += 1;
});
source.token.onCancellationRequested(function () {
count += 1;
});
source.token.onCancellationRequested(function () {
count += 1;
});
source.cancel();
assert.equal(count, 3);
});
test('token stays the same', function () {
let source = new CancellationTokenSource();
let token = source.token;
assert.ok(token === source.token); // doesn't change on get
source.cancel();
assert.ok(token === source.token); // doesn't change after cancel
source.cancel();
assert.ok(token === source.token); // doesn't change after 2nd cancel
source = new CancellationTokenSource();
source.cancel();
token = source.token;
assert.ok(token === source.token); // doesn't change on get
});
test('dispose calls no listeners', function () {
let count = 0;
let source = new CancellationTokenSource();
source.token.onCancellationRequested(function () {
count += 1;
});
source.dispose();
source.cancel();
assert.equal(count, 0);
});
test('dispose calls no listeners (unless told to cancel)', function () {
let count = 0;
let source = new CancellationTokenSource();
source.token.onCancellationRequested(function () {
count += 1;
});
source.dispose(true);
// source.cancel();
assert.equal(count, 1);
});
test('parent cancels child', function () {
let parent = new CancellationTokenSource();
let child = new CancellationTokenSource(parent.token);
let count = 0;
child.token.onCancellationRequested(() => count += 1);
parent.cancel();
assert.equal(count, 1);
assert.equal(child.token.isCancellationRequested, true);
assert.equal(parent.token.isCancellationRequested, true);
});
});

View File

@@ -0,0 +1,121 @@
/*---------------------------------------------------------------------------------------------
* 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 { CharCode } from 'vs/base/common/charCode';
suite('CharCode', () => {
test('has good values', () => {
function assertValue(actual: CharCode, expected: string): void {
assert.equal(actual, expected.charCodeAt(0), 'char code ok for <<' + expected + '>>');
}
assertValue(CharCode.Tab, '\t');
assertValue(CharCode.LineFeed, '\n');
assertValue(CharCode.CarriageReturn, '\r');
assertValue(CharCode.Space, ' ');
assertValue(CharCode.ExclamationMark, '!');
assertValue(CharCode.DoubleQuote, '"');
assertValue(CharCode.Hash, '#');
assertValue(CharCode.DollarSign, '$');
assertValue(CharCode.PercentSign, '%');
assertValue(CharCode.Ampersand, '&');
assertValue(CharCode.SingleQuote, '\'');
assertValue(CharCode.OpenParen, '(');
assertValue(CharCode.CloseParen, ')');
assertValue(CharCode.Asterisk, '*');
assertValue(CharCode.Plus, '+');
assertValue(CharCode.Comma, ',');
assertValue(CharCode.Dash, '-');
assertValue(CharCode.Period, '.');
assertValue(CharCode.Slash, '/');
assertValue(CharCode.Digit0, '0');
assertValue(CharCode.Digit1, '1');
assertValue(CharCode.Digit2, '2');
assertValue(CharCode.Digit3, '3');
assertValue(CharCode.Digit4, '4');
assertValue(CharCode.Digit5, '5');
assertValue(CharCode.Digit6, '6');
assertValue(CharCode.Digit7, '7');
assertValue(CharCode.Digit8, '8');
assertValue(CharCode.Digit9, '9');
assertValue(CharCode.Colon, ':');
assertValue(CharCode.Semicolon, ';');
assertValue(CharCode.LessThan, '<');
assertValue(CharCode.Equals, '=');
assertValue(CharCode.GreaterThan, '>');
assertValue(CharCode.QuestionMark, '?');
assertValue(CharCode.AtSign, '@');
assertValue(CharCode.A, 'A');
assertValue(CharCode.B, 'B');
assertValue(CharCode.C, 'C');
assertValue(CharCode.D, 'D');
assertValue(CharCode.E, 'E');
assertValue(CharCode.F, 'F');
assertValue(CharCode.G, 'G');
assertValue(CharCode.H, 'H');
assertValue(CharCode.I, 'I');
assertValue(CharCode.J, 'J');
assertValue(CharCode.K, 'K');
assertValue(CharCode.L, 'L');
assertValue(CharCode.M, 'M');
assertValue(CharCode.N, 'N');
assertValue(CharCode.O, 'O');
assertValue(CharCode.P, 'P');
assertValue(CharCode.Q, 'Q');
assertValue(CharCode.R, 'R');
assertValue(CharCode.S, 'S');
assertValue(CharCode.T, 'T');
assertValue(CharCode.U, 'U');
assertValue(CharCode.V, 'V');
assertValue(CharCode.W, 'W');
assertValue(CharCode.X, 'X');
assertValue(CharCode.Y, 'Y');
assertValue(CharCode.Z, 'Z');
assertValue(CharCode.OpenSquareBracket, '[');
assertValue(CharCode.Backslash, '\\');
assertValue(CharCode.CloseSquareBracket, ']');
assertValue(CharCode.Caret, '^');
assertValue(CharCode.Underline, '_');
assertValue(CharCode.BackTick, '`');
assertValue(CharCode.a, 'a');
assertValue(CharCode.b, 'b');
assertValue(CharCode.c, 'c');
assertValue(CharCode.d, 'd');
assertValue(CharCode.e, 'e');
assertValue(CharCode.f, 'f');
assertValue(CharCode.g, 'g');
assertValue(CharCode.h, 'h');
assertValue(CharCode.i, 'i');
assertValue(CharCode.j, 'j');
assertValue(CharCode.k, 'k');
assertValue(CharCode.l, 'l');
assertValue(CharCode.m, 'm');
assertValue(CharCode.n, 'n');
assertValue(CharCode.o, 'o');
assertValue(CharCode.p, 'p');
assertValue(CharCode.q, 'q');
assertValue(CharCode.r, 'r');
assertValue(CharCode.s, 's');
assertValue(CharCode.t, 't');
assertValue(CharCode.u, 'u');
assertValue(CharCode.v, 'v');
assertValue(CharCode.w, 'w');
assertValue(CharCode.x, 'x');
assertValue(CharCode.y, 'y');
assertValue(CharCode.z, 'z');
assertValue(CharCode.OpenCurlyBrace, '{');
assertValue(CharCode.Pipe, '|');
assertValue(CharCode.CloseCurlyBrace, '}');
assertValue(CharCode.Tilde, '~');
});
});

View File

@@ -0,0 +1,78 @@
/*---------------------------------------------------------------------------------------------
* 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 { IMatch } from 'vs/base/common/filters';
import { matchesFuzzyCodiconAware, parseCodicons, IParsedCodicons } from 'vs/base/common/codicon';
import { stripCodicons } from 'vs/base/common/codicons';
export interface ICodiconFilter {
// Returns null if word doesn't match.
(query: string, target: IParsedCodicons): IMatch[] | null;
}
function filterOk(filter: ICodiconFilter, word: string, target: IParsedCodicons, highlights?: { start: number; end: number; }[]) {
let r = filter(word, target);
assert(r);
if (highlights) {
assert.deepEqual(r, highlights);
}
}
suite('Codicon', () => {
test('matchesFuzzzyCodiconAware', () => {
// Camel Case
filterOk(matchesFuzzyCodiconAware, 'ccr', parseCodicons('$(codicon)CamelCaseRocks$(codicon)'), [
{ start: 10, end: 11 },
{ start: 15, end: 16 },
{ start: 19, end: 20 }
]);
filterOk(matchesFuzzyCodiconAware, 'ccr', parseCodicons('$(codicon) CamelCaseRocks $(codicon)'), [
{ start: 11, end: 12 },
{ start: 16, end: 17 },
{ start: 20, end: 21 }
]);
filterOk(matchesFuzzyCodiconAware, 'iut', parseCodicons('$(codicon) Indent $(octico) Using $(octic) Tpaces'), [
{ start: 11, end: 12 },
{ start: 28, end: 29 },
{ start: 43, end: 44 },
]);
// Prefix
filterOk(matchesFuzzyCodiconAware, 'using', parseCodicons('$(codicon) Indent Using Spaces'), [
{ start: 18, end: 23 },
]);
// Broken Codicon
filterOk(matchesFuzzyCodiconAware, 'codicon', parseCodicons('This $(codicon Indent Using Spaces'), [
{ start: 7, end: 14 },
]);
filterOk(matchesFuzzyCodiconAware, 'indent', parseCodicons('This $codicon Indent Using Spaces'), [
{ start: 14, end: 20 },
]);
// Testing #59343
filterOk(matchesFuzzyCodiconAware, 'unt', parseCodicons('$(primitive-dot) $(file-text) Untitled-1'), [
{ start: 30, end: 33 },
]);
});
});
suite('Codicons', () => {
test('stripCodicons', () => {
assert.equal(stripCodicons('Hello World'), 'Hello World');
assert.equal(stripCodicons('$(Hello World'), '$(Hello World');
assert.equal(stripCodicons('$(Hello) World'), ' World');
assert.equal(stripCodicons('$(Hello) W$(oi)rld'), ' Wrld');
});
});

View File

@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------------------------
* 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 * as collections from 'vs/base/common/collections';
suite('Collections', () => {
test('forEach', () => {
collections.forEach({}, () => assert(false));
collections.forEach(Object.create(null), () => assert(false));
let count = 0;
collections.forEach({ toString: 123 }, () => count++);
assert.equal(count, 1);
count = 0;
let dict = Object.create(null);
dict['toString'] = 123;
collections.forEach(dict, () => count++);
assert.equal(count, 1);
collections.forEach(dict, () => false);
collections.forEach(dict, (x, remove) => remove());
assert.equal(dict['toString'], null);
// don't iterate over properties that are not on the object itself
let test = Object.create({ 'derived': true });
collections.forEach(test, () => assert(false));
});
test('groupBy', () => {
const group1 = 'a', group2 = 'b';
const value1 = 1, value2 = 2, value3 = 3;
let source = [
{ key: group1, value: value1 },
{ key: group1, value: value2 },
{ key: group2, value: value3 },
];
let grouped = collections.groupBy(source, x => x.key);
// Group 1
assert.equal(grouped[group1].length, 2);
assert.equal(grouped[group1][0].value, value1);
assert.equal(grouped[group1][1].value, value2);
// Group 2
assert.equal(grouped[group2].length, 1);
assert.equal(grouped[group2][0].value, value3);
});
});

View File

@@ -0,0 +1,253 @@
/*---------------------------------------------------------------------------------------------
* 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 { Color, RGBA, HSLA, HSVA } from 'vs/base/common/color';
suite('Color', () => {
test('isLighterColor', () => {
let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1));
assert.ok(color1.isLighterThan(color2));
// Abyss theme
assert.ok(Color.fromHex('#770811').isLighterThan(Color.fromHex('#000c18')));
});
test('getLighterColor', () => {
let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1));
assert.deepEqual(color1.hsla, Color.getLighterColor(color1, color2).hsla);
assert.deepEqual(new HSLA(0, 0, 0.916, 1), Color.getLighterColor(color2, color1).hsla);
assert.deepEqual(new HSLA(0, 0, 0.851, 1), Color.getLighterColor(color2, color1, 0.3).hsla);
assert.deepEqual(new HSLA(0, 0, 0.981, 1), Color.getLighterColor(color2, color1, 0.7).hsla);
assert.deepEqual(new HSLA(0, 0, 1, 1), Color.getLighterColor(color2, color1, 1).hsla);
});
test('isDarkerColor', () => {
let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1));
assert.ok(color2.isDarkerThan(color1));
});
test('getDarkerColor', () => {
let color1 = new Color(new HSLA(60, 1, 0.5, 1)), color2 = new Color(new HSLA(0, 0, 0.753, 1));
assert.deepEqual(color2.hsla, Color.getDarkerColor(color2, color1).hsla);
assert.deepEqual(new HSLA(60, 1, 0.392, 1), Color.getDarkerColor(color1, color2).hsla);
assert.deepEqual(new HSLA(60, 1, 0.435, 1), Color.getDarkerColor(color1, color2, 0.3).hsla);
assert.deepEqual(new HSLA(60, 1, 0.349, 1), Color.getDarkerColor(color1, color2, 0.7).hsla);
assert.deepEqual(new HSLA(60, 1, 0.284, 1), Color.getDarkerColor(color1, color2, 1).hsla);
// Abyss theme
assert.deepEqual(new HSLA(355, 0.874, 0.157, 1), Color.getDarkerColor(Color.fromHex('#770811'), Color.fromHex('#000c18'), 0.4).hsla);
});
test('luminance', () => {
assert.deepEqual(0, new Color(new RGBA(0, 0, 0, 1)).getRelativeLuminance());
assert.deepEqual(1, new Color(new RGBA(255, 255, 255, 1)).getRelativeLuminance());
assert.deepEqual(0.2126, new Color(new RGBA(255, 0, 0, 1)).getRelativeLuminance());
assert.deepEqual(0.7152, new Color(new RGBA(0, 255, 0, 1)).getRelativeLuminance());
assert.deepEqual(0.0722, new Color(new RGBA(0, 0, 255, 1)).getRelativeLuminance());
assert.deepEqual(0.9278, new Color(new RGBA(255, 255, 0, 1)).getRelativeLuminance());
assert.deepEqual(0.7874, new Color(new RGBA(0, 255, 255, 1)).getRelativeLuminance());
assert.deepEqual(0.2848, new Color(new RGBA(255, 0, 255, 1)).getRelativeLuminance());
assert.deepEqual(0.5271, new Color(new RGBA(192, 192, 192, 1)).getRelativeLuminance());
assert.deepEqual(0.2159, new Color(new RGBA(128, 128, 128, 1)).getRelativeLuminance());
assert.deepEqual(0.0459, new Color(new RGBA(128, 0, 0, 1)).getRelativeLuminance());
assert.deepEqual(0.2003, new Color(new RGBA(128, 128, 0, 1)).getRelativeLuminance());
assert.deepEqual(0.1544, new Color(new RGBA(0, 128, 0, 1)).getRelativeLuminance());
assert.deepEqual(0.0615, new Color(new RGBA(128, 0, 128, 1)).getRelativeLuminance());
assert.deepEqual(0.17, new Color(new RGBA(0, 128, 128, 1)).getRelativeLuminance());
assert.deepEqual(0.0156, new Color(new RGBA(0, 0, 128, 1)).getRelativeLuminance());
});
test('blending', () => {
assert.deepEqual(new Color(new RGBA(0, 0, 0, 0)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(243, 34, 43)));
assert.deepEqual(new Color(new RGBA(255, 255, 255)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(255, 255, 255)));
assert.deepEqual(new Color(new RGBA(122, 122, 122, 0.7)).blend(new Color(new RGBA(243, 34, 43))), new Color(new RGBA(158, 95, 98)));
assert.deepEqual(new Color(new RGBA(0, 0, 0, 0.58)).blend(new Color(new RGBA(255, 255, 255, 0.33))), new Color(new RGBA(49, 49, 49, 0.719)));
});
suite('HSLA', () => {
test('HSLA.toRGBA', () => {
assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 0)), new RGBA(0, 0, 0, 0));
assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0, 1)), new RGBA(0, 0, 0, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 1, 1)), new RGBA(255, 255, 255, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.5, 1)), new RGBA(255, 0, 0, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.5, 1)), new RGBA(0, 255, 0, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.5, 1)), new RGBA(0, 0, 255, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.5, 1)), new RGBA(255, 255, 0, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.5, 1)), new RGBA(0, 255, 255, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.5, 1)), new RGBA(255, 0, 255, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(0, 1, 0.251, 1)), new RGBA(128, 0, 0, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(60, 1, 0.251, 1)), new RGBA(128, 128, 0, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(120, 1, 0.251, 1)), new RGBA(0, 128, 0, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(300, 1, 0.251, 1)), new RGBA(128, 0, 128, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(180, 1, 0.251, 1)), new RGBA(0, 128, 128, 1));
assert.deepEqual(HSLA.toRGBA(new HSLA(240, 1, 0.251, 1)), new RGBA(0, 0, 128, 1));
});
test('HSLA.fromRGBA', () => {
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 0)), new HSLA(0, 0, 0, 0));
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 0, 1)), new HSLA(0, 0, 0, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 255, 1)), new HSLA(0, 0, 1, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 0, 1)), new HSLA(0, 1, 0.5, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 0, 1)), new HSLA(120, 1, 0.5, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 255, 1)), new HSLA(240, 1, 0.5, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 255, 0, 1)), new HSLA(60, 1, 0.5, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 255, 255, 1)), new HSLA(180, 1, 0.5, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(255, 0, 255, 1)), new HSLA(300, 1, 0.5, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(192, 192, 192, 1)), new HSLA(0, 0, 0.753, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 128, 1)), new HSLA(0, 0, 0.502, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 0, 1)), new HSLA(0, 1, 0.251, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 128, 0, 1)), new HSLA(60, 1, 0.251, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 0, 1)), new HSLA(120, 1, 0.251, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(128, 0, 128, 1)), new HSLA(300, 1, 0.251, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 128, 128, 1)), new HSLA(180, 1, 0.251, 1));
assert.deepEqual(HSLA.fromRGBA(new RGBA(0, 0, 128, 1)), new HSLA(240, 1, 0.251, 1));
});
});
suite('HSVA', () => {
test('HSVA.toRGBA', () => {
assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 0)), new RGBA(0, 0, 0, 0));
assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0, 1)), new RGBA(0, 0, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 1, 1)), new RGBA(255, 255, 255, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 1, 1)), new RGBA(255, 0, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 1, 1)), new RGBA(0, 255, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 1, 1)), new RGBA(0, 0, 255, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 1, 1)), new RGBA(255, 255, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 1, 1)), new RGBA(0, 255, 255, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 1, 1)), new RGBA(255, 0, 255, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.753, 1)), new RGBA(192, 192, 192, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(0, 0, 0.502, 1)), new RGBA(128, 128, 128, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(0, 1, 0.502, 1)), new RGBA(128, 0, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(60, 1, 0.502, 1)), new RGBA(128, 128, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(120, 1, 0.502, 1)), new RGBA(0, 128, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(300, 1, 0.502, 1)), new RGBA(128, 0, 128, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(180, 1, 0.502, 1)), new RGBA(0, 128, 128, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(240, 1, 0.502, 1)), new RGBA(0, 0, 128, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0, 0)), new RGBA(0, 0, 0, 0));
assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0, 1)), new RGBA(0, 0, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 1, 1)), new RGBA(255, 255, 255, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(360, 1, 1, 1)), new RGBA(255, 0, 0, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0.753, 1)), new RGBA(192, 192, 192, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(360, 0, 0.502, 1)), new RGBA(128, 128, 128, 1));
assert.deepEqual(HSVA.toRGBA(new HSVA(360, 1, 0.502, 1)), new RGBA(128, 0, 0, 1));
});
test('HSVA.fromRGBA', () => {
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 0)), new HSVA(0, 0, 0, 0));
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 0, 1)), new HSVA(0, 0, 0, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 255, 1)), new HSVA(0, 0, 1, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 0, 1)), new HSVA(0, 1, 1, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 0, 1)), new HSVA(120, 1, 1, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 255, 1)), new HSVA(240, 1, 1, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 255, 0, 1)), new HSVA(60, 1, 1, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 255, 255, 1)), new HSVA(180, 1, 1, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(255, 0, 255, 1)), new HSVA(300, 1, 1, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(192, 192, 192, 1)), new HSVA(0, 0, 0.753, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 128, 1)), new HSVA(0, 0, 0.502, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 0, 1)), new HSVA(0, 1, 0.502, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 128, 0, 1)), new HSVA(60, 1, 0.502, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 0, 1)), new HSVA(120, 1, 0.502, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(128, 0, 128, 1)), new HSVA(300, 1, 0.502, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 128, 128, 1)), new HSVA(180, 1, 0.502, 1));
assert.deepEqual(HSVA.fromRGBA(new RGBA(0, 0, 128, 1)), new HSVA(240, 1, 0.502, 1));
});
test('Keep hue value when saturation is 0', () => {
assert.deepEqual(HSVA.toRGBA(new HSVA(10, 0, 0, 0)), HSVA.toRGBA(new HSVA(20, 0, 0, 0)));
assert.deepEqual(new Color(new HSVA(10, 0, 0, 0)).rgba, new Color(new HSVA(20, 0, 0, 0)).rgba);
assert.notDeepEqual(new Color(new HSVA(10, 0, 0, 0)).hsva, new Color(new HSVA(20, 0, 0, 0)).hsva);
});
test('bug#36240', () => {
assert.deepEqual(HSVA.fromRGBA(new RGBA(92, 106, 196, 1)), new HSVA(232, 0.531, 0.769, 1));
assert.deepEqual(HSVA.toRGBA(HSVA.fromRGBA(new RGBA(92, 106, 196, 1))), new RGBA(92, 106, 196, 1));
});
});
suite('Format', () => {
suite('CSS', () => {
test('parseHex', () => {
// invalid
assert.deepEqual(Color.Format.CSS.parseHex(''), null);
assert.deepEqual(Color.Format.CSS.parseHex('#'), null);
assert.deepEqual(Color.Format.CSS.parseHex('#0102030'), null);
// somewhat valid
assert.deepEqual(Color.Format.CSS.parseHex('#FFFFG0')!.rgba, new RGBA(255, 255, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#FFFFg0')!.rgba, new RGBA(255, 255, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#-FFF00')!.rgba, new RGBA(15, 255, 0, 1));
// valid
assert.deepEqual(Color.Format.CSS.parseHex('#000000')!.rgba, new RGBA(0, 0, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#FFFFFF')!.rgba, new RGBA(255, 255, 255, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#FF0000')!.rgba, new RGBA(255, 0, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#00FF00')!.rgba, new RGBA(0, 255, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#0000FF')!.rgba, new RGBA(0, 0, 255, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#FFFF00')!.rgba, new RGBA(255, 255, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#00FFFF')!.rgba, new RGBA(0, 255, 255, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#FF00FF')!.rgba, new RGBA(255, 0, 255, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#C0C0C0')!.rgba, new RGBA(192, 192, 192, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#808080')!.rgba, new RGBA(128, 128, 128, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#800000')!.rgba, new RGBA(128, 0, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#808000')!.rgba, new RGBA(128, 128, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#008000')!.rgba, new RGBA(0, 128, 0, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#800080')!.rgba, new RGBA(128, 0, 128, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#008080')!.rgba, new RGBA(0, 128, 128, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#000080')!.rgba, new RGBA(0, 0, 128, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#010203')!.rgba, new RGBA(1, 2, 3, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#040506')!.rgba, new RGBA(4, 5, 6, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#070809')!.rgba, new RGBA(7, 8, 9, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#0a0A0a')!.rgba, new RGBA(10, 10, 10, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#0b0B0b')!.rgba, new RGBA(11, 11, 11, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#0c0C0c')!.rgba, new RGBA(12, 12, 12, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#0d0D0d')!.rgba, new RGBA(13, 13, 13, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#0e0E0e')!.rgba, new RGBA(14, 14, 14, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#0f0F0f')!.rgba, new RGBA(15, 15, 15, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#a0A0a0')!.rgba, new RGBA(160, 160, 160, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#CFA')!.rgba, new RGBA(204, 255, 170, 1));
assert.deepEqual(Color.Format.CSS.parseHex('#CFA8')!.rgba, new RGBA(204, 255, 170, 0.533));
});
});
});
});

View File

@@ -0,0 +1,200 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as sinon from 'sinon';
import * as assert from 'assert';
import { memoize, createMemoizer, throttle } from 'vs/base/common/decorators';
suite('Decorators', () => {
test('memoize should memoize methods', () => {
class Foo {
count = 0;
constructor(private _answer: number | null | undefined) { }
@memoize
answer() {
this.count++;
return this._answer;
}
}
const foo = new Foo(42);
assert.equal(foo.count, 0);
assert.equal(foo.answer(), 42);
assert.equal(foo.count, 1);
assert.equal(foo.answer(), 42);
assert.equal(foo.count, 1);
const foo2 = new Foo(1337);
assert.equal(foo2.count, 0);
assert.equal(foo2.answer(), 1337);
assert.equal(foo2.count, 1);
assert.equal(foo2.answer(), 1337);
assert.equal(foo2.count, 1);
assert.equal(foo.answer(), 42);
assert.equal(foo.count, 1);
const foo3 = new Foo(null);
assert.equal(foo3.count, 0);
assert.equal(foo3.answer(), null);
assert.equal(foo3.count, 1);
assert.equal(foo3.answer(), null);
assert.equal(foo3.count, 1);
const foo4 = new Foo(undefined);
assert.equal(foo4.count, 0);
assert.equal(foo4.answer(), undefined);
assert.equal(foo4.count, 1);
assert.equal(foo4.answer(), undefined);
assert.equal(foo4.count, 1);
});
test('memoize should memoize getters', () => {
class Foo {
count = 0;
constructor(private _answer: number | null | undefined) { }
@memoize
get answer() {
this.count++;
return this._answer;
}
}
const foo = new Foo(42);
assert.equal(foo.count, 0);
assert.equal(foo.answer, 42);
assert.equal(foo.count, 1);
assert.equal(foo.answer, 42);
assert.equal(foo.count, 1);
const foo2 = new Foo(1337);
assert.equal(foo2.count, 0);
assert.equal(foo2.answer, 1337);
assert.equal(foo2.count, 1);
assert.equal(foo2.answer, 1337);
assert.equal(foo2.count, 1);
assert.equal(foo.answer, 42);
assert.equal(foo.count, 1);
const foo3 = new Foo(null);
assert.equal(foo3.count, 0);
assert.equal(foo3.answer, null);
assert.equal(foo3.count, 1);
assert.equal(foo3.answer, null);
assert.equal(foo3.count, 1);
const foo4 = new Foo(undefined);
assert.equal(foo4.count, 0);
assert.equal(foo4.answer, undefined);
assert.equal(foo4.count, 1);
assert.equal(foo4.answer, undefined);
assert.equal(foo4.count, 1);
});
test('memoized property should not be enumerable', () => {
class Foo {
@memoize
get answer() {
return 42;
}
}
const foo = new Foo();
assert.equal(foo.answer, 42);
assert(!Object.keys(foo).some(k => /\$memoize\$/.test(k)));
});
test('memoized property should not be writable', () => {
class Foo {
@memoize
get answer() {
return 42;
}
}
const foo = new Foo();
assert.equal(foo.answer, 42);
try {
(foo as any)['$memoize$answer'] = 1337;
assert(false);
} catch (e) {
assert.equal(foo.answer, 42);
}
});
test('memoize clear', () => {
const memoizer = createMemoizer();
let counter = 0;
class Foo {
@memoizer
get answer() {
return ++counter;
}
}
const foo = new Foo();
assert.equal(foo.answer, 1);
assert.equal(foo.answer, 1);
memoizer.clear();
assert.equal(foo.answer, 2);
assert.equal(foo.answer, 2);
memoizer.clear();
assert.equal(foo.answer, 3);
assert.equal(foo.answer, 3);
assert.equal(foo.answer, 3);
});
test('throttle', () => {
const spy = sinon.spy();
const clock = sinon.useFakeTimers();
try {
class ThrottleTest {
private _handle: Function;
constructor(fn: Function) {
this._handle = fn;
}
@throttle(
100,
(a: number, b: number) => a + b,
() => 0
)
report(p: number): void {
this._handle(p);
}
}
const t = new ThrottleTest(spy);
t.report(1);
t.report(2);
t.report(3);
assert.deepEqual(spy.args, [[1]]);
clock.tick(200);
assert.deepEqual(spy.args, [[1], [5]]);
spy.reset();
t.report(4);
t.report(5);
clock.tick(50);
t.report(6);
assert.deepEqual(spy.args, [[4]]);
clock.tick(60);
assert.deepEqual(spy.args, [[4], [11]]);
} finally {
clock.restore();
}
});
});

View File

@@ -0,0 +1,180 @@
/*---------------------------------------------------------------------------------------------
* 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 { LcsDiff, IDiffChange, StringDiffSequence } from 'vs/base/common/diff/diff';
function createArray<T>(length: number, value: T): T[] {
const r: T[] = [];
for (let i = 0; i < length; i++) {
r[i] = value;
}
return r;
}
function maskBasedSubstring(str: string, mask: boolean[]): string {
let r = '';
for (let i = 0; i < str.length; i++) {
if (mask[i]) {
r += str.charAt(i);
}
}
return r;
}
function assertAnswer(originalStr: string, modifiedStr: string, changes: IDiffChange[], answerStr: string, onlyLength: boolean = false): void {
let originalMask = createArray(originalStr.length, true);
let modifiedMask = createArray(modifiedStr.length, true);
let i, j, change;
for (i = 0; i < changes.length; i++) {
change = changes[i];
if (change.originalLength) {
for (j = 0; j < change.originalLength; j++) {
originalMask[change.originalStart + j] = false;
}
}
if (change.modifiedLength) {
for (j = 0; j < change.modifiedLength; j++) {
modifiedMask[change.modifiedStart + j] = false;
}
}
}
let originalAnswer = maskBasedSubstring(originalStr, originalMask);
let modifiedAnswer = maskBasedSubstring(modifiedStr, modifiedMask);
if (onlyLength) {
assert.equal(originalAnswer.length, answerStr.length);
assert.equal(modifiedAnswer.length, answerStr.length);
} else {
assert.equal(originalAnswer, answerStr);
assert.equal(modifiedAnswer, answerStr);
}
}
function lcsInnerTest(originalStr: string, modifiedStr: string, answerStr: string, onlyLength: boolean = false): void {
let diff = new LcsDiff(new StringDiffSequence(originalStr), new StringDiffSequence(modifiedStr));
let changes = diff.ComputeDiff(false).changes;
assertAnswer(originalStr, modifiedStr, changes, answerStr, onlyLength);
}
function stringPower(str: string, power: number): string {
let r = str;
for (let i = 0; i < power; i++) {
r += r;
}
return r;
}
function lcsTest(originalStr: string, modifiedStr: string, answerStr: string) {
lcsInnerTest(originalStr, modifiedStr, answerStr);
for (let i = 2; i <= 5; i++) {
lcsInnerTest(stringPower(originalStr, i), stringPower(modifiedStr, i), stringPower(answerStr, i), true);
}
}
suite('Diff', () => {
test('LcsDiff - different strings tests', function () {
this.timeout(10000);
lcsTest('heLLo world', 'hello orlando', 'heo orld');
lcsTest('abcde', 'acd', 'acd'); // simple
lcsTest('abcdbce', 'bcede', 'bcde'); // skip
lcsTest('abcdefgabcdefg', 'bcehafg', 'bceafg'); // long
lcsTest('abcde', 'fgh', ''); // no match
lcsTest('abcfabc', 'fabc', 'fabc');
lcsTest('0azby0', '9axbzby9', 'azby');
lcsTest('0abc00000', '9a1b2c399999', 'abc');
lcsTest('fooBar', 'myfooBar', 'fooBar'); // all insertions
lcsTest('fooBar', 'fooMyBar', 'fooBar'); // all insertions
lcsTest('fooBar', 'fooBar', 'fooBar'); // identical sequences
});
});
suite('Diff - Ported from VS', () => {
test('using continue processing predicate to quit early', function () {
let left = 'abcdef';
let right = 'abxxcyyydzzzzezzzzzzzzzzzzzzzzzzzzf';
// We use a long non-matching portion at the end of the right-side string, so the backwards tracking logic
// doesn't get there first.
let predicateCallCount = 0;
let diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
assert.equal(predicateCallCount, 0);
predicateCallCount++;
assert.equal(leftIndex, 1);
// cancel processing
return false;
});
let changes = diff.ComputeDiff(true).changes;
assert.equal(predicateCallCount, 1);
// Doesn't include 'c', 'd', or 'e', since we quit on the first request
assertAnswer(left, right, changes, 'abf');
// Cancel after the first match ('c')
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
assert(longestMatchSoFar <= 1); // We never see a match of length > 1
// Continue processing as long as there hasn't been a match made.
return longestMatchSoFar < 1;
});
changes = diff.ComputeDiff(true).changes;
assertAnswer(left, right, changes, 'abcf');
// Cancel after the second match ('d')
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
assert(longestMatchSoFar <= 2); // We never see a match of length > 2
// Continue processing as long as there hasn't been a match made.
return longestMatchSoFar < 2;
});
changes = diff.ComputeDiff(true).changes;
assertAnswer(left, right, changes, 'abcdf');
// Cancel *one iteration* after the second match ('d')
let hitSecondMatch = false;
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
assert(longestMatchSoFar <= 2); // We never see a match of length > 2
let hitYet = hitSecondMatch;
hitSecondMatch = longestMatchSoFar > 1;
// Continue processing as long as there hasn't been a match made.
return !hitYet;
});
changes = diff.ComputeDiff(true).changes;
assertAnswer(left, right, changes, 'abcdf');
// Cancel after the third and final match ('e')
diff = new LcsDiff(new StringDiffSequence(left), new StringDiffSequence(right), function (leftIndex, longestMatchSoFar) {
assert(longestMatchSoFar <= 3); // We never see a match of length > 3
// Continue processing as long as there hasn't been a match made.
return longestMatchSoFar < 3;
});
changes = diff.ComputeDiff(true).changes;
assertAnswer(left, right, changes, 'abcdef');
});
});

View File

@@ -0,0 +1,33 @@
/*---------------------------------------------------------------------------------------------
* 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 { toErrorMessage } from 'vs/base/common/errorMessage';
suite('Errors', () => {
test('Get Error Message', function () {
assert.strictEqual(toErrorMessage('Foo Bar'), 'Foo Bar');
assert.strictEqual(toErrorMessage(new Error('Foo Bar')), 'Foo Bar');
let error: any = new Error();
error = new Error();
error.detail = {};
error.detail.exception = {};
error.detail.exception.message = 'Foo Bar';
assert.strictEqual(toErrorMessage(error), 'Foo Bar');
assert.strictEqual(toErrorMessage(error, true), 'Foo Bar');
assert(toErrorMessage());
assert(toErrorMessage(null));
assert(toErrorMessage({}));
try {
throw new Error();
} catch (error) {
assert.strictEqual(toErrorMessage(error), 'An unknown error occurred. Please consult the log for more details.');
assert.ok(toErrorMessage(error, true).length > 'An unknown error occurred. Please consult the log for more details.'.length);
}
});
});

View File

@@ -0,0 +1,896 @@
/*---------------------------------------------------------------------------------------------
* 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 { Event, Emitter, EventBufferer, EventMultiplexer, IWaitUntil, PauseableEmitter, AsyncEmitter } from 'vs/base/common/event';
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import * as Errors from 'vs/base/common/errors';
import { timeout } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
namespace Samples {
export class EventCounter {
count = 0;
reset() {
this.count = 0;
}
onEvent() {
this.count += 1;
}
}
export class Document3 {
private readonly _onDidChange = new Emitter<string>();
onDidChange: Event<string> = this._onDidChange.event;
setText(value: string) {
//...
this._onDidChange.fire(value);
}
}
}
suite('Event', function () {
const counter = new Samples.EventCounter();
setup(() => counter.reset());
test('Emitter plain', function () {
let doc = new Samples.Document3();
document.createElement('div').onclick = function () { };
let subscription = doc.onDidChange(counter.onEvent, counter);
doc.setText('far');
doc.setText('boo');
// unhook listener
subscription.dispose();
doc.setText('boo');
assert.equal(counter.count, 2);
});
test('Emitter, bucket', function () {
let bucket: IDisposable[] = [];
let doc = new Samples.Document3();
let subscription = doc.onDidChange(counter.onEvent, counter, bucket);
doc.setText('far');
doc.setText('boo');
// unhook listener
while (bucket.length) {
bucket.pop()!.dispose();
}
doc.setText('boo');
// noop
subscription.dispose();
doc.setText('boo');
assert.equal(counter.count, 2);
});
test('Emitter, store', function () {
let bucket = new DisposableStore();
let doc = new Samples.Document3();
let subscription = doc.onDidChange(counter.onEvent, counter, bucket);
doc.setText('far');
doc.setText('boo');
// unhook listener
bucket.clear();
doc.setText('boo');
// noop
subscription.dispose();
doc.setText('boo');
assert.equal(counter.count, 2);
});
test('onFirstAdd|onLastRemove', () => {
let firstCount = 0;
let lastCount = 0;
let a = new Emitter({
onFirstListenerAdd() { firstCount += 1; },
onLastListenerRemove() { lastCount += 1; }
});
assert.equal(firstCount, 0);
assert.equal(lastCount, 0);
let subscription = a.event(function () { });
assert.equal(firstCount, 1);
assert.equal(lastCount, 0);
subscription.dispose();
assert.equal(firstCount, 1);
assert.equal(lastCount, 1);
subscription = a.event(function () { });
assert.equal(firstCount, 2);
assert.equal(lastCount, 1);
});
test('throwingListener', () => {
const origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => null);
try {
let a = new Emitter<undefined>();
let hit = false;
a.event(function () {
// eslint-disable-next-line no-throw-literal
throw 9;
});
a.event(function () {
hit = true;
});
a.fire(undefined);
assert.equal(hit, true);
} finally {
Errors.setUnexpectedErrorHandler(origErrorHandler);
}
});
test('reusing event function and context', function () {
let counter = 0;
function listener() {
counter += 1;
}
const context = {};
let emitter = new Emitter<undefined>();
let reg1 = emitter.event(listener, context);
let reg2 = emitter.event(listener, context);
emitter.fire(undefined);
assert.equal(counter, 2);
reg1.dispose();
emitter.fire(undefined);
assert.equal(counter, 3);
reg2.dispose();
emitter.fire(undefined);
assert.equal(counter, 3);
});
test('Debounce Event', function (done: () => void) {
let doc = new Samples.Document3();
let onDocDidChange = Event.debounce(doc.onDidChange, (prev: string[] | undefined, cur) => {
if (!prev) {
prev = [cur];
} else if (prev.indexOf(cur) < 0) {
prev.push(cur);
}
return prev;
}, 10);
let count = 0;
onDocDidChange(keys => {
count++;
assert.ok(keys, 'was not expecting keys.');
if (count === 1) {
doc.setText('4');
assert.deepEqual(keys, ['1', '2', '3']);
} else if (count === 2) {
assert.deepEqual(keys, ['4']);
done();
}
});
doc.setText('1');
doc.setText('2');
doc.setText('3');
});
test('Debounce Event - leading', async function () {
const emitter = new Emitter<void>();
let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true);
let calls = 0;
debounced(() => {
calls++;
});
// If the source event is fired once, the debounced (on the leading edge) event should be fired only once
emitter.fire();
await timeout(1);
assert.equal(calls, 1);
});
test('Debounce Event - leading', async function () {
const emitter = new Emitter<void>();
let debounced = Event.debounce(emitter.event, (l, e) => e, 0, /*leading=*/true);
let calls = 0;
debounced(() => {
calls++;
});
// If the source event is fired multiple times, the debounced (on the leading edge) event should be fired twice
emitter.fire();
emitter.fire();
emitter.fire();
await timeout(1);
assert.equal(calls, 2);
});
test('Debounce Event - leading reset', async function () {
const emitter = new Emitter<number>();
let debounced = Event.debounce(emitter.event, (l, e) => l ? l + 1 : 1, 0, /*leading=*/true);
let calls: number[] = [];
debounced((e) => calls.push(e));
emitter.fire(1);
emitter.fire(1);
await timeout(1);
assert.deepEqual(calls, [1, 1]);
});
test('Emitter - In Order Delivery', function () {
const a = new Emitter<string>();
const listener2Events: string[] = [];
a.event(function listener1(event) {
if (event === 'e1') {
a.fire('e2');
// assert that all events are delivered at this point
assert.deepEqual(listener2Events, ['e1', 'e2']);
}
});
a.event(function listener2(event) {
listener2Events.push(event);
});
a.fire('e1');
// assert that all events are delivered in order
assert.deepEqual(listener2Events, ['e1', 'e2']);
});
});
suite('AsyncEmitter', function () {
test('event has waitUntil-function', async function () {
interface E extends IWaitUntil {
foo: boolean;
bar: number;
}
let emitter = new AsyncEmitter<E>();
emitter.event(e => {
assert.equal(e.foo, true);
assert.equal(e.bar, 1);
assert.equal(typeof e.waitUntil, 'function');
});
emitter.fireAsync({ foo: true, bar: 1, }, CancellationToken.None);
emitter.dispose();
});
test('sequential delivery', async function () {
interface E extends IWaitUntil {
foo: boolean;
}
let globalState = 0;
let emitter = new AsyncEmitter<E>();
emitter.event(e => {
e.waitUntil(timeout(10).then(_ => {
assert.equal(globalState, 0);
globalState += 1;
}));
});
emitter.event(e => {
e.waitUntil(timeout(1).then(_ => {
assert.equal(globalState, 1);
globalState += 1;
}));
});
await emitter.fireAsync({ foo: true }, CancellationToken.None);
assert.equal(globalState, 2);
});
test('sequential, in-order delivery', async function () {
interface E extends IWaitUntil {
foo: number;
}
let events: number[] = [];
let done = false;
let emitter = new AsyncEmitter<E>();
// e1
emitter.event(e => {
e.waitUntil(timeout(10).then(async _ => {
if (e.foo === 1) {
await emitter.fireAsync({ foo: 2 }, CancellationToken.None);
assert.deepEqual(events, [1, 2]);
done = true;
}
}));
});
// e2
emitter.event(e => {
events.push(e.foo);
e.waitUntil(timeout(7));
});
await emitter.fireAsync({ foo: 1 }, CancellationToken.None);
assert.ok(done);
});
test('catch errors', async function () {
const origErrorHandler = Errors.errorHandler.getUnexpectedErrorHandler();
Errors.setUnexpectedErrorHandler(() => null);
interface E extends IWaitUntil {
foo: boolean;
}
let globalState = 0;
let emitter = new AsyncEmitter<E>();
emitter.event(e => {
globalState += 1;
e.waitUntil(new Promise((_r, reject) => reject(new Error())));
});
emitter.event(e => {
globalState += 1;
e.waitUntil(timeout(10));
});
await emitter.fireAsync({ foo: true }, CancellationToken.None).then(() => {
assert.equal(globalState, 2);
}).catch(e => {
console.log(e);
assert.ok(false);
});
Errors.setUnexpectedErrorHandler(origErrorHandler);
});
});
suite('PausableEmitter', function () {
test('basic', function () {
const data: number[] = [];
const emitter = new PauseableEmitter<number>();
emitter.event(e => data.push(e));
emitter.fire(1);
emitter.fire(2);
assert.deepEqual(data, [1, 2]);
});
test('pause/resume - no merge', function () {
const data: number[] = [];
const emitter = new PauseableEmitter<number>();
emitter.event(e => data.push(e));
emitter.fire(1);
emitter.fire(2);
assert.deepEqual(data, [1, 2]);
emitter.pause();
emitter.fire(3);
emitter.fire(4);
assert.deepEqual(data, [1, 2]);
emitter.resume();
assert.deepEqual(data, [1, 2, 3, 4]);
emitter.fire(5);
assert.deepEqual(data, [1, 2, 3, 4, 5]);
});
test('pause/resume - merge', function () {
const data: number[] = [];
const emitter = new PauseableEmitter<number>({ merge: (a) => a.reduce((p, c) => p + c, 0) });
emitter.event(e => data.push(e));
emitter.fire(1);
emitter.fire(2);
assert.deepEqual(data, [1, 2]);
emitter.pause();
emitter.fire(3);
emitter.fire(4);
assert.deepEqual(data, [1, 2]);
emitter.resume();
assert.deepEqual(data, [1, 2, 7]);
emitter.fire(5);
assert.deepEqual(data, [1, 2, 7, 5]);
});
test('double pause/resume', function () {
const data: number[] = [];
const emitter = new PauseableEmitter<number>();
emitter.event(e => data.push(e));
emitter.fire(1);
emitter.fire(2);
assert.deepEqual(data, [1, 2]);
emitter.pause();
emitter.pause();
emitter.fire(3);
emitter.fire(4);
assert.deepEqual(data, [1, 2]);
emitter.resume();
assert.deepEqual(data, [1, 2]);
emitter.resume();
assert.deepEqual(data, [1, 2, 3, 4]);
emitter.resume();
assert.deepEqual(data, [1, 2, 3, 4]);
});
test('resume, no pause', function () {
const data: number[] = [];
const emitter = new PauseableEmitter<number>();
emitter.event(e => data.push(e));
emitter.fire(1);
emitter.fire(2);
assert.deepEqual(data, [1, 2]);
emitter.resume();
emitter.fire(3);
assert.deepEqual(data, [1, 2, 3]);
});
test('nested pause', function () {
const data: number[] = [];
const emitter = new PauseableEmitter<number>();
let once = true;
emitter.event(e => {
data.push(e);
if (once) {
emitter.pause();
once = false;
}
});
emitter.event(e => {
data.push(e);
});
emitter.pause();
emitter.fire(1);
emitter.fire(2);
assert.deepEqual(data, []);
emitter.resume();
assert.deepEqual(data, [1, 1]); // paused after first event
emitter.resume();
assert.deepEqual(data, [1, 1, 2, 2]); // remaing event delivered
emitter.fire(3);
assert.deepEqual(data, [1, 1, 2, 2, 3, 3]);
});
});
suite('Event utils', () => {
suite('EventBufferer', () => {
test('should not buffer when not wrapped', () => {
const bufferer = new EventBufferer();
const counter = new Samples.EventCounter();
const emitter = new Emitter<void>();
const event = bufferer.wrapEvent(emitter.event);
const listener = event(counter.onEvent, counter);
assert.equal(counter.count, 0);
emitter.fire();
assert.equal(counter.count, 1);
emitter.fire();
assert.equal(counter.count, 2);
emitter.fire();
assert.equal(counter.count, 3);
listener.dispose();
});
test('should buffer when wrapped', () => {
const bufferer = new EventBufferer();
const counter = new Samples.EventCounter();
const emitter = new Emitter<void>();
const event = bufferer.wrapEvent(emitter.event);
const listener = event(counter.onEvent, counter);
assert.equal(counter.count, 0);
emitter.fire();
assert.equal(counter.count, 1);
bufferer.bufferEvents(() => {
emitter.fire();
assert.equal(counter.count, 1);
emitter.fire();
assert.equal(counter.count, 1);
});
assert.equal(counter.count, 3);
emitter.fire();
assert.equal(counter.count, 4);
listener.dispose();
});
test('once', () => {
const emitter = new Emitter<void>();
let counter1 = 0, counter2 = 0, counter3 = 0;
const listener1 = emitter.event(() => counter1++);
const listener2 = Event.once(emitter.event)(() => counter2++);
const listener3 = Event.once(emitter.event)(() => counter3++);
assert.equal(counter1, 0);
assert.equal(counter2, 0);
assert.equal(counter3, 0);
listener3.dispose();
emitter.fire();
assert.equal(counter1, 1);
assert.equal(counter2, 1);
assert.equal(counter3, 0);
emitter.fire();
assert.equal(counter1, 2);
assert.equal(counter2, 1);
assert.equal(counter3, 0);
listener1.dispose();
listener2.dispose();
});
});
suite('fromPromise', () => {
test('should emit when done', async () => {
let count = 0;
const event = Event.fromPromise(Promise.resolve(null));
event(() => count++);
assert.equal(count, 0);
await timeout(10);
assert.equal(count, 1);
});
test('should emit when done - setTimeout', async () => {
let count = 0;
const promise = timeout(5);
const event = Event.fromPromise(promise);
event(() => count++);
assert.equal(count, 0);
await promise;
assert.equal(count, 1);
});
});
suite('stopwatch', () => {
test('should emit', () => {
const emitter = new Emitter<void>();
const event = Event.stopwatch(emitter.event);
return new Promise((c, e) => {
event(duration => {
try {
assert(duration > 0);
} catch (err) {
e(err);
}
c(undefined);
});
setTimeout(() => emitter.fire(), 10);
});
});
});
suite('buffer', () => {
test('should buffer events', () => {
const result: number[] = [];
const emitter = new Emitter<number>();
const event = emitter.event;
const bufferedEvent = Event.buffer(event);
emitter.fire(1);
emitter.fire(2);
emitter.fire(3);
assert.deepEqual(result, []);
const listener = bufferedEvent(num => result.push(num));
assert.deepEqual(result, [1, 2, 3]);
emitter.fire(4);
assert.deepEqual(result, [1, 2, 3, 4]);
listener.dispose();
emitter.fire(5);
assert.deepEqual(result, [1, 2, 3, 4]);
});
test('should buffer events on next tick', async () => {
const result: number[] = [];
const emitter = new Emitter<number>();
const event = emitter.event;
const bufferedEvent = Event.buffer(event, true);
emitter.fire(1);
emitter.fire(2);
emitter.fire(3);
assert.deepEqual(result, []);
const listener = bufferedEvent(num => result.push(num));
assert.deepEqual(result, []);
await timeout(10);
emitter.fire(4);
assert.deepEqual(result, [1, 2, 3, 4]);
listener.dispose();
emitter.fire(5);
assert.deepEqual(result, [1, 2, 3, 4]);
});
test('should fire initial buffer events', () => {
const result: number[] = [];
const emitter = new Emitter<number>();
const event = emitter.event;
const bufferedEvent = Event.buffer(event, false, [-2, -1, 0]);
emitter.fire(1);
emitter.fire(2);
emitter.fire(3);
assert.deepEqual(result, []);
bufferedEvent(num => result.push(num));
assert.deepEqual(result, [-2, -1, 0, 1, 2, 3]);
});
});
suite('EventMultiplexer', () => {
test('works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
m.event(r => result.push(r));
const e1 = new Emitter<number>();
m.add(e1.event);
assert.deepEqual(result, []);
e1.fire(0);
assert.deepEqual(result, [0]);
});
test('multiplexer dispose works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
m.event(r => result.push(r));
const e1 = new Emitter<number>();
m.add(e1.event);
assert.deepEqual(result, []);
e1.fire(0);
assert.deepEqual(result, [0]);
m.dispose();
assert.deepEqual(result, [0]);
e1.fire(0);
assert.deepEqual(result, [0]);
});
test('event dispose works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
m.event(r => result.push(r));
const e1 = new Emitter<number>();
m.add(e1.event);
assert.deepEqual(result, []);
e1.fire(0);
assert.deepEqual(result, [0]);
e1.dispose();
assert.deepEqual(result, [0]);
e1.fire(0);
assert.deepEqual(result, [0]);
});
test('mutliplexer event dispose works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
m.event(r => result.push(r));
const e1 = new Emitter<number>();
const l1 = m.add(e1.event);
assert.deepEqual(result, []);
e1.fire(0);
assert.deepEqual(result, [0]);
l1.dispose();
assert.deepEqual(result, [0]);
e1.fire(0);
assert.deepEqual(result, [0]);
});
test('hot start works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
m.event(r => result.push(r));
const e1 = new Emitter<number>();
m.add(e1.event);
const e2 = new Emitter<number>();
m.add(e2.event);
const e3 = new Emitter<number>();
m.add(e3.event);
e1.fire(1);
e2.fire(2);
e3.fire(3);
assert.deepEqual(result, [1, 2, 3]);
});
test('cold start works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
const e1 = new Emitter<number>();
m.add(e1.event);
const e2 = new Emitter<number>();
m.add(e2.event);
const e3 = new Emitter<number>();
m.add(e3.event);
m.event(r => result.push(r));
e1.fire(1);
e2.fire(2);
e3.fire(3);
assert.deepEqual(result, [1, 2, 3]);
});
test('late add works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
const e1 = new Emitter<number>();
m.add(e1.event);
const e2 = new Emitter<number>();
m.add(e2.event);
m.event(r => result.push(r));
e1.fire(1);
e2.fire(2);
const e3 = new Emitter<number>();
m.add(e3.event);
e3.fire(3);
assert.deepEqual(result, [1, 2, 3]);
});
test('add dispose works', () => {
const result: number[] = [];
const m = new EventMultiplexer<number>();
const e1 = new Emitter<number>();
m.add(e1.event);
const e2 = new Emitter<number>();
m.add(e2.event);
m.event(r => result.push(r));
e1.fire(1);
e2.fire(2);
const e3 = new Emitter<number>();
const l3 = m.add(e3.event);
e3.fire(3);
assert.deepEqual(result, [1, 2, 3]);
l3.dispose();
e3.fire(4);
assert.deepEqual(result, [1, 2, 3]);
e2.fire(4);
e1.fire(5);
assert.deepEqual(result, [1, 2, 3, 4, 5]);
});
});
test('latch', () => {
const emitter = new Emitter<number>();
const event = Event.latch(emitter.event);
const result: number[] = [];
const listener = event(num => result.push(num));
assert.deepEqual(result, []);
emitter.fire(1);
assert.deepEqual(result, [1]);
emitter.fire(2);
assert.deepEqual(result, [1, 2]);
emitter.fire(2);
assert.deepEqual(result, [1, 2]);
emitter.fire(1);
assert.deepEqual(result, [1, 2, 1]);
emitter.fire(1);
assert.deepEqual(result, [1, 2, 1]);
emitter.fire(3);
assert.deepEqual(result, [1, 2, 1, 3]);
emitter.fire(3);
assert.deepEqual(result, [1, 2, 1, 3]);
emitter.fire(3);
assert.deepEqual(result, [1, 2, 1, 3]);
listener.dispose();
});
});

View File

@@ -0,0 +1,177 @@
/*---------------------------------------------------------------------------------------------
* 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 * as extpath from 'vs/base/common/extpath';
import * as platform from 'vs/base/common/platform';
import { CharCode } from 'vs/base/common/charCode';
suite('Paths', () => {
test('toForwardSlashes', () => {
assert.equal(extpath.toSlashes('\\\\server\\share\\some\\path'), '//server/share/some/path');
assert.equal(extpath.toSlashes('c:\\test'), 'c:/test');
assert.equal(extpath.toSlashes('foo\\bar'), 'foo/bar');
assert.equal(extpath.toSlashes('/user/far'), '/user/far');
});
test('getRoot', () => {
assert.equal(extpath.getRoot('/user/far'), '/');
assert.equal(extpath.getRoot('\\\\server\\share\\some\\path'), '//server/share/');
assert.equal(extpath.getRoot('//server/share/some/path'), '//server/share/');
assert.equal(extpath.getRoot('//server/share'), '/');
assert.equal(extpath.getRoot('//server'), '/');
assert.equal(extpath.getRoot('//server//'), '/');
assert.equal(extpath.getRoot('c:/user/far'), 'c:/');
assert.equal(extpath.getRoot('c:user/far'), 'c:');
assert.equal(extpath.getRoot('http://www'), '');
assert.equal(extpath.getRoot('http://www/'), 'http://www/');
assert.equal(extpath.getRoot('file:///foo'), 'file:///');
assert.equal(extpath.getRoot('file://foo'), '');
});
test('isUNC', () => {
if (platform.isWindows) {
assert.ok(!extpath.isUNC('foo'));
assert.ok(!extpath.isUNC('/foo'));
assert.ok(!extpath.isUNC('\\foo'));
assert.ok(!extpath.isUNC('\\\\foo'));
assert.ok(extpath.isUNC('\\\\a\\b'));
assert.ok(!extpath.isUNC('//a/b'));
assert.ok(extpath.isUNC('\\\\server\\share'));
assert.ok(extpath.isUNC('\\\\server\\share\\'));
assert.ok(extpath.isUNC('\\\\server\\share\\path'));
}
});
test('isValidBasename', () => {
assert.ok(!extpath.isValidBasename(null));
assert.ok(!extpath.isValidBasename(''));
assert.ok(extpath.isValidBasename('test.txt'));
assert.ok(!extpath.isValidBasename('/test.txt'));
assert.ok(!extpath.isValidBasename('\\test.txt'));
if (platform.isWindows) {
assert.ok(!extpath.isValidBasename('aux'));
assert.ok(!extpath.isValidBasename('Aux'));
assert.ok(!extpath.isValidBasename('LPT0'));
assert.ok(!extpath.isValidBasename('aux.txt'));
assert.ok(!extpath.isValidBasename('com0.abc'));
assert.ok(extpath.isValidBasename('LPT00'));
assert.ok(extpath.isValidBasename('aux1'));
assert.ok(extpath.isValidBasename('aux1.txt'));
assert.ok(extpath.isValidBasename('aux1.aux.txt'));
assert.ok(!extpath.isValidBasename('test.txt.'));
assert.ok(!extpath.isValidBasename('test.txt..'));
assert.ok(!extpath.isValidBasename('test.txt '));
assert.ok(!extpath.isValidBasename('test.txt\t'));
assert.ok(!extpath.isValidBasename('tes:t.txt'));
assert.ok(!extpath.isValidBasename('tes"t.txt'));
}
});
test('sanitizeFilePath', () => {
if (platform.isWindows) {
assert.equal(extpath.sanitizeFilePath('.', 'C:\\the\\cwd'), 'C:\\the\\cwd');
assert.equal(extpath.sanitizeFilePath('', 'C:\\the\\cwd'), 'C:\\the\\cwd');
assert.equal(extpath.sanitizeFilePath('C:', 'C:\\the\\cwd'), 'C:\\');
assert.equal(extpath.sanitizeFilePath('C:\\', 'C:\\the\\cwd'), 'C:\\');
assert.equal(extpath.sanitizeFilePath('C:\\\\', 'C:\\the\\cwd'), 'C:\\');
assert.equal(extpath.sanitizeFilePath('C:\\folder\\my.txt', 'C:\\the\\cwd'), 'C:\\folder\\my.txt');
assert.equal(extpath.sanitizeFilePath('C:\\folder\\my', 'C:\\the\\cwd'), 'C:\\folder\\my');
assert.equal(extpath.sanitizeFilePath('C:\\folder\\..\\my', 'C:\\the\\cwd'), 'C:\\my');
assert.equal(extpath.sanitizeFilePath('C:\\folder\\my\\', 'C:\\the\\cwd'), 'C:\\folder\\my');
assert.equal(extpath.sanitizeFilePath('C:\\folder\\my\\\\\\', 'C:\\the\\cwd'), 'C:\\folder\\my');
assert.equal(extpath.sanitizeFilePath('my.txt', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt');
assert.equal(extpath.sanitizeFilePath('my.txt\\', 'C:\\the\\cwd'), 'C:\\the\\cwd\\my.txt');
assert.equal(extpath.sanitizeFilePath('\\\\localhost\\folder\\my', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my');
assert.equal(extpath.sanitizeFilePath('\\\\localhost\\folder\\my\\', 'C:\\the\\cwd'), '\\\\localhost\\folder\\my');
} else {
assert.equal(extpath.sanitizeFilePath('.', '/the/cwd'), '/the/cwd');
assert.equal(extpath.sanitizeFilePath('', '/the/cwd'), '/the/cwd');
assert.equal(extpath.sanitizeFilePath('/', '/the/cwd'), '/');
assert.equal(extpath.sanitizeFilePath('/folder/my.txt', '/the/cwd'), '/folder/my.txt');
assert.equal(extpath.sanitizeFilePath('/folder/my', '/the/cwd'), '/folder/my');
assert.equal(extpath.sanitizeFilePath('/folder/../my', '/the/cwd'), '/my');
assert.equal(extpath.sanitizeFilePath('/folder/my/', '/the/cwd'), '/folder/my');
assert.equal(extpath.sanitizeFilePath('/folder/my///', '/the/cwd'), '/folder/my');
assert.equal(extpath.sanitizeFilePath('my.txt', '/the/cwd'), '/the/cwd/my.txt');
assert.equal(extpath.sanitizeFilePath('my.txt/', '/the/cwd'), '/the/cwd/my.txt');
}
});
test('isRoot', () => {
if (platform.isWindows) {
assert.ok(extpath.isRootOrDriveLetter('c:'));
assert.ok(extpath.isRootOrDriveLetter('D:'));
assert.ok(extpath.isRootOrDriveLetter('D:/'));
assert.ok(extpath.isRootOrDriveLetter('D:\\'));
assert.ok(!extpath.isRootOrDriveLetter('D:\\path'));
assert.ok(!extpath.isRootOrDriveLetter('D:/path'));
} else {
assert.ok(extpath.isRootOrDriveLetter('/'));
assert.ok(!extpath.isRootOrDriveLetter('/path'));
}
});
test('isWindowsDriveLetter', () => {
assert.ok(!extpath.isWindowsDriveLetter(0));
assert.ok(!extpath.isWindowsDriveLetter(-1));
assert.ok(extpath.isWindowsDriveLetter(CharCode.A));
assert.ok(extpath.isWindowsDriveLetter(CharCode.z));
});
test('indexOfPath', () => {
assert.equal(extpath.indexOfPath('/foo', '/bar', true), -1);
assert.equal(extpath.indexOfPath('/foo', '/FOO', false), -1);
assert.equal(extpath.indexOfPath('/foo', '/FOO', true), 0);
assert.equal(extpath.indexOfPath('/some/long/path', '/some/long', false), 0);
assert.equal(extpath.indexOfPath('/some/long/path', '/PATH', true), 10);
});
test('parseLineAndColumnAware', () => {
let res = extpath.parseLineAndColumnAware('/foo/bar');
assert.equal(res.path, '/foo/bar');
assert.equal(res.line, undefined);
assert.equal(res.column, undefined);
res = extpath.parseLineAndColumnAware('/foo/bar:33');
assert.equal(res.path, '/foo/bar');
assert.equal(res.line, 33);
assert.equal(res.column, 1);
res = extpath.parseLineAndColumnAware('/foo/bar:33:34');
assert.equal(res.path, '/foo/bar');
assert.equal(res.line, 33);
assert.equal(res.column, 34);
res = extpath.parseLineAndColumnAware('C:\\foo\\bar');
assert.equal(res.path, 'C:\\foo\\bar');
assert.equal(res.line, undefined);
assert.equal(res.column, undefined);
res = extpath.parseLineAndColumnAware('C:\\foo\\bar:33');
assert.equal(res.path, 'C:\\foo\\bar');
assert.equal(res.line, 33);
assert.equal(res.column, 1);
res = extpath.parseLineAndColumnAware('C:\\foo\\bar:33:34');
assert.equal(res.path, 'C:\\foo\\bar');
assert.equal(res.line, 33);
assert.equal(res.column, 34);
res = extpath.parseLineAndColumnAware('/foo/bar:abb');
assert.equal(res.path, '/foo/bar:abb');
assert.equal(res.line, undefined);
assert.equal(res.column, undefined);
});
});

View File

@@ -0,0 +1,5 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const data: string[];

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as filters from 'vs/base/common/filters';
import { data } from './filters.perf.data';
const patterns = ['cci', 'ida', 'pos', 'CCI', 'enbled', 'callback', 'gGame', 'cons', 'zyx', 'aBc'];
const _enablePerf = false;
function perfSuite(name: string, callback: (this: Mocha.ISuiteCallbackContext) => void) {
if (_enablePerf) {
suite(name, callback);
}
}
perfSuite('Performance - fuzzyMatch', function () {
console.log(`Matching ${data.length} items against ${patterns.length} patterns (${data.length * patterns.length} operations) `);
function perfTest(name: string, match: filters.FuzzyScorer) {
test(name, () => {
const t1 = Date.now();
let count = 0;
for (let i = 0; i < 2; i++) {
for (const pattern of patterns) {
const patternLow = pattern.toLowerCase();
for (const item of data) {
count += 1;
match(pattern, patternLow, 0, item, item.toLowerCase(), 0, false);
}
}
}
const d = Date.now() - t1;
console.log(name, `${d}ms, ${Math.round(count / d) * 15}/15ms, ${Math.round(count / d)}/1ms`);
});
}
perfTest('fuzzyScore', filters.fuzzyScore);
perfTest('fuzzyScoreGraceful', filters.fuzzyScoreGraceful);
perfTest('fuzzyScoreGracefulAggressive', filters.fuzzyScoreGracefulAggressive);
});
perfSuite('Performance - IFilter', function () {
function perfTest(name: string, match: filters.IFilter) {
test(name, () => {
const t1 = Date.now();
let count = 0;
for (let i = 0; i < 2; i++) {
for (const pattern of patterns) {
for (const item of data) {
count += 1;
match(pattern, item);
}
}
}
const d = Date.now() - t1;
console.log(name, `${d}ms, ${Math.round(count / d) * 15}/15ms, ${Math.round(count / d)}/1ms`);
});
}
perfTest('matchesFuzzy', filters.matchesFuzzy);
perfTest('matchesFuzzy2', filters.matchesFuzzy2);
perfTest('matchesPrefix', filters.matchesPrefix);
perfTest('matchesContiguousSubString', filters.matchesContiguousSubString);
perfTest('matchesCamelCase', filters.matchesCamelCase);
});

View File

@@ -0,0 +1,541 @@
/*---------------------------------------------------------------------------------------------
* 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 { IFilter, or, matchesPrefix, matchesStrictPrefix, matchesCamelCase, matchesSubString, matchesContiguousSubString, matchesWords, fuzzyScore, IMatch, fuzzyScoreGraceful, fuzzyScoreGracefulAggressive, FuzzyScorer, createMatches } from 'vs/base/common/filters';
function filterOk(filter: IFilter, word: string, wordToMatchAgainst: string, highlights?: { start: number; end: number; }[]) {
let r = filter(word, wordToMatchAgainst);
assert(r, `${word} didn't match ${wordToMatchAgainst}`);
if (highlights) {
assert.deepEqual(r, highlights);
}
}
function filterNotOk(filter: IFilter, word: string, wordToMatchAgainst: string) {
assert(!filter(word, wordToMatchAgainst), `${word} matched ${wordToMatchAgainst}`);
}
suite('Filters', () => {
test('or', () => {
let filter: IFilter;
let counters: number[];
let newFilter = function (i: number, r: boolean): IFilter {
return function (): IMatch[] { counters[i]++; return r as any; };
};
counters = [0, 0];
filter = or(newFilter(0, false), newFilter(1, false));
filterNotOk(filter, 'anything', 'anything');
assert.deepEqual(counters, [1, 1]);
counters = [0, 0];
filter = or(newFilter(0, true), newFilter(1, false));
filterOk(filter, 'anything', 'anything');
assert.deepEqual(counters, [1, 0]);
counters = [0, 0];
filter = or(newFilter(0, true), newFilter(1, true));
filterOk(filter, 'anything', 'anything');
assert.deepEqual(counters, [1, 0]);
counters = [0, 0];
filter = or(newFilter(0, false), newFilter(1, true));
filterOk(filter, 'anything', 'anything');
assert.deepEqual(counters, [1, 1]);
});
test('PrefixFilter - case sensitive', function () {
filterNotOk(matchesStrictPrefix, '', '');
filterOk(matchesStrictPrefix, '', 'anything', []);
filterOk(matchesStrictPrefix, 'alpha', 'alpha', [{ start: 0, end: 5 }]);
filterOk(matchesStrictPrefix, 'alpha', 'alphasomething', [{ start: 0, end: 5 }]);
filterNotOk(matchesStrictPrefix, 'alpha', 'alp');
filterOk(matchesStrictPrefix, 'a', 'alpha', [{ start: 0, end: 1 }]);
filterNotOk(matchesStrictPrefix, 'x', 'alpha');
filterNotOk(matchesStrictPrefix, 'A', 'alpha');
filterNotOk(matchesStrictPrefix, 'AlPh', 'alPHA');
});
test('PrefixFilter - ignore case', function () {
filterOk(matchesPrefix, 'alpha', 'alpha', [{ start: 0, end: 5 }]);
filterOk(matchesPrefix, 'alpha', 'alphasomething', [{ start: 0, end: 5 }]);
filterNotOk(matchesPrefix, 'alpha', 'alp');
filterOk(matchesPrefix, 'a', 'alpha', [{ start: 0, end: 1 }]);
filterOk(matchesPrefix, 'ä', 'Älpha', [{ start: 0, end: 1 }]);
filterNotOk(matchesPrefix, 'x', 'alpha');
filterOk(matchesPrefix, 'A', 'alpha', [{ start: 0, end: 1 }]);
filterOk(matchesPrefix, 'AlPh', 'alPHA', [{ start: 0, end: 4 }]);
filterNotOk(matchesPrefix, 'T', '4'); // see https://github.com/microsoft/vscode/issues/22401
});
test('CamelCaseFilter', () => {
filterNotOk(matchesCamelCase, '', '');
filterOk(matchesCamelCase, '', 'anything', []);
filterOk(matchesCamelCase, 'alpha', 'alpha', [{ start: 0, end: 5 }]);
filterOk(matchesCamelCase, 'AlPhA', 'alpha', [{ start: 0, end: 5 }]);
filterOk(matchesCamelCase, 'alpha', 'alphasomething', [{ start: 0, end: 5 }]);
filterNotOk(matchesCamelCase, 'alpha', 'alp');
filterOk(matchesCamelCase, 'c', 'CamelCaseRocks', [
{ start: 0, end: 1 }
]);
filterOk(matchesCamelCase, 'cc', 'CamelCaseRocks', [
{ start: 0, end: 1 },
{ start: 5, end: 6 }
]);
filterOk(matchesCamelCase, 'ccr', 'CamelCaseRocks', [
{ start: 0, end: 1 },
{ start: 5, end: 6 },
{ start: 9, end: 10 }
]);
filterOk(matchesCamelCase, 'cacr', 'CamelCaseRocks', [
{ start: 0, end: 2 },
{ start: 5, end: 6 },
{ start: 9, end: 10 }
]);
filterOk(matchesCamelCase, 'cacar', 'CamelCaseRocks', [
{ start: 0, end: 2 },
{ start: 5, end: 7 },
{ start: 9, end: 10 }
]);
filterOk(matchesCamelCase, 'ccarocks', 'CamelCaseRocks', [
{ start: 0, end: 1 },
{ start: 5, end: 7 },
{ start: 9, end: 14 }
]);
filterOk(matchesCamelCase, 'cr', 'CamelCaseRocks', [
{ start: 0, end: 1 },
{ start: 9, end: 10 }
]);
filterOk(matchesCamelCase, 'fba', 'FooBarAbe', [
{ start: 0, end: 1 },
{ start: 3, end: 5 }
]);
filterOk(matchesCamelCase, 'fbar', 'FooBarAbe', [
{ start: 0, end: 1 },
{ start: 3, end: 6 }
]);
filterOk(matchesCamelCase, 'fbara', 'FooBarAbe', [
{ start: 0, end: 1 },
{ start: 3, end: 7 }
]);
filterOk(matchesCamelCase, 'fbaa', 'FooBarAbe', [
{ start: 0, end: 1 },
{ start: 3, end: 5 },
{ start: 6, end: 7 }
]);
filterOk(matchesCamelCase, 'fbaab', 'FooBarAbe', [
{ start: 0, end: 1 },
{ start: 3, end: 5 },
{ start: 6, end: 8 }
]);
filterOk(matchesCamelCase, 'c2d', 'canvasCreation2D', [
{ start: 0, end: 1 },
{ start: 14, end: 16 }
]);
filterOk(matchesCamelCase, 'cce', '_canvasCreationEvent', [
{ start: 1, end: 2 },
{ start: 7, end: 8 },
{ start: 15, end: 16 }
]);
});
test('CamelCaseFilter - #19256', function () {
assert(matchesCamelCase('Debug Console', 'Open: Debug Console'));
assert(matchesCamelCase('Debug console', 'Open: Debug Console'));
assert(matchesCamelCase('debug console', 'Open: Debug Console'));
});
test('matchesContiguousSubString', () => {
filterOk(matchesContiguousSubString, 'cela', 'cancelAnimationFrame()', [
{ start: 3, end: 7 }
]);
});
test('matchesSubString', () => {
filterOk(matchesSubString, 'cmm', 'cancelAnimationFrame()', [
{ start: 0, end: 1 },
{ start: 9, end: 10 },
{ start: 18, end: 19 }
]);
filterOk(matchesSubString, 'abc', 'abcabc', [
{ start: 0, end: 3 },
]);
filterOk(matchesSubString, 'abc', 'aaabbbccc', [
{ start: 0, end: 1 },
{ start: 3, end: 4 },
{ start: 6, end: 7 },
]);
});
test('matchesSubString performance (#35346)', function () {
filterNotOk(matchesSubString, 'aaaaaaaaaaaaaaaaaaaax', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
});
test('WordFilter', () => {
filterOk(matchesWords, 'alpha', 'alpha', [{ start: 0, end: 5 }]);
filterOk(matchesWords, 'alpha', 'alphasomething', [{ start: 0, end: 5 }]);
filterNotOk(matchesWords, 'alpha', 'alp');
filterOk(matchesWords, 'a', 'alpha', [{ start: 0, end: 1 }]);
filterNotOk(matchesWords, 'x', 'alpha');
filterOk(matchesWords, 'A', 'alpha', [{ start: 0, end: 1 }]);
filterOk(matchesWords, 'AlPh', 'alPHA', [{ start: 0, end: 4 }]);
assert(matchesWords('Debug Console', 'Open: Debug Console'));
filterOk(matchesWords, 'gp', 'Git: Pull', [{ start: 0, end: 1 }, { start: 5, end: 6 }]);
filterOk(matchesWords, 'g p', 'Git: Pull', [{ start: 0, end: 1 }, { start: 3, end: 4 }, { start: 5, end: 6 }]);
filterOk(matchesWords, 'gipu', 'Git: Pull', [{ start: 0, end: 2 }, { start: 5, end: 7 }]);
filterOk(matchesWords, 'gp', 'Category: Git: Pull', [{ start: 10, end: 11 }, { start: 15, end: 16 }]);
filterOk(matchesWords, 'g p', 'Category: Git: Pull', [{ start: 10, end: 11 }, { start: 13, end: 14 }, { start: 15, end: 16 }]);
filterOk(matchesWords, 'gipu', 'Category: Git: Pull', [{ start: 10, end: 12 }, { start: 15, end: 17 }]);
filterNotOk(matchesWords, 'it', 'Git: Pull');
filterNotOk(matchesWords, 'll', 'Git: Pull');
filterOk(matchesWords, 'git: プル', 'git: プル', [{ start: 0, end: 7 }]);
filterOk(matchesWords, 'git プル', 'git: プル', [{ start: 0, end: 4 }, { start: 5, end: 7 }]);
filterOk(matchesWords, 'öäk', 'Öhm: Älles Klar', [{ start: 0, end: 1 }, { start: 5, end: 6 }, { start: 11, end: 12 }]);
// assert.ok(matchesWords('gipu', 'Category: Git: Pull', true) === null);
// assert.deepEqual(matchesWords('pu', 'Category: Git: Pull', true), [{ start: 15, end: 17 }]);
filterOk(matchesWords, 'bar', 'foo-bar');
filterOk(matchesWords, 'bar test', 'foo-bar test');
filterOk(matchesWords, 'fbt', 'foo-bar test');
filterOk(matchesWords, 'bar test', 'foo-bar (test)');
filterOk(matchesWords, 'foo bar', 'foo (bar)');
filterNotOk(matchesWords, 'bar est', 'foo-bar test');
filterNotOk(matchesWords, 'fo ar', 'foo-bar test');
filterNotOk(matchesWords, 'for', 'foo-bar test');
filterOk(matchesWords, 'foo bar', 'foo-bar');
filterOk(matchesWords, 'foo bar', '123 foo-bar 456');
filterOk(matchesWords, 'foo+bar', 'foo-bar');
filterOk(matchesWords, 'foo-bar', 'foo bar');
filterOk(matchesWords, 'foo:bar', 'foo:bar');
});
function assertMatches(pattern: string, word: string, decoratedWord: string | undefined, filter: FuzzyScorer, opts: { patternPos?: number, wordPos?: number, firstMatchCanBeWeak?: boolean } = {}) {
let r = filter(pattern, pattern.toLowerCase(), opts.patternPos || 0, word, word.toLowerCase(), opts.wordPos || 0, opts.firstMatchCanBeWeak || false);
assert.ok(!decoratedWord === !r);
if (r) {
let matches = createMatches(r);
let actualWord = '';
let pos = 0;
for (const match of matches) {
actualWord += word.substring(pos, match.start);
actualWord += '^' + word.substring(match.start, match.end).split('').join('^');
pos = match.end;
}
actualWord += word.substring(pos);
assert.equal(actualWord, decoratedWord);
}
}
test('fuzzyScore, #23215', function () {
assertMatches('tit', 'win.tit', 'win.^t^i^t', fuzzyScore);
assertMatches('title', 'win.title', 'win.^t^i^t^l^e', fuzzyScore);
assertMatches('WordCla', 'WordCharacterClassifier', '^W^o^r^dCharacter^C^l^assifier', fuzzyScore);
assertMatches('WordCCla', 'WordCharacterClassifier', '^W^o^r^d^Character^C^l^assifier', fuzzyScore);
});
test('fuzzyScore, #23332', function () {
assertMatches('dete', '"editor.quickSuggestionsDelay"', undefined, fuzzyScore);
});
test('fuzzyScore, #23190', function () {
assertMatches('c:\\do', '& \'C:\\Documents and Settings\'', '& \'^C^:^\\^D^ocuments and Settings\'', fuzzyScore);
assertMatches('c:\\do', '& \'c:\\Documents and Settings\'', '& \'^c^:^\\^D^ocuments and Settings\'', fuzzyScore);
});
test('fuzzyScore, #23581', function () {
assertMatches('close', 'css.lint.importStatement', '^css.^lint.imp^ort^Stat^ement', fuzzyScore);
assertMatches('close', 'css.colorDecorators.enable', '^css.co^l^orDecorator^s.^enable', fuzzyScore);
assertMatches('close', 'workbench.quickOpen.closeOnFocusOut', 'workbench.quickOpen.^c^l^o^s^eOnFocusOut', fuzzyScore);
assertTopScore(fuzzyScore, 'close', 2, 'css.lint.importStatement', 'css.colorDecorators.enable', 'workbench.quickOpen.closeOnFocusOut');
});
test('fuzzyScore, #23458', function () {
assertMatches('highlight', 'editorHoverHighlight', 'editorHover^H^i^g^h^l^i^g^h^t', fuzzyScore);
assertMatches('hhighlight', 'editorHoverHighlight', 'editor^Hover^H^i^g^h^l^i^g^h^t', fuzzyScore);
assertMatches('dhhighlight', 'editorHoverHighlight', undefined, fuzzyScore);
});
test('fuzzyScore, #23746', function () {
assertMatches('-moz', '-moz-foo', '^-^m^o^z-foo', fuzzyScore);
assertMatches('moz', '-moz-foo', '-^m^o^z-foo', fuzzyScore);
assertMatches('moz', '-moz-animation', '-^m^o^z-animation', fuzzyScore);
assertMatches('moza', '-moz-animation', '-^m^o^z-^animation', fuzzyScore);
});
test('fuzzyScore', () => {
assertMatches('ab', 'abA', '^a^bA', fuzzyScore);
assertMatches('ccm', 'cacmelCase', '^ca^c^melCase', fuzzyScore);
assertMatches('bti', 'the_black_knight', undefined, fuzzyScore);
assertMatches('ccm', 'camelCase', undefined, fuzzyScore);
assertMatches('cmcm', 'camelCase', undefined, fuzzyScore);
assertMatches('BK', 'the_black_knight', 'the_^black_^knight', fuzzyScore);
assertMatches('KeyboardLayout=', 'KeyboardLayout', undefined, fuzzyScore);
assertMatches('LLL', 'SVisualLoggerLogsList', 'SVisual^Logger^Logs^List', fuzzyScore);
assertMatches('LLLL', 'SVilLoLosLi', undefined, fuzzyScore);
assertMatches('LLLL', 'SVisualLoggerLogsList', undefined, fuzzyScore);
assertMatches('TEdit', 'TextEdit', '^Text^E^d^i^t', fuzzyScore);
assertMatches('TEdit', 'TextEditor', '^Text^E^d^i^tor', fuzzyScore);
assertMatches('TEdit', 'Textedit', '^T^exte^d^i^t', fuzzyScore);
assertMatches('TEdit', 'text_edit', '^text_^e^d^i^t', fuzzyScore);
assertMatches('TEditDit', 'TextEditorDecorationType', '^Text^E^d^i^tor^Decorat^ion^Type', fuzzyScore);
assertMatches('TEdit', 'TextEditorDecorationType', '^Text^E^d^i^torDecorationType', fuzzyScore);
assertMatches('Tedit', 'TextEdit', '^Text^E^d^i^t', fuzzyScore);
assertMatches('ba', '?AB?', undefined, fuzzyScore);
assertMatches('bkn', 'the_black_knight', 'the_^black_^k^night', fuzzyScore);
assertMatches('bt', 'the_black_knight', 'the_^black_knigh^t', fuzzyScore);
assertMatches('ccm', 'camelCasecm', '^camel^Casec^m', fuzzyScore);
assertMatches('fdm', 'findModel', '^fin^d^Model', fuzzyScore);
assertMatches('fob', 'foobar', '^f^oo^bar', fuzzyScore);
assertMatches('fobz', 'foobar', undefined, fuzzyScore);
assertMatches('foobar', 'foobar', '^f^o^o^b^a^r', fuzzyScore);
assertMatches('form', 'editor.formatOnSave', 'editor.^f^o^r^matOnSave', fuzzyScore);
assertMatches('g p', 'Git: Pull', '^Git:^ ^Pull', fuzzyScore);
assertMatches('g p', 'Git: Pull', '^Git:^ ^Pull', fuzzyScore);
assertMatches('gip', 'Git: Pull', '^G^it: ^Pull', fuzzyScore);
assertMatches('gip', 'Git: Pull', '^G^it: ^Pull', fuzzyScore);
assertMatches('gp', 'Git: Pull', '^Git: ^Pull', fuzzyScore);
assertMatches('gp', 'Git_Git_Pull', '^Git_Git_^Pull', fuzzyScore);
assertMatches('is', 'ImportStatement', '^Import^Statement', fuzzyScore);
assertMatches('is', 'isValid', '^i^sValid', fuzzyScore);
assertMatches('lowrd', 'lowWord', '^l^o^wWo^r^d', fuzzyScore);
assertMatches('myvable', 'myvariable', '^m^y^v^aria^b^l^e', fuzzyScore);
assertMatches('no', '', undefined, fuzzyScore);
assertMatches('no', 'match', undefined, fuzzyScore);
assertMatches('ob', 'foobar', undefined, fuzzyScore);
assertMatches('sl', 'SVisualLoggerLogsList', '^SVisual^LoggerLogsList', fuzzyScore);
assertMatches('sllll', 'SVisualLoggerLogsList', '^SVisua^l^Logger^Logs^List', fuzzyScore);
assertMatches('Three', 'HTMLHRElement', undefined, fuzzyScore);
assertMatches('Three', 'Three', '^T^h^r^e^e', fuzzyScore);
assertMatches('fo', 'barfoo', undefined, fuzzyScore);
assertMatches('fo', 'bar_foo', 'bar_^f^oo', fuzzyScore);
assertMatches('fo', 'bar_Foo', 'bar_^F^oo', fuzzyScore);
assertMatches('fo', 'bar foo', 'bar ^f^oo', fuzzyScore);
assertMatches('fo', 'bar.foo', 'bar.^f^oo', fuzzyScore);
assertMatches('fo', 'bar/foo', 'bar/^f^oo', fuzzyScore);
assertMatches('fo', 'bar\\foo', 'bar\\^f^oo', fuzzyScore);
});
test('fuzzyScore (first match can be weak)', function () {
assertMatches('Three', 'HTMLHRElement', 'H^TML^H^R^El^ement', fuzzyScore, { firstMatchCanBeWeak: true });
assertMatches('tor', 'constructor', 'construc^t^o^r', fuzzyScore, { firstMatchCanBeWeak: true });
assertMatches('ur', 'constructor', 'constr^ucto^r', fuzzyScore, { firstMatchCanBeWeak: true });
assertTopScore(fuzzyScore, 'tor', 2, 'constructor', 'Thor', 'cTor');
});
test('fuzzyScore, many matches', function () {
assertMatches(
'aaaaaa',
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
'^a^a^a^a^a^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
fuzzyScore
);
});
test('Freeze when fjfj -> jfjf, https://github.com/microsoft/vscode/issues/91807', function () {
assertMatches(
'jfjfj',
'fjfjfjfjfjfjfjfjfjfjfj',
undefined, fuzzyScore
);
assertMatches(
'jfjfjfjfjfjfjfjfjfj',
'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj',
undefined, fuzzyScore
);
assertMatches(
'jfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfj',
'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj',
undefined, fuzzyScore
);
assertMatches(
'jfjfjfjfjfjfjfjfjfj',
'fJfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj',
'f^J^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', // strong match
fuzzyScore
);
assertMatches(
'jfjfjfjfjfjfjfjfjfj',
'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj',
'f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', // any match
fuzzyScore, { firstMatchCanBeWeak: true }
);
});
test('fuzzyScore, issue #26423', function () {
assertMatches('baba', 'abababab', undefined, fuzzyScore);
assertMatches(
'fsfsfs',
'dsafdsafdsafdsafdsafdsafdsafasdfdsa',
undefined,
fuzzyScore
);
assertMatches(
'fsfsfsfsfsfsfsf',
'dsafdsafdsafdsafdsafdsafdsafasdfdsafdsafdsafdsafdsfdsafdsfdfdfasdnfdsajfndsjnafjndsajlknfdsa',
undefined,
fuzzyScore
);
});
test('Fuzzy IntelliSense matching vs Haxe metadata completion, #26995', function () {
assertMatches('f', ':Foo', ':^Foo', fuzzyScore);
assertMatches('f', ':foo', ':^foo', fuzzyScore);
});
test('Separator only match should not be weak #79558', function () {
assertMatches('.', 'foo.bar', 'foo^.bar', fuzzyScore);
});
test('Cannot set property \'1\' of undefined, #26511', function () {
let word = new Array<void>(123).join('a');
let pattern = new Array<void>(120).join('a');
fuzzyScore(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, false);
assert.ok(true); // must not explode
});
test('Vscode 1.12 no longer obeys \'sortText\' in completion items (from language server), #26096', function () {
assertMatches(' ', ' group', undefined, fuzzyScore, { patternPos: 2 });
assertMatches(' g', ' group', ' ^group', fuzzyScore, { patternPos: 2 });
assertMatches('g', ' group', ' ^group', fuzzyScore);
assertMatches('g g', ' groupGroup', undefined, fuzzyScore);
assertMatches('g g', ' group Group', ' ^group^ ^Group', fuzzyScore);
assertMatches(' g g', ' group Group', ' ^group^ ^Group', fuzzyScore, { patternPos: 1 });
assertMatches('zz', 'zzGroup', '^z^zGroup', fuzzyScore);
assertMatches('zzg', 'zzGroup', '^z^z^Group', fuzzyScore);
assertMatches('g', 'zzGroup', 'zz^Group', fuzzyScore);
});
test('patternPos isn\'t working correctly #79815', function () {
assertMatches(':p'.substr(1), 'prop', '^prop', fuzzyScore, { patternPos: 0 });
assertMatches(':p', 'prop', '^prop', fuzzyScore, { patternPos: 1 });
assertMatches(':p', 'prop', undefined, fuzzyScore, { patternPos: 2 });
assertMatches(':p', 'proP', 'pro^P', fuzzyScore, { patternPos: 1, wordPos: 1 });
assertMatches(':p', 'aprop', 'a^prop', fuzzyScore, { patternPos: 1, firstMatchCanBeWeak: true });
assertMatches(':p', 'aprop', undefined, fuzzyScore, { patternPos: 1, firstMatchCanBeWeak: false });
});
function assertTopScore(filter: typeof fuzzyScore, pattern: string, expected: number, ...words: string[]) {
let topScore = -(100 * 10);
let topIdx = 0;
for (let i = 0; i < words.length; i++) {
const word = words[i];
const m = filter(pattern, pattern.toLowerCase(), 0, word, word.toLowerCase(), 0, false);
if (m) {
const [score] = m;
if (score > topScore) {
topScore = score;
topIdx = i;
}
}
}
assert.equal(topIdx, expected, `${pattern} -> actual=${words[topIdx]} <> expected=${words[expected]}`);
}
test('topScore - fuzzyScore', function () {
assertTopScore(fuzzyScore, 'cons', 2, 'ArrayBufferConstructor', 'Console', 'console');
assertTopScore(fuzzyScore, 'Foo', 1, 'foo', 'Foo', 'foo');
// #24904
assertTopScore(fuzzyScore, 'onMess', 1, 'onmessage', 'onMessage', 'onThisMegaEscape');
assertTopScore(fuzzyScore, 'CC', 1, 'camelCase', 'CamelCase');
assertTopScore(fuzzyScore, 'cC', 0, 'camelCase', 'CamelCase');
// assertTopScore(fuzzyScore, 'cC', 1, 'ccfoo', 'camelCase');
// assertTopScore(fuzzyScore, 'cC', 1, 'ccfoo', 'camelCase', 'foo-cC-bar');
// issue #17836
// assertTopScore(fuzzyScore, 'TEdit', 1, 'TextEditorDecorationType', 'TextEdit', 'TextEditor');
assertTopScore(fuzzyScore, 'p', 4, 'parse', 'posix', 'pafdsa', 'path', 'p');
assertTopScore(fuzzyScore, 'pa', 0, 'parse', 'pafdsa', 'path');
// issue #14583
assertTopScore(fuzzyScore, 'log', 3, 'HTMLOptGroupElement', 'ScrollLogicalPosition', 'SVGFEMorphologyElement', 'log', 'logger');
assertTopScore(fuzzyScore, 'e', 2, 'AbstractWorker', 'ActiveXObject', 'else');
// issue #14446
assertTopScore(fuzzyScore, 'workbench.sideb', 1, 'workbench.editor.defaultSideBySideLayout', 'workbench.sideBar.location');
// issue #11423
assertTopScore(fuzzyScore, 'editor.r', 2, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
// assertTopScore(fuzzyScore, 'editor.R', 1, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
// assertTopScore(fuzzyScore, 'Editor.r', 0, 'diffEditor.renderSideBySide', 'editor.overviewRulerlanes', 'editor.renderControlCharacter', 'editor.renderWhitespace');
assertTopScore(fuzzyScore, '-mo', 1, '-ms-ime-mode', '-moz-columns');
// // dupe, issue #14861
assertTopScore(fuzzyScore, 'convertModelPosition', 0, 'convertModelPositionToViewPosition', 'convertViewToModelPosition');
// // dupe, issue #14942
assertTopScore(fuzzyScore, 'is', 0, 'isValidViewletId', 'import statement');
assertTopScore(fuzzyScore, 'title', 1, 'files.trimTrailingWhitespace', 'window.title');
assertTopScore(fuzzyScore, 'const', 1, 'constructor', 'const', 'cuOnstrul');
});
test('Unexpected suggestion scoring, #28791', function () {
assertTopScore(fuzzyScore, '_lines', 1, '_lineStarts', '_lines');
assertTopScore(fuzzyScore, '_lines', 1, '_lineS', '_lines');
assertTopScore(fuzzyScore, '_lineS', 0, '_lineS', '_lines');
});
test('HTML closing tag proposal filtered out #38880', function () {
assertMatches('\t\t<', '\t\t</body>', '^\t^\t^</body>', fuzzyScore, { patternPos: 0 });
assertMatches('\t\t<', '\t\t</body>', '\t\t^</body>', fuzzyScore, { patternPos: 2 });
assertMatches('\t<', '\t</body>', '\t^</body>', fuzzyScore, { patternPos: 1 });
});
test('fuzzyScoreGraceful', () => {
assertMatches('rlut', 'result', undefined, fuzzyScore);
assertMatches('rlut', 'result', '^res^u^l^t', fuzzyScoreGraceful);
assertMatches('cno', 'console', '^co^ns^ole', fuzzyScore);
assertMatches('cno', 'console', '^co^ns^ole', fuzzyScoreGraceful);
assertMatches('cno', 'console', '^c^o^nsole', fuzzyScoreGracefulAggressive);
assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGraceful);
assertMatches('cno', 'co_new', '^c^o_^new', fuzzyScoreGracefulAggressive);
});
test('List highlight filter: Not all characters from match are highlighterd #66923', () => {
assertMatches('foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo', 'barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o', fuzzyScore);
});
test('Autocompletion is matched against truncated filterText to 54 characters #74133', () => {
assertMatches(
'foo',
'ffffffffffffffffffffffffffffbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo',
'ffffffffffffffffffffffffffffbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_^f^o^o',
fuzzyScore
);
assertMatches(
'foo',
'Gffffffffffffffffffffffffffffbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar_foo',
undefined,
fuzzyScore
);
});
test('"Go to Symbol" with the exact method name doesn\'t work as expected #84787', function () {
const match = fuzzyScore(':get', ':get', 1, 'get', 'get', 0, true);
assert.ok(Boolean(match));
});
test('Suggestion is not highlighted #85826', function () {
assertMatches('SemanticTokens', 'SemanticTokensEdits', '^S^e^m^a^n^t^i^c^T^o^k^e^n^sEdits', fuzzyScore);
assertMatches('SemanticTokens', 'SemanticTokensEdits', '^S^e^m^a^n^t^i^c^T^o^k^e^n^sEdits', fuzzyScoreGracefulAggressive);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* 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 { hash, StringSHA1 } from 'vs/base/common/hash';
suite('Hash', () => {
test('string', () => {
assert.equal(hash('hello'), hash('hello'));
assert.notEqual(hash('hello'), hash('world'));
assert.notEqual(hash('hello'), hash('olleh'));
assert.notEqual(hash('hello'), hash('Hello'));
assert.notEqual(hash('hello'), hash('Hello '));
assert.notEqual(hash('h'), hash('H'));
assert.notEqual(hash('-'), hash('_'));
});
test('number', () => {
assert.equal(hash(1), hash(1));
assert.notEqual(hash(0), hash(1));
assert.notEqual(hash(1), hash(-1));
assert.notEqual(hash(0x12345678), hash(0x123456789));
});
test('boolean', () => {
assert.equal(hash(true), hash(true));
assert.notEqual(hash(true), hash(false));
});
test('array', () => {
assert.equal(hash([1, 2, 3]), hash([1, 2, 3]));
assert.equal(hash(['foo', 'bar']), hash(['foo', 'bar']));
assert.equal(hash([]), hash([]));
assert.equal(hash([]), hash(new Array()));
assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo']));
assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', null]));
assert.notEqual(hash(['foo', 'bar', null]), hash(['bar', 'foo', null]));
assert.notEqual(hash(['foo', 'bar']), hash(['bar', 'foo', undefined]));
assert.notEqual(hash(['foo', 'bar', undefined]), hash(['bar', 'foo', undefined]));
assert.notEqual(hash(['foo', 'bar', null]), hash(['foo', 'bar', undefined]));
});
test('object', () => {
assert.equal(hash({}), hash({}));
assert.equal(hash({}), hash(Object.create(null)));
assert.equal(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar' }));
assert.equal(hash({ 'foo': 'bar', 'foo2': undefined }), hash({ 'foo2': undefined, 'foo': 'bar' }));
assert.notEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' }));
assert.notEqual(hash({}), hash([]));
});
test('array - unexpected collision', function () {
const a = hash([undefined, undefined, undefined, undefined, undefined]);
const b = hash([undefined, undefined, 'HHHHHH', [{ line: 0, character: 0 }, { line: 0, character: 0 }], undefined]);
assert.notEqual(a, b);
});
test('all different', () => {
const candidates: any[] = [
null, undefined, {}, [], 0, false, true, '', ' ', [null], [undefined], [undefined, undefined], { '': undefined }, { [' ']: undefined },
'ab', 'ba', ['ab']
];
const hashes: number[] = candidates.map(hash);
for (let i = 0; i < hashes.length; i++) {
assert.equal(hashes[i], hash(candidates[i])); // verify that repeated invocation returns the same hash
for (let k = i + 1; k < hashes.length; k++) {
assert.notEqual(hashes[i], hashes[k], `Same hash ${hashes[i]} for ${JSON.stringify(candidates[i])} and ${JSON.stringify(candidates[k])}`);
}
}
});
function checkSHA1(strings: string[], expected: string) {
const hash = new StringSHA1();
for (const str of strings) {
hash.update(str);
}
const actual = hash.digest();
assert.equal(actual, expected);
}
test('sha1-1', () => {
checkSHA1(['\udd56'], '9bdb77276c1852e1fb067820472812fcf6084024');
});
test('sha1-2', () => {
checkSHA1(['\udb52'], '9bdb77276c1852e1fb067820472812fcf6084024');
});
test('sha1-3', () => {
checkSHA1(['\uda02ꑍ'], '9b483a471f22fe7e09d83f221871a987244bbd3f');
});
test('sha1-4', () => {
checkSHA1(['hello'], 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d');
});
});

View File

@@ -0,0 +1,160 @@
/*---------------------------------------------------------------------------------------------
* 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 { HistoryNavigator } from 'vs/base/common/history';
suite('History Navigator', () => {
test('create reduces the input to limit', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 2);
assert.deepEqual(['3', '4'], toArray(testObject));
});
test('create sets the position to last', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 100);
assert.equal(testObject.current(), null);
assert.equal(testObject.next(), null);
assert.equal(testObject.previous(), '4');
});
test('last returns last element', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 100);
assert.equal(testObject.first(), '1');
assert.equal(testObject.last(), '4');
});
test('first returns first element', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 3);
assert.equal('2', testObject.first());
});
test('next returns next element', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 3);
testObject.first();
assert.equal(testObject.next(), '3');
assert.equal(testObject.next(), '4');
assert.equal(testObject.next(), null);
});
test('previous returns previous element', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 3);
assert.equal(testObject.previous(), '4');
assert.equal(testObject.previous(), '3');
assert.equal(testObject.previous(), '2');
assert.equal(testObject.previous(), null);
});
test('next on last element returs null and remains on last', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 3);
testObject.first();
testObject.last();
assert.equal(testObject.current(), '4');
assert.equal(testObject.next(), null);
});
test('previous on first element returs null and remains on first', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 3);
testObject.first();
assert.equal(testObject.current(), '2');
assert.equal(testObject.previous(), null);
});
test('add reduces the input to limit', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 2);
testObject.add('5');
assert.deepEqual(toArray(testObject), ['4', '5']);
});
test('adding existing element changes the position', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 5);
testObject.add('2');
assert.deepEqual(toArray(testObject), ['1', '3', '4', '2']);
});
test('add resets the navigator to last', () => {
const testObject = new HistoryNavigator(['1', '2', '3', '4'], 3);
testObject.first();
testObject.add('5');
assert.equal(testObject.previous(), '5');
assert.equal(testObject.next(), null);
});
test('adding an existing item changes the order', () => {
const testObject = new HistoryNavigator(['1', '2', '3']);
testObject.add('1');
assert.deepEqual(['2', '3', '1'], toArray(testObject));
});
test('previous returns null if the current position is the first one', () => {
const testObject = new HistoryNavigator(['1', '2', '3']);
testObject.first();
assert.deepEqual(testObject.previous(), null);
});
test('previous returns object if the current position is not the first one', () => {
const testObject = new HistoryNavigator(['1', '2', '3']);
testObject.first();
testObject.next();
assert.deepEqual(testObject.previous(), '1');
});
test('next returns null if the current position is the last one', () => {
const testObject = new HistoryNavigator(['1', '2', '3']);
testObject.last();
assert.deepEqual(testObject.next(), null);
});
test('next returns object if the current position is not the last one', () => {
const testObject = new HistoryNavigator(['1', '2', '3']);
testObject.last();
testObject.previous();
assert.deepEqual(testObject.next(), '3');
});
test('clear', () => {
const testObject = new HistoryNavigator(['a', 'b', 'c']);
assert.equal(testObject.previous(), 'c');
testObject.clear();
assert.equal(testObject.current(), undefined);
});
function toArray(historyNavigator: HistoryNavigator<string>): Array<string | null> {
let result: Array<string | null> = [];
historyNavigator.first();
if (historyNavigator.current()) {
do {
result.push(historyNavigator.current()!);
} while (historyNavigator.next());
}
return result;
}
});

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* 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 { Iterable } from 'vs/base/common/iterator';
suite('Iterable', function () {
const customIterable = new class {
*[Symbol.iterator]() {
yield 'one';
yield 'two';
yield 'three';
}
};
test('first', function () {
assert.equal(Iterable.first([]), undefined);
assert.equal(Iterable.first([1]), 1);
assert.equal(Iterable.first(customIterable), 'one');
assert.equal(Iterable.first(customIterable), 'one'); // fresh
});
});

View File

@@ -0,0 +1,327 @@
/*---------------------------------------------------------------------------------------------
* 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 {
SyntaxKind, createScanner, parse, Node, ParseError, parseTree, ParseErrorCode, ParseOptions, ScanError
} from 'vs/base/common/json';
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
function assertKinds(text: string, ...kinds: SyntaxKind[]): void {
let scanner = createScanner(text);
let kind: SyntaxKind;
while ((kind = scanner.scan()) !== SyntaxKind.EOF) {
assert.equal(kind, kinds.shift());
}
assert.equal(kinds.length, 0);
}
function assertScanError(text: string, expectedKind: SyntaxKind, scanError: ScanError): void {
let scanner = createScanner(text);
scanner.scan();
assert.equal(scanner.getToken(), expectedKind);
assert.equal(scanner.getTokenError(), scanError);
}
function assertValidParse(input: string, expected: any, options?: ParseOptions): void {
let errors: ParseError[] = [];
let actual = parse(input, errors, options);
if (errors.length !== 0) {
assert(false, getParseErrorMessage(errors[0].error));
}
assert.deepEqual(actual, expected);
}
function assertInvalidParse(input: string, expected: any, options?: ParseOptions): void {
let errors: ParseError[] = [];
let actual = parse(input, errors, options);
assert(errors.length > 0);
assert.deepEqual(actual, expected);
}
function assertTree(input: string, expected: any, expectedErrors: number[] = [], options?: ParseOptions): void {
let errors: ParseError[] = [];
let actual = parseTree(input, errors, options);
assert.deepEqual(errors.map(e => e.error, expected), expectedErrors);
let checkParent = (node: Node) => {
if (node.children) {
for (let child of node.children) {
assert.equal(node, child.parent);
delete (<any>child).parent; // delete to avoid recursion in deep equal
checkParent(child);
}
}
};
checkParent(actual);
assert.deepEqual(actual, expected);
}
suite('JSON', () => {
test('tokens', () => {
assertKinds('{', SyntaxKind.OpenBraceToken);
assertKinds('}', SyntaxKind.CloseBraceToken);
assertKinds('[', SyntaxKind.OpenBracketToken);
assertKinds(']', SyntaxKind.CloseBracketToken);
assertKinds(':', SyntaxKind.ColonToken);
assertKinds(',', SyntaxKind.CommaToken);
});
test('comments', () => {
assertKinds('// this is a comment', SyntaxKind.LineCommentTrivia);
assertKinds('// this is a comment\n', SyntaxKind.LineCommentTrivia, SyntaxKind.LineBreakTrivia);
assertKinds('/* this is a comment*/', SyntaxKind.BlockCommentTrivia);
assertKinds('/* this is a \r\ncomment*/', SyntaxKind.BlockCommentTrivia);
assertKinds('/* this is a \ncomment*/', SyntaxKind.BlockCommentTrivia);
// unexpected end
assertKinds('/* this is a', SyntaxKind.BlockCommentTrivia);
assertKinds('/* this is a \ncomment', SyntaxKind.BlockCommentTrivia);
// broken comment
assertKinds('/ ttt', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
});
test('strings', () => {
assertKinds('"test"', SyntaxKind.StringLiteral);
assertKinds('"\\""', SyntaxKind.StringLiteral);
assertKinds('"\\/"', SyntaxKind.StringLiteral);
assertKinds('"\\b"', SyntaxKind.StringLiteral);
assertKinds('"\\f"', SyntaxKind.StringLiteral);
assertKinds('"\\n"', SyntaxKind.StringLiteral);
assertKinds('"\\r"', SyntaxKind.StringLiteral);
assertKinds('"\\t"', SyntaxKind.StringLiteral);
assertKinds('"\\v"', SyntaxKind.StringLiteral);
assertKinds('"\u88ff"', SyntaxKind.StringLiteral);
assertKinds('"\u2028"', SyntaxKind.StringLiteral);
// unexpected end
assertKinds('"test', SyntaxKind.StringLiteral);
assertKinds('"test\n"', SyntaxKind.StringLiteral, SyntaxKind.LineBreakTrivia, SyntaxKind.StringLiteral);
// invalid characters
assertScanError('"\t"', SyntaxKind.StringLiteral, ScanError.InvalidCharacter);
assertScanError('"\t "', SyntaxKind.StringLiteral, ScanError.InvalidCharacter);
});
test('numbers', () => {
assertKinds('0', SyntaxKind.NumericLiteral);
assertKinds('0.1', SyntaxKind.NumericLiteral);
assertKinds('-0.1', SyntaxKind.NumericLiteral);
assertKinds('-1', SyntaxKind.NumericLiteral);
assertKinds('1', SyntaxKind.NumericLiteral);
assertKinds('123456789', SyntaxKind.NumericLiteral);
assertKinds('10', SyntaxKind.NumericLiteral);
assertKinds('90', SyntaxKind.NumericLiteral);
assertKinds('90E+123', SyntaxKind.NumericLiteral);
assertKinds('90e+123', SyntaxKind.NumericLiteral);
assertKinds('90e-123', SyntaxKind.NumericLiteral);
assertKinds('90E-123', SyntaxKind.NumericLiteral);
assertKinds('90E123', SyntaxKind.NumericLiteral);
assertKinds('90e123', SyntaxKind.NumericLiteral);
// zero handling
assertKinds('01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
assertKinds('-01', SyntaxKind.NumericLiteral, SyntaxKind.NumericLiteral);
// unexpected end
assertKinds('-', SyntaxKind.Unknown);
assertKinds('.0', SyntaxKind.Unknown);
});
test('keywords: true, false, null', () => {
assertKinds('true', SyntaxKind.TrueKeyword);
assertKinds('false', SyntaxKind.FalseKeyword);
assertKinds('null', SyntaxKind.NullKeyword);
assertKinds('true false null',
SyntaxKind.TrueKeyword,
SyntaxKind.Trivia,
SyntaxKind.FalseKeyword,
SyntaxKind.Trivia,
SyntaxKind.NullKeyword);
// invalid words
assertKinds('nulllll', SyntaxKind.Unknown);
assertKinds('True', SyntaxKind.Unknown);
assertKinds('foo-bar', SyntaxKind.Unknown);
assertKinds('foo bar', SyntaxKind.Unknown, SyntaxKind.Trivia, SyntaxKind.Unknown);
});
test('trivia', () => {
assertKinds(' ', SyntaxKind.Trivia);
assertKinds(' \t ', SyntaxKind.Trivia);
assertKinds(' \t \n \t ', SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia);
assertKinds('\r\n', SyntaxKind.LineBreakTrivia);
assertKinds('\r', SyntaxKind.LineBreakTrivia);
assertKinds('\n', SyntaxKind.LineBreakTrivia);
assertKinds('\n\r', SyntaxKind.LineBreakTrivia, SyntaxKind.LineBreakTrivia);
assertKinds('\n \n', SyntaxKind.LineBreakTrivia, SyntaxKind.Trivia, SyntaxKind.LineBreakTrivia);
});
test('parse: literals', () => {
assertValidParse('true', true);
assertValidParse('false', false);
assertValidParse('null', null);
assertValidParse('"foo"', 'foo');
assertValidParse('"\\"-\\\\-\\/-\\b-\\f-\\n-\\r-\\t"', '"-\\-/-\b-\f-\n-\r-\t');
assertValidParse('"\\u00DC"', 'Ü');
assertValidParse('9', 9);
assertValidParse('-9', -9);
assertValidParse('0.129', 0.129);
assertValidParse('23e3', 23e3);
assertValidParse('1.2E+3', 1.2E+3);
assertValidParse('1.2E-3', 1.2E-3);
assertValidParse('1.2E-3 // comment', 1.2E-3);
});
test('parse: objects', () => {
assertValidParse('{}', {});
assertValidParse('{ "foo": true }', { foo: true });
assertValidParse('{ "bar": 8, "xoo": "foo" }', { bar: 8, xoo: 'foo' });
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
assertValidParse('{ "a": false, "b": true, "c": [ 7.4 ] }', { a: false, b: true, c: [7.4] });
assertValidParse('{ "lineComment": "//", "blockComment": ["/*", "*/"], "brackets": [ ["{", "}"], ["[", "]"], ["(", ")"] ] }', { lineComment: '//', blockComment: ['/*', '*/'], brackets: [['{', '}'], ['[', ']'], ['(', ')']] });
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} });
assertValidParse('{ "hello": { "again": { "inside": 5 }, "world": 1 }}', { hello: { again: { inside: 5 }, world: 1 } });
assertValidParse('{ "foo": /*hello*/true }', { foo: true });
});
test('parse: arrays', () => {
assertValidParse('[]', []);
assertValidParse('[ [], [ [] ]]', [[], [[]]]);
assertValidParse('[ 1, 2, 3 ]', [1, 2, 3]);
assertValidParse('[ { "a": null } ]', [{ a: null }]);
});
test('parse: objects with errors', () => {
assertInvalidParse('{,}', {});
assertInvalidParse('{ "foo": true, }', { foo: true }, { allowTrailingComma: false });
assertInvalidParse('{ "bar": 8 "xoo": "foo" }', { bar: 8, xoo: 'foo' });
assertInvalidParse('{ ,"bar": 8 }', { bar: 8 });
assertInvalidParse('{ ,"bar": 8, "foo" }', { bar: 8 });
assertInvalidParse('{ "bar": 8, "foo": }', { bar: 8 });
assertInvalidParse('{ 8, "foo": 9 }', { foo: 9 });
});
test('parse: array with errors', () => {
assertInvalidParse('[,]', []);
assertInvalidParse('[ 1, 2, ]', [1, 2], { allowTrailingComma: false });
assertInvalidParse('[ 1 2, 3 ]', [1, 2, 3]);
assertInvalidParse('[ ,1, 2, 3 ]', [1, 2, 3]);
assertInvalidParse('[ ,1, 2, 3, ]', [1, 2, 3], { allowTrailingComma: false });
});
test('parse: disallow commments', () => {
let options = { disallowComments: true };
assertValidParse('[ 1, 2, null, "foo" ]', [1, 2, null, 'foo'], options);
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
assertInvalidParse('{ "foo": /*comment*/ true }', { foo: true }, options);
});
test('parse: trailing comma', () => {
// default is allow
assertValidParse('{ "hello": [], }', { hello: [] });
let options = { allowTrailingComma: true };
assertValidParse('{ "hello": [], }', { hello: [] }, options);
assertValidParse('{ "hello": [] }', { hello: [] }, options);
assertValidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
assertValidParse('{ "hello": [], "world": {} }', { hello: [], world: {} }, options);
assertValidParse('{ "hello": [1,] }', { hello: [1] }, options);
options = { allowTrailingComma: false };
assertInvalidParse('{ "hello": [], }', { hello: [] }, options);
assertInvalidParse('{ "hello": [], "world": {}, }', { hello: [], world: {} }, options);
});
test('tree: literals', () => {
assertTree('true', { type: 'boolean', offset: 0, length: 4, value: true });
assertTree('false', { type: 'boolean', offset: 0, length: 5, value: false });
assertTree('null', { type: 'null', offset: 0, length: 4, value: null });
assertTree('23', { type: 'number', offset: 0, length: 2, value: 23 });
assertTree('-1.93e-19', { type: 'number', offset: 0, length: 9, value: -1.93e-19 });
assertTree('"hello"', { type: 'string', offset: 0, length: 7, value: 'hello' });
});
test('tree: arrays', () => {
assertTree('[]', { type: 'array', offset: 0, length: 2, children: [] });
assertTree('[ 1 ]', { type: 'array', offset: 0, length: 5, children: [{ type: 'number', offset: 2, length: 1, value: 1 }] });
assertTree('[ 1,"x"]', {
type: 'array', offset: 0, length: 8, children: [
{ type: 'number', offset: 2, length: 1, value: 1 },
{ type: 'string', offset: 4, length: 3, value: 'x' }
]
});
assertTree('[[]]', {
type: 'array', offset: 0, length: 4, children: [
{ type: 'array', offset: 1, length: 2, children: [] }
]
});
});
test('tree: objects', () => {
assertTree('{ }', { type: 'object', offset: 0, length: 3, children: [] });
assertTree('{ "val": 1 }', {
type: 'object', offset: 0, length: 12, children: [
{
type: 'property', offset: 2, length: 8, colonOffset: 7, children: [
{ type: 'string', offset: 2, length: 5, value: 'val' },
{ type: 'number', offset: 9, length: 1, value: 1 }
]
}
]
});
assertTree('{"id": "$", "v": [ null, null] }',
{
type: 'object', offset: 0, length: 32, children: [
{
type: 'property', offset: 1, length: 9, colonOffset: 5, children: [
{ type: 'string', offset: 1, length: 4, value: 'id' },
{ type: 'string', offset: 7, length: 3, value: '$' }
]
},
{
type: 'property', offset: 12, length: 18, colonOffset: 15, children: [
{ type: 'string', offset: 12, length: 3, value: 'v' },
{
type: 'array', offset: 17, length: 13, children: [
{ type: 'null', offset: 19, length: 4, value: null },
{ type: 'null', offset: 25, length: 4, value: null }
]
}
]
}
]
}
);
assertTree('{ "id": { "foo": { } } , }',
{
type: 'object', offset: 0, length: 27, children: [
{
type: 'property', offset: 3, length: 20, colonOffset: 7, children: [
{ type: 'string', offset: 3, length: 4, value: 'id' },
{
type: 'object', offset: 9, length: 14, children: [
{
type: 'property', offset: 11, length: 10, colonOffset: 16, children: [
{ type: 'string', offset: 11, length: 5, value: 'foo' },
{ type: 'object', offset: 18, length: 3, children: [] }
]
}
]
}
]
}
]
}
, [ParseErrorCode.PropertyNameExpected, ParseErrorCode.ValueExpected], { allowTrailingComma: false });
});
});

View File

@@ -0,0 +1,193 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { FormattingOptions, Edit } from 'vs/base/common/jsonFormatter';
import { setProperty, removeProperty } from 'vs/base/common/jsonEdit';
import * as assert from 'assert';
suite('JSON - edits', () => {
function assertEdit(content: string, edits: Edit[], expected: string) {
assert(edits);
let lastEditOffset = content.length;
for (let i = edits.length - 1; i >= 0; i--) {
let edit = edits[i];
assert(edit.offset >= 0 && edit.length >= 0 && edit.offset + edit.length <= content.length);
assert(typeof edit.content === 'string');
assert(lastEditOffset >= edit.offset + edit.length); // make sure all edits are ordered
lastEditOffset = edit.offset;
content = content.substring(0, edit.offset) + edit.content + content.substring(edit.offset + edit.length);
}
assert.equal(content, expected);
}
let formatterOptions: FormattingOptions = {
insertSpaces: true,
tabSize: 2,
eol: '\n'
};
test('set property', () => {
let content = '{\n "x": "y"\n}';
let edits = setProperty(content, ['x'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "x": "bar"\n}');
content = 'true';
edits = setProperty(content, [], 'bar', formatterOptions);
assertEdit(content, edits, '"bar"');
content = '{\n "x": "y"\n}';
edits = setProperty(content, ['x'], { key: true }, formatterOptions);
assertEdit(content, edits, '{\n "x": {\n "key": true\n }\n}');
content = '{\n "a": "b", "x": "y"\n}';
edits = setProperty(content, ['a'], null, formatterOptions);
assertEdit(content, edits, '{\n "a": null, "x": "y"\n}');
});
test('insert property', () => {
let content = '{}';
let edits = setProperty(content, ['foo'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "foo": "bar"\n}');
edits = setProperty(content, ['foo', 'foo2'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "foo": {\n "foo2": "bar"\n }\n}');
content = '{\n}';
edits = setProperty(content, ['foo'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "foo": "bar"\n}');
content = ' {\n }';
edits = setProperty(content, ['foo'], 'bar', formatterOptions);
assertEdit(content, edits, ' {\n "foo": "bar"\n }');
content = '{\n "x": "y"\n}';
edits = setProperty(content, ['foo'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "x": "y",\n "foo": "bar"\n}');
content = '{\n "x": "y"\n}';
edits = setProperty(content, ['e'], 'null', formatterOptions);
assertEdit(content, edits, '{\n "x": "y",\n "e": "null"\n}');
edits = setProperty(content, ['x'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "x": "bar"\n}');
content = '{\n "x": {\n "a": 1,\n "b": true\n }\n}\n';
edits = setProperty(content, ['x'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "x": "bar"\n}\n');
edits = setProperty(content, ['x', 'b'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "x": {\n "a": 1,\n "b": "bar"\n }\n}\n');
edits = setProperty(content, ['x', 'c'], 'bar', formatterOptions, () => 0);
assertEdit(content, edits, '{\n "x": {\n "c": "bar",\n "a": 1,\n "b": true\n }\n}\n');
edits = setProperty(content, ['x', 'c'], 'bar', formatterOptions, () => 1);
assertEdit(content, edits, '{\n "x": {\n "a": 1,\n "c": "bar",\n "b": true\n }\n}\n');
edits = setProperty(content, ['x', 'c'], 'bar', formatterOptions, () => 2);
assertEdit(content, edits, '{\n "x": {\n "a": 1,\n "b": true,\n "c": "bar"\n }\n}\n');
edits = setProperty(content, ['c'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "x": {\n "a": 1,\n "b": true\n },\n "c": "bar"\n}\n');
content = '{\n "a": [\n {\n } \n ] \n}';
edits = setProperty(content, ['foo'], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "a": [\n {\n } \n ],\n "foo": "bar"\n}');
content = '';
edits = setProperty(content, ['foo', 0], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "foo": [\n "bar"\n ]\n}');
content = '//comment';
edits = setProperty(content, ['foo', 0], 'bar', formatterOptions);
assertEdit(content, edits, '{\n "foo": [\n "bar"\n ]\n} //comment');
});
test('remove property', () => {
let content = '{\n "x": "y"\n}';
let edits = removeProperty(content, ['x'], formatterOptions);
assertEdit(content, edits, '{\n}');
content = '{\n "x": "y", "a": []\n}';
edits = removeProperty(content, ['x'], formatterOptions);
assertEdit(content, edits, '{\n "a": []\n}');
content = '{\n "x": "y", "a": []\n}';
edits = removeProperty(content, ['a'], formatterOptions);
assertEdit(content, edits, '{\n "x": "y"\n}');
});
test('insert item at 0', () => {
let content = '[\n 2,\n 3\n]';
let edits = setProperty(content, [0], 1, formatterOptions);
assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]');
});
test('insert item at 0 in empty array', () => {
let content = '[\n]';
let edits = setProperty(content, [0], 1, formatterOptions);
assertEdit(content, edits, '[\n 1\n]');
});
test('insert item at an index', () => {
let content = '[\n 1,\n 3\n]';
let edits = setProperty(content, [1], 2, formatterOptions);
assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]');
});
test('insert item at an index im empty array', () => {
let content = '[\n]';
let edits = setProperty(content, [1], 1, formatterOptions);
assertEdit(content, edits, '[\n 1\n]');
});
test('insert item at end index', () => {
let content = '[\n 1,\n 2\n]';
let edits = setProperty(content, [2], 3, formatterOptions);
assertEdit(content, edits, '[\n 1,\n 2,\n 3\n]');
});
test('insert item at end to empty array', () => {
let content = '[\n]';
let edits = setProperty(content, [-1], 'bar', formatterOptions);
assertEdit(content, edits, '[\n "bar"\n]');
});
test('insert item at end', () => {
let content = '[\n 1,\n 2\n]';
let edits = setProperty(content, [-1], 'bar', formatterOptions);
assertEdit(content, edits, '[\n 1,\n 2,\n "bar"\n]');
});
test('remove item in array with one item', () => {
let content = '[\n 1\n]';
let edits = setProperty(content, [0], undefined, formatterOptions);
assertEdit(content, edits, '[]');
});
test('remove item in the middle of the array', () => {
let content = '[\n 1,\n 2,\n 3\n]';
let edits = setProperty(content, [1], undefined, formatterOptions);
assertEdit(content, edits, '[\n 1,\n 3\n]');
});
test('remove last item in the array', () => {
let content = '[\n 1,\n 2,\n "bar"\n]';
let edits = setProperty(content, [2], undefined, formatterOptions);
assertEdit(content, edits, '[\n 1,\n 2\n]');
});
test('remove last item in the array if ends with comma', () => {
let content = '[\n 1,\n "foo",\n "bar",\n]';
let edits = setProperty(content, [2], undefined, formatterOptions);
assertEdit(content, edits, '[\n 1,\n "foo"\n]');
});
test('remove last item in the array if there is a comment in the beginning', () => {
let content = '// This is a comment\n[\n 1,\n "foo",\n "bar"\n]';
let edits = setProperty(content, [2], undefined, formatterOptions);
assertEdit(content, edits, '// This is a comment\n[\n 1,\n "foo"\n]');
});
});

View File

@@ -0,0 +1,441 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as Formatter from 'vs/base/common/jsonFormatter';
import * as assert from 'assert';
suite('JSON - formatter', () => {
function format(content: string, expected: string, insertSpaces = true) {
let range: Formatter.Range | undefined = undefined;
const rangeStart = content.indexOf('|');
const rangeEnd = content.lastIndexOf('|');
if (rangeStart !== -1 && rangeEnd !== -1) {
content = content.substring(0, rangeStart) + content.substring(rangeStart + 1, rangeEnd) + content.substring(rangeEnd + 1);
range = { offset: rangeStart, length: rangeEnd - rangeStart };
}
const edits = Formatter.format(content, range, { tabSize: 2, insertSpaces: insertSpaces, eol: '\n' });
let lastEditOffset = content.length;
for (let i = edits.length - 1; i >= 0; i--) {
let edit = edits[i];
assert(edit.offset >= 0 && edit.length >= 0 && edit.offset + edit.length <= content.length);
assert(typeof edit.content === 'string');
assert(lastEditOffset >= edit.offset + edit.length); // make sure all edits are ordered
lastEditOffset = edit.offset;
content = content.substring(0, edit.offset) + edit.content + content.substring(edit.offset + edit.length);
}
assert.equal(content, expected);
}
test('object - single property', () => {
const content = [
'{"x" : 1}'
].join('\n');
const expected = [
'{',
' "x": 1',
'}'
].join('\n');
format(content, expected);
});
test('object - multiple properties', () => {
const content = [
'{"x" : 1, "y" : "foo", "z" : true}'
].join('\n');
const expected = [
'{',
' "x": 1,',
' "y": "foo",',
' "z": true',
'}'
].join('\n');
format(content, expected);
});
test('object - no properties ', () => {
const content = [
'{"x" : { }, "y" : {}}'
].join('\n');
const expected = [
'{',
' "x": {},',
' "y": {}',
'}'
].join('\n');
format(content, expected);
});
test('object - nesting', () => {
const content = [
'{"x" : { "y" : { "z" : { }}, "a": true}}'
].join('\n');
const expected = [
'{',
' "x": {',
' "y": {',
' "z": {}',
' },',
' "a": true',
' }',
'}'
].join('\n');
format(content, expected);
});
test('array - single items', () => {
const content = [
'["[]"]'
].join('\n');
const expected = [
'[',
' "[]"',
']'
].join('\n');
format(content, expected);
});
test('array - multiple items', () => {
const content = [
'[true,null,1.2]'
].join('\n');
const expected = [
'[',
' true,',
' null,',
' 1.2',
']'
].join('\n');
format(content, expected);
});
test('array - no items', () => {
const content = [
'[ ]'
].join('\n');
const expected = [
'[]'
].join('\n');
format(content, expected);
});
test('array - nesting', () => {
const content = [
'[ [], [ [ {} ], "a" ] ]'
].join('\n');
const expected = [
'[',
' [],',
' [',
' [',
' {}',
' ],',
' "a"',
' ]',
']',
].join('\n');
format(content, expected);
});
test('syntax errors', () => {
const content = [
'[ null 1.2 ]'
].join('\n');
const expected = [
'[',
' null 1.2',
']',
].join('\n');
format(content, expected);
});
test('empty lines', () => {
const content = [
'{',
'"a": true,',
'',
'"b": true',
'}',
].join('\n');
const expected = [
'{',
'\t"a": true,',
'\t"b": true',
'}',
].join('\n');
format(content, expected, false);
});
test('single line comment', () => {
const content = [
'[ ',
'//comment',
'"foo", "bar"',
'] '
].join('\n');
const expected = [
'[',
' //comment',
' "foo",',
' "bar"',
']',
].join('\n');
format(content, expected);
});
test('block line comment', () => {
const content = [
'[{',
' /*comment*/ ',
'"foo" : true',
'}] '
].join('\n');
const expected = [
'[',
' {',
' /*comment*/',
' "foo": true',
' }',
']',
].join('\n');
format(content, expected);
});
test('single line comment on same line', () => {
const content = [
' { ',
' "a": {}// comment ',
' } '
].join('\n');
const expected = [
'{',
' "a": {} // comment ',
'}',
].join('\n');
format(content, expected);
});
test('single line comment on same line 2', () => {
const content = [
'{ //comment',
'}'
].join('\n');
const expected = [
'{ //comment',
'}'
].join('\n');
format(content, expected);
});
test('block comment on same line', () => {
const content = [
'{ "a": {}, /*comment*/ ',
' /*comment*/ "b": {}, ',
' "c": {/*comment*/} } ',
].join('\n');
const expected = [
'{',
' "a": {}, /*comment*/',
' /*comment*/ "b": {},',
' "c": { /*comment*/}',
'}',
].join('\n');
format(content, expected);
});
test('block comment on same line advanced', () => {
const content = [
' { "d": [',
' null',
' ] /*comment*/',
' ,"e": /*comment*/ [null] }',
].join('\n');
const expected = [
'{',
' "d": [',
' null',
' ] /*comment*/,',
' "e": /*comment*/ [',
' null',
' ]',
'}',
].join('\n');
format(content, expected);
});
test('multiple block comments on same line', () => {
const content = [
'{ "a": {} /*comment*/, /*comment*/ ',
' /*comment*/ "b": {} /*comment*/ } '
].join('\n');
const expected = [
'{',
' "a": {} /*comment*/, /*comment*/',
' /*comment*/ "b": {} /*comment*/',
'}',
].join('\n');
format(content, expected);
});
test('multiple mixed comments on same line', () => {
const content = [
'[ /*comment*/ /*comment*/ // comment ',
']'
].join('\n');
const expected = [
'[ /*comment*/ /*comment*/ // comment ',
']'
].join('\n');
format(content, expected);
});
test('range', () => {
const content = [
'{ "a": {},',
'|"b": [null, null]|',
'} '
].join('\n');
const expected = [
'{ "a": {},',
'"b": [',
' null,',
' null',
']',
'} ',
].join('\n');
format(content, expected);
});
test('range with existing indent', () => {
const content = [
'{ "a": {},',
' |"b": [null],',
'"c": {}',
'}|'
].join('\n');
const expected = [
'{ "a": {},',
' "b": [',
' null',
' ],',
' "c": {}',
'}',
].join('\n');
format(content, expected);
});
test('range with existing indent - tabs', () => {
const content = [
'{ "a": {},',
'| "b": [null], ',
'"c": {}',
'} | '
].join('\n');
const expected = [
'{ "a": {},',
'\t"b": [',
'\t\tnull',
'\t],',
'\t"c": {}',
'}',
].join('\n');
format(content, expected, false);
});
test('block comment none-line breaking symbols', () => {
const content = [
'{ "a": [ 1',
'/* comment */',
', 2',
'/* comment */',
']',
'/* comment */',
',',
' "b": true',
'/* comment */',
'}'
].join('\n');
const expected = [
'{',
' "a": [',
' 1',
' /* comment */',
' ,',
' 2',
' /* comment */',
' ]',
' /* comment */',
' ,',
' "b": true',
' /* comment */',
'}',
].join('\n');
format(content, expected);
});
test('line comment after none-line breaking symbols', () => {
const content = [
'{ "a":',
'// comment',
'null,',
' "b"',
'// comment',
': null',
'// comment',
'}'
].join('\n');
const expected = [
'{',
' "a":',
' // comment',
' null,',
' "b"',
' // comment',
' : null',
' // comment',
'}',
].join('\n');
format(content, expected);
});
});

View File

@@ -0,0 +1,99 @@
/*---------------------------------------------------------------------------------------------
* 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 { ChordKeybinding, KeyChord, KeyCode, KeyMod, Keybinding, SimpleKeybinding, createKeybinding } from 'vs/base/common/keyCodes';
import { OperatingSystem } from 'vs/base/common/platform';
suite('keyCodes', () => {
function testBinaryEncoding(expected: Keybinding | null, k: number, OS: OperatingSystem): void {
assert.deepEqual(createKeybinding(k, OS), expected);
}
test('MAC binary encoding', () => {
function test(expected: Keybinding | null, k: number): void {
testBinaryEncoding(expected, k, OperatingSystem.Macintosh);
}
test(null, 0);
test(new SimpleKeybinding(false, false, false, false, KeyCode.Enter).toChord(), KeyCode.Enter);
test(new SimpleKeybinding(true, false, false, false, KeyCode.Enter).toChord(), KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, false, true, false, KeyCode.Enter).toChord(), KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(true, false, true, false, KeyCode.Enter).toChord(), KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, true, false, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyCode.Enter);
test(new SimpleKeybinding(true, true, false, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, true, true, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(true, true, true, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, false, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyCode.Enter);
test(new SimpleKeybinding(true, false, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, false, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(true, false, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, true, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter);
test(new SimpleKeybinding(true, true, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, true, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(true, true, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(
new ChordKeybinding([
new SimpleKeybinding(false, false, false, false, KeyCode.Enter),
new SimpleKeybinding(false, false, false, false, KeyCode.Tab)
]),
KeyChord(KeyCode.Enter, KeyCode.Tab)
);
test(
new ChordKeybinding([
new SimpleKeybinding(false, false, false, true, KeyCode.KEY_Y),
new SimpleKeybinding(false, false, false, false, KeyCode.KEY_Z)
]),
KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z)
);
});
test('WINDOWS & LINUX binary encoding', () => {
[OperatingSystem.Linux, OperatingSystem.Windows].forEach((OS) => {
function test(expected: Keybinding | null, k: number): void {
testBinaryEncoding(expected, k, OS);
}
test(null, 0);
test(new SimpleKeybinding(false, false, false, false, KeyCode.Enter).toChord(), KeyCode.Enter);
test(new SimpleKeybinding(false, false, false, true, KeyCode.Enter).toChord(), KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, false, true, false, KeyCode.Enter).toChord(), KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(false, false, true, true, KeyCode.Enter).toChord(), KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, true, false, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyCode.Enter);
test(new SimpleKeybinding(false, true, false, true, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(false, true, true, false, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(false, true, true, true, KeyCode.Enter).toChord(), KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(true, false, false, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyCode.Enter);
test(new SimpleKeybinding(true, false, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(true, false, true, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(true, false, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(true, true, false, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Enter);
test(new SimpleKeybinding(true, true, false, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.WinCtrl | KeyCode.Enter);
test(new SimpleKeybinding(true, true, true, false, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyCode.Enter);
test(new SimpleKeybinding(true, true, true, true, KeyCode.Enter).toChord(), KeyMod.CtrlCmd | KeyMod.Shift | KeyMod.Alt | KeyMod.WinCtrl | KeyCode.Enter);
test(
new ChordKeybinding([
new SimpleKeybinding(false, false, false, false, KeyCode.Enter),
new SimpleKeybinding(false, false, false, false, KeyCode.Tab)
]),
KeyChord(KeyCode.Enter, KeyCode.Tab)
);
test(
new ChordKeybinding([
new SimpleKeybinding(true, false, false, false, KeyCode.KEY_Y),
new SimpleKeybinding(false, false, false, false, KeyCode.KEY_Z)
]),
KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_Y, KeyCode.KEY_Z)
);
});
});
});

View File

@@ -0,0 +1,182 @@
/*---------------------------------------------------------------------------------------------
* 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 * as labels from 'vs/base/common/labels';
import * as platform from 'vs/base/common/platform';
suite('Labels', () => {
test('shorten - windows', () => {
if (!platform.isWindows) {
assert.ok(true);
return;
}
// nothing to shorten
assert.deepEqual(labels.shorten(['a']), ['a']);
assert.deepEqual(labels.shorten(['a', 'b']), ['a', 'b']);
assert.deepEqual(labels.shorten(['a', 'b', 'c']), ['a', 'b', 'c']);
// completely different paths
assert.deepEqual(labels.shorten(['a\\b', 'c\\d', 'e\\f']), ['…\\b', '…\\d', '…\\f']);
// same beginning
assert.deepEqual(labels.shorten(['a', 'a\\b']), ['a', '…\\b']);
assert.deepEqual(labels.shorten(['a\\b', 'a\\b\\c']), ['…\\b', '…\\c']);
assert.deepEqual(labels.shorten(['a', 'a\\b', 'a\\b\\c']), ['a', '…\\b', '…\\c']);
assert.deepEqual(labels.shorten(['x:\\a\\b', 'x:\\a\\c']), ['x:\\…\\b', 'x:\\…\\c']);
assert.deepEqual(labels.shorten(['\\\\a\\b', '\\\\a\\c']), ['\\\\a\\b', '\\\\a\\c']);
// same ending
assert.deepEqual(labels.shorten(['a', 'b\\a']), ['a', 'b\\…']);
assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\c']), ['a\\…', 'd\\…']);
assert.deepEqual(labels.shorten(['a\\b\\c\\d', 'f\\b\\c\\d']), ['a\\…', 'f\\…']);
assert.deepEqual(labels.shorten(['d\\e\\a\\b\\c', 'd\\b\\c']), ['…\\a\\…', 'd\\b\\…']);
assert.deepEqual(labels.shorten(['a\\b\\c\\d', 'a\\f\\b\\c\\d']), ['a\\b\\…', '…\\f\\…']);
assert.deepEqual(labels.shorten(['a\\b\\a', 'b\\b\\a']), ['a\\b\\…', 'b\\b\\…']);
assert.deepEqual(labels.shorten(['d\\f\\a\\b\\c', 'h\\d\\b\\c']), ['…\\a\\…', 'h\\…']);
assert.deepEqual(labels.shorten(['a\\b\\c', 'x:\\0\\a\\b\\c']), ['a\\b\\c', 'x:\\0\\…']);
assert.deepEqual(labels.shorten(['x:\\a\\b\\c', 'x:\\0\\a\\b\\c']), ['x:\\a\\…', 'x:\\0\\…']);
assert.deepEqual(labels.shorten(['x:\\a\\b', 'y:\\a\\b']), ['x:\\…', 'y:\\…']);
assert.deepEqual(labels.shorten(['x:\\a', 'x:\\c']), ['x:\\a', 'x:\\c']);
assert.deepEqual(labels.shorten(['x:\\a\\b', 'y:\\x\\a\\b']), ['x:\\…', 'y:\\…']);
assert.deepEqual(labels.shorten(['\\\\x\\b', '\\\\y\\b']), ['\\\\x\\…', '\\\\y\\…']);
assert.deepEqual(labels.shorten(['\\\\x\\a', '\\\\x\\b']), ['\\\\x\\a', '\\\\x\\b']);
// same name ending
assert.deepEqual(labels.shorten(['a\\b', 'a\\c', 'a\\e-b']), ['…\\b', '…\\c', '…\\e-b']);
// same in the middle
assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\e']), ['…\\c', '…\\e']);
// case-sensetive
assert.deepEqual(labels.shorten(['a\\b\\c', 'd\\b\\C']), ['…\\c', '…\\C']);
// empty or null
assert.deepEqual(labels.shorten(['', null!]), ['.\\', null]);
assert.deepEqual(labels.shorten(['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']), ['a', 'a\\b', 'a\\b\\c', 'd\\b\\c', 'd\\b']);
assert.deepEqual(labels.shorten(['a', 'a\\b', 'b']), ['a', 'a\\b', 'b']);
assert.deepEqual(labels.shorten(['', 'a', 'b', 'b\\c', 'a\\c']), ['.\\', 'a', 'b', 'b\\c', 'a\\c']);
assert.deepEqual(labels.shorten(['src\\vs\\workbench\\parts\\execution\\electron-browser', 'src\\vs\\workbench\\parts\\execution\\electron-browser\\something', 'src\\vs\\workbench\\parts\\terminal\\electron-browser']), ['…\\execution\\electron-browser', '…\\something', '…\\terminal\\…']);
});
test('shorten - not windows', () => {
if (platform.isWindows) {
assert.ok(true);
return;
}
// nothing to shorten
assert.deepEqual(labels.shorten(['a']), ['a']);
assert.deepEqual(labels.shorten(['a', 'b']), ['a', 'b']);
assert.deepEqual(labels.shorten(['a', 'b', 'c']), ['a', 'b', 'c']);
// completely different paths
assert.deepEqual(labels.shorten(['a/b', 'c/d', 'e/f']), ['…/b', '…/d', '…/f']);
// same beginning
assert.deepEqual(labels.shorten(['a', 'a/b']), ['a', '…/b']);
assert.deepEqual(labels.shorten(['a/b', 'a/b/c']), ['…/b', '…/c']);
assert.deepEqual(labels.shorten(['a', 'a/b', 'a/b/c']), ['a', '…/b', '…/c']);
assert.deepEqual(labels.shorten(['/a/b', '/a/c']), ['/a/b', '/a/c']);
// same ending
assert.deepEqual(labels.shorten(['a', 'b/a']), ['a', 'b/…']);
assert.deepEqual(labels.shorten(['a/b/c', 'd/b/c']), ['a/…', 'd/…']);
assert.deepEqual(labels.shorten(['a/b/c/d', 'f/b/c/d']), ['a/…', 'f/…']);
assert.deepEqual(labels.shorten(['d/e/a/b/c', 'd/b/c']), ['…/a/…', 'd/b/…']);
assert.deepEqual(labels.shorten(['a/b/c/d', 'a/f/b/c/d']), ['a/b/…', '…/f/…']);
assert.deepEqual(labels.shorten(['a/b/a', 'b/b/a']), ['a/b/…', 'b/b/…']);
assert.deepEqual(labels.shorten(['d/f/a/b/c', 'h/d/b/c']), ['…/a/…', 'h/…']);
assert.deepEqual(labels.shorten(['/x/b', '/y/b']), ['/x/…', '/y/…']);
// same name ending
assert.deepEqual(labels.shorten(['a/b', 'a/c', 'a/e-b']), ['…/b', '…/c', '…/e-b']);
// same in the middle
assert.deepEqual(labels.shorten(['a/b/c', 'd/b/e']), ['…/c', '…/e']);
// case-sensitive
assert.deepEqual(labels.shorten(['a/b/c', 'd/b/C']), ['…/c', '…/C']);
// empty or null
assert.deepEqual(labels.shorten(['', null!]), ['./', null]);
assert.deepEqual(labels.shorten(['a', 'a/b', 'a/b/c', 'd/b/c', 'd/b']), ['a', 'a/b', 'a/b/c', 'd/b/c', 'd/b']);
assert.deepEqual(labels.shorten(['a', 'a/b', 'b']), ['a', 'a/b', 'b']);
assert.deepEqual(labels.shorten(['', 'a', 'b', 'b/c', 'a/c']), ['./', 'a', 'b', 'b/c', 'a/c']);
});
test('template', () => {
// simple
assert.strictEqual(labels.template('Foo Bar'), 'Foo Bar');
assert.strictEqual(labels.template('Foo${}Bar'), 'FooBar');
assert.strictEqual(labels.template('$FooBar'), '');
assert.strictEqual(labels.template('}FooBar'), '}FooBar');
assert.strictEqual(labels.template('Foo ${one} Bar', { one: 'value' }), 'Foo value Bar');
assert.strictEqual(labels.template('Foo ${one} Bar ${two}', { one: 'value', two: 'other value' }), 'Foo value Bar other value');
// conditional separator
assert.strictEqual(labels.template('Foo${separator}Bar'), 'FooBar');
assert.strictEqual(labels.template('Foo${separator}Bar', { separator: { label: ' - ' } }), 'Foo - Bar');
assert.strictEqual(labels.template('${separator}Foo${separator}Bar', { value: 'something', separator: { label: ' - ' } }), 'Foo - Bar');
assert.strictEqual(labels.template('${value} Foo${separator}Bar', { value: 'something', separator: { label: ' - ' } }), 'something Foo - Bar');
// // real world example (macOS)
let t = '${activeEditorShort}${separator}${rootName}';
assert.strictEqual(labels.template(t, { activeEditorShort: '', rootName: '', separator: { label: ' - ' } }), '');
assert.strictEqual(labels.template(t, { activeEditorShort: '', rootName: 'root', separator: { label: ' - ' } }), 'root');
assert.strictEqual(labels.template(t, { activeEditorShort: 'markdown.txt', rootName: 'root', separator: { label: ' - ' } }), 'markdown.txt - root');
// // real world example (other)
t = '${dirty}${activeEditorShort}${separator}${rootName}${separator}${appName}';
assert.strictEqual(labels.template(t, { dirty: '', activeEditorShort: '', rootName: '', appName: '', separator: { label: ' - ' } }), '');
assert.strictEqual(labels.template(t, { dirty: '', activeEditorShort: '', rootName: '', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'Visual Studio Code');
assert.strictEqual(labels.template(t, { dirty: '', activeEditorShort: 'Untitled-1', rootName: '', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'Untitled-1 - Visual Studio Code');
assert.strictEqual(labels.template(t, { dirty: '', activeEditorShort: '', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'monaco - Visual Studio Code');
assert.strictEqual(labels.template(t, { dirty: '', activeEditorShort: 'somefile.txt', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), 'somefile.txt - monaco - Visual Studio Code');
assert.strictEqual(labels.template(t, { dirty: '* ', activeEditorShort: 'somefile.txt', rootName: 'monaco', appName: 'Visual Studio Code', separator: { label: ' - ' } }), '* somefile.txt - monaco - Visual Studio Code');
});
test('getBaseLabel - unix', () => {
if (platform.isWindows) {
assert.ok(true);
return;
}
assert.equal(labels.getBaseLabel('/some/folder/file.txt'), 'file.txt');
assert.equal(labels.getBaseLabel('/some/folder'), 'folder');
assert.equal(labels.getBaseLabel('/'), '/');
});
test('getBaseLabel - windows', () => {
if (!platform.isWindows) {
assert.ok(true);
return;
}
assert.equal(labels.getBaseLabel('c:'), 'C:');
assert.equal(labels.getBaseLabel('c:\\'), 'C:');
assert.equal(labels.getBaseLabel('c:\\some\\folder\\file.txt'), 'file.txt');
assert.equal(labels.getBaseLabel('c:\\some\\folder'), 'folder');
});
test('mnemonicButtonLabel', () => {
assert.equal(labels.mnemonicButtonLabel('Hello World'), 'Hello World');
assert.equal(labels.mnemonicButtonLabel(''), '');
if (platform.isWindows) {
assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello && World');
assert.equal(labels.mnemonicButtonLabel('Do &&not Save & Continue'), 'Do &not Save && Continue');
} else if (platform.isMacintosh) {
assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World');
assert.equal(labels.mnemonicButtonLabel('Do &&not Save & Continue'), 'Do not Save & Continue');
} else {
assert.equal(labels.mnemonicButtonLabel('Hello & World'), 'Hello & World');
assert.equal(labels.mnemonicButtonLabel('Do &&not Save & Continue'), 'Do _not Save & Continue');
}
});
});

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* 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 { Lazy } from 'vs/base/common/lazy';
suite('Lazy', () => {
test('lazy values should only be resolved once', () => {
let counter = 0;
const value = new Lazy(() => ++counter);
assert.strictEqual(value.hasValue(), false);
assert.strictEqual(value.getValue(), 1);
assert.strictEqual(value.hasValue(), true);
assert.strictEqual(value.getValue(), 1); // make sure we did not evaluate again
});
test('lazy values handle error case', () => {
let counter = 0;
const value = new Lazy(() => { throw new Error(`${++counter}`); });
assert.strictEqual(value.hasValue(), false);
assert.throws(() => value.getValue(), /\b1\b/);
assert.strictEqual(value.hasValue(), true);
assert.throws(() => value.getValue(), /\b1\b/);
});
test('map should not cause lazy values to be re-resolved', () => {
let outer = 0;
let inner = 10;
const outerLazy = new Lazy(() => ++outer);
const innerLazy = outerLazy.map(x => [x, ++inner]);
assert.strictEqual(outerLazy.hasValue(), false);
assert.strictEqual(innerLazy.hasValue(), false);
assert.deepEqual(innerLazy.getValue(), [1, 11]);
assert.strictEqual(outerLazy.hasValue(), true);
assert.strictEqual(innerLazy.hasValue(), true);
assert.strictEqual(outerLazy.getValue(), 1);
// make sure we did not evaluate again
assert.strictEqual(outerLazy.getValue(), 1);
assert.deepEqual(innerLazy.getValue(), [1, 11]);
});
test('map should handle error values', () => {
let outer = 0;
let inner = 10;
const outerLazy = new Lazy(() => { throw new Error(`${++outer}`); });
const innerLazy = outerLazy.map(x => { throw new Error(`${++inner}`); });
assert.strictEqual(outerLazy.hasValue(), false);
assert.strictEqual(innerLazy.hasValue(), false);
assert.throws(() => innerLazy.getValue(), /\b1\b/); // we should get result from outer
assert.strictEqual(outerLazy.hasValue(), true);
assert.strictEqual(innerLazy.hasValue(), true);
assert.throws(() => outerLazy.getValue(), /\b1\b/);
});
});

View File

@@ -0,0 +1,191 @@
/*---------------------------------------------------------------------------------------------
* 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 { DisposableStore, dispose, IDisposable, MultiDisposeError, ReferenceCollection, toDisposable } from 'vs/base/common/lifecycle';
class Disposable implements IDisposable {
isDisposed = false;
dispose() { this.isDisposed = true; }
}
suite('Lifecycle', () => {
test('dispose single disposable', () => {
const disposable = new Disposable();
assert(!disposable.isDisposed);
dispose(disposable);
assert(disposable.isDisposed);
});
test('dispose disposable array', () => {
const disposable = new Disposable();
const disposable2 = new Disposable();
assert(!disposable.isDisposed);
assert(!disposable2.isDisposed);
dispose([disposable, disposable2]);
assert(disposable.isDisposed);
assert(disposable2.isDisposed);
});
test('dispose disposables', () => {
const disposable = new Disposable();
const disposable2 = new Disposable();
assert(!disposable.isDisposed);
assert(!disposable2.isDisposed);
dispose(disposable);
dispose(disposable2);
assert(disposable.isDisposed);
assert(disposable2.isDisposed);
});
test('dispose array should dispose all if a child throws on dispose', () => {
const disposedValues = new Set<number>();
let thrownError: any;
try {
dispose([
toDisposable(() => { disposedValues.add(1); }),
toDisposable(() => { throw new Error('I am error'); }),
toDisposable(() => { disposedValues.add(3); }),
]);
} catch (e) {
thrownError = e;
}
assert.ok(disposedValues.has(1));
assert.ok(disposedValues.has(3));
assert.strictEqual(thrownError.message, 'I am error');
});
test('dispose array should rethrow composite error if multiple entries throw on dispose', () => {
const disposedValues = new Set<number>();
let thrownError: any;
try {
dispose([
toDisposable(() => { disposedValues.add(1); }),
toDisposable(() => { throw new Error('I am error 1'); }),
toDisposable(() => { throw new Error('I am error 2'); }),
toDisposable(() => { disposedValues.add(4); }),
]);
} catch (e) {
thrownError = e;
}
assert.ok(disposedValues.has(1));
assert.ok(disposedValues.has(4));
assert.ok(thrownError instanceof MultiDisposeError);
assert.strictEqual((thrownError as MultiDisposeError).errors.length, 2);
assert.strictEqual((thrownError as MultiDisposeError).errors[0].message, 'I am error 1');
assert.strictEqual((thrownError as MultiDisposeError).errors[1].message, 'I am error 2');
});
test('Action bar has broken accessibility #100273', function () {
let array = [{ dispose() { } }, { dispose() { } }];
let array2 = dispose(array);
assert.equal(array.length, 2);
assert.equal(array2.length, 0);
assert.ok(array !== array2);
let set = new Set<IDisposable>([{ dispose() { } }, { dispose() { } }]);
let setValues = set.values();
let setValues2 = dispose(setValues);
assert.ok(setValues === setValues2);
});
});
suite('DisposableStore', () => {
test('dispose should call all child disposes even if a child throws on dispose', () => {
const disposedValues = new Set<number>();
const store = new DisposableStore();
store.add(toDisposable(() => { disposedValues.add(1); }));
store.add(toDisposable(() => { throw new Error('I am error'); }));
store.add(toDisposable(() => { disposedValues.add(3); }));
let thrownError: any;
try {
store.dispose();
} catch (e) {
thrownError = e;
}
assert.ok(disposedValues.has(1));
assert.ok(disposedValues.has(3));
assert.strictEqual(thrownError.message, 'I am error');
});
test('dispose should throw composite error if multiple children throw on dispose', () => {
const disposedValues = new Set<number>();
const store = new DisposableStore();
store.add(toDisposable(() => { disposedValues.add(1); }));
store.add(toDisposable(() => { throw new Error('I am error 1'); }));
store.add(toDisposable(() => { throw new Error('I am error 2'); }));
store.add(toDisposable(() => { disposedValues.add(4); }));
let thrownError: any;
try {
store.dispose();
} catch (e) {
thrownError = e;
}
assert.ok(disposedValues.has(1));
assert.ok(disposedValues.has(4));
assert.ok(thrownError instanceof MultiDisposeError);
assert.strictEqual((thrownError as MultiDisposeError).errors.length, 2);
assert.strictEqual((thrownError as MultiDisposeError).errors[0].message, 'I am error 1');
assert.strictEqual((thrownError as MultiDisposeError).errors[1].message, 'I am error 2');
});
});
suite('Reference Collection', () => {
class Collection extends ReferenceCollection<number> {
private _count = 0;
get count() { return this._count; }
protected createReferencedObject(key: string): number { this._count++; return key.length; }
protected destroyReferencedObject(key: string, object: number): void { this._count--; }
}
test('simple', () => {
const collection = new Collection();
const ref1 = collection.acquire('test');
assert(ref1);
assert.equal(ref1.object, 4);
assert.equal(collection.count, 1);
ref1.dispose();
assert.equal(collection.count, 0);
const ref2 = collection.acquire('test');
const ref3 = collection.acquire('test');
assert.equal(ref2.object, ref3.object);
assert.equal(collection.count, 1);
const ref4 = collection.acquire('monkey');
assert.equal(ref4.object, 6);
assert.equal(collection.count, 2);
ref2.dispose();
assert.equal(collection.count, 2);
ref3.dispose();
assert.equal(collection.count, 1);
ref4.dispose();
assert.equal(collection.count, 0);
});
});

View File

@@ -0,0 +1,137 @@
/*---------------------------------------------------------------------------------------------
* 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 { LinkedList } from 'vs/base/common/linkedList';
suite('LinkedList', function () {
function assertElements<E>(list: LinkedList<E>, ...elements: E[]) {
// check size
assert.equal(list.size, elements.length);
// assert toArray
assert.deepEqual(list.toArray(), elements);
// assert Symbol.iterator (1)
assert.deepEqual([...list], elements);
// assert Symbol.iterator (2)
for (const item of list) {
assert.equal(item, elements.shift());
}
assert.equal(elements.length, 0);
}
test('Push/Iter', () => {
const list = new LinkedList<number>();
list.push(0);
list.push(1);
list.push(2);
assertElements(list, 0, 1, 2);
});
test('Push/Remove', () => {
let list = new LinkedList<number>();
let disp = list.push(0);
list.push(1);
list.push(2);
disp();
assertElements(list, 1, 2);
list = new LinkedList<number>();
list.push(0);
disp = list.push(1);
list.push(2);
disp();
assertElements(list, 0, 2);
list = new LinkedList<number>();
list.push(0);
list.push(1);
disp = list.push(2);
disp();
assertElements(list, 0, 1);
list = new LinkedList<number>();
list.push(0);
list.push(1);
disp = list.push(2);
disp();
disp();
assertElements(list, 0, 1);
});
test('Push/toArray', () => {
let list = new LinkedList<string>();
list.push('foo');
list.push('bar');
list.push('far');
list.push('boo');
assertElements(list, 'foo', 'bar', 'far', 'boo');
});
test('unshift/Iter', () => {
const list = new LinkedList<number>();
list.unshift(0);
list.unshift(1);
list.unshift(2);
assertElements(list, 2, 1, 0);
});
test('unshift/Remove', () => {
let list = new LinkedList<number>();
let disp = list.unshift(0);
list.unshift(1);
list.unshift(2);
disp();
assertElements(list, 2, 1);
list = new LinkedList<number>();
list.unshift(0);
disp = list.unshift(1);
list.unshift(2);
disp();
assertElements(list, 2, 0);
list = new LinkedList<number>();
list.unshift(0);
list.unshift(1);
disp = list.unshift(2);
disp();
assertElements(list, 1, 0);
});
test('unshift/toArray', () => {
let list = new LinkedList<string>();
list.unshift('foo');
list.unshift('bar');
list.unshift('far');
list.unshift('boo');
assertElements(list, 'boo', 'far', 'bar', 'foo');
});
test('pop/unshift', function () {
let list = new LinkedList<string>();
list.push('a');
list.push('b');
assertElements(list, 'a', 'b');
let a = list.shift();
assert.equal(a, 'a');
assertElements(list, 'b');
list.unshift('a');
assertElements(list, 'a', 'b');
let b = list.pop();
assert.equal(b, 'b');
assertElements(list, 'a');
});
});

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* 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 { parseLinkedText } from 'vs/base/common/linkedText';
suite('LinkedText', () => {
test('parses correctly', () => {
assert.deepEqual(parseLinkedText('').nodes, []);
assert.deepEqual(parseLinkedText('hello').nodes, ['hello']);
assert.deepEqual(parseLinkedText('hello there').nodes, ['hello there']);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href).').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a title").').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a title' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a title\').').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a title' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href "and a \'title\'").').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a \'title\'' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](http://link.href \'and a "title"\').').nodes, [
'Some message with ',
{ label: 'link text', href: 'http://link.href', title: 'and a "title"' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [link text](random stuff).').nodes, [
'Some message with [link text](random stuff).'
]);
assert.deepEqual(parseLinkedText('Some message with [https link](https://link.href).').nodes, [
'Some message with ',
{ label: 'https link', href: 'https://link.href' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [https link](https:).').nodes, [
'Some message with [https link](https:).'
]);
assert.deepEqual(parseLinkedText('Some message with [a command](command:foobar).').nodes, [
'Some message with ',
{ label: 'a command', href: 'command:foobar' },
'.'
]);
assert.deepEqual(parseLinkedText('Some message with [a command](command:).').nodes, [
'Some message with [a command](command:).'
]);
assert.deepEqual(parseLinkedText('link [one](command:foo "nice") and link [two](http://foo)...').nodes, [
'link ',
{ label: 'one', href: 'command:foo', title: 'nice' },
' and link ',
{ label: 'two', href: 'http://foo' },
'...'
]);
assert.deepEqual(parseLinkedText('link\n[one](command:foo "nice")\nand link [two](http://foo)...').nodes, [
'link\n',
{ label: 'one', href: 'command:foo', title: 'nice' },
'\nand link ',
{ label: 'two', href: 'http://foo' },
'...'
]);
});
});

View File

@@ -0,0 +1,914 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator } from 'vs/base/common/map';
import * as assert from 'assert';
import { URI } from 'vs/base/common/uri';
import { extUriIgnorePathCase } from 'vs/base/common/resources';
suite('Map', () => {
test('LinkedMap - Simple', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('bk', 'bv');
assert.deepStrictEqual([...map.keys()], ['ak', 'bk']);
assert.deepStrictEqual([...map.values()], ['av', 'bv']);
assert.equal(map.first, 'av');
assert.equal(map.last, 'bv');
});
test('LinkedMap - Touch Old one', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('ak', 'av', Touch.AsOld);
assert.deepStrictEqual([...map.keys()], ['ak']);
assert.deepStrictEqual([...map.values()], ['av']);
});
test('LinkedMap - Touch New one', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('ak', 'av', Touch.AsNew);
assert.deepStrictEqual([...map.keys()], ['ak']);
assert.deepStrictEqual([...map.values()], ['av']);
});
test('LinkedMap - Touch Old two', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('bk', 'bv');
map.set('bk', 'bv', Touch.AsOld);
assert.deepStrictEqual([...map.keys()], ['bk', 'ak']);
assert.deepStrictEqual([...map.values()], ['bv', 'av']);
});
test('LinkedMap - Touch New two', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('bk', 'bv');
map.set('ak', 'av', Touch.AsNew);
assert.deepStrictEqual([...map.keys()], ['bk', 'ak']);
assert.deepStrictEqual([...map.values()], ['bv', 'av']);
});
test('LinkedMap - Touch Old from middle', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('bk', 'bv');
map.set('ck', 'cv');
map.set('bk', 'bv', Touch.AsOld);
assert.deepStrictEqual([...map.keys()], ['bk', 'ak', 'ck']);
assert.deepStrictEqual([...map.values()], ['bv', 'av', 'cv']);
});
test('LinkedMap - Touch New from middle', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('bk', 'bv');
map.set('ck', 'cv');
map.set('bk', 'bv', Touch.AsNew);
assert.deepStrictEqual([...map.keys()], ['ak', 'ck', 'bk']);
assert.deepStrictEqual([...map.values()], ['av', 'cv', 'bv']);
});
test('LinkedMap - basics', function () {
const map = new LinkedMap<string, any>();
assert.equal(map.size, 0);
map.set('1', 1);
map.set('2', '2');
map.set('3', true);
const obj = Object.create(null);
map.set('4', obj);
const date = Date.now();
map.set('5', date);
assert.equal(map.size, 5);
assert.equal(map.get('1'), 1);
assert.equal(map.get('2'), '2');
assert.equal(map.get('3'), true);
assert.equal(map.get('4'), obj);
assert.equal(map.get('5'), date);
assert.ok(!map.get('6'));
map.delete('6');
assert.equal(map.size, 5);
assert.equal(map.delete('1'), true);
assert.equal(map.delete('2'), true);
assert.equal(map.delete('3'), true);
assert.equal(map.delete('4'), true);
assert.equal(map.delete('5'), true);
assert.equal(map.size, 0);
assert.ok(!map.get('5'));
assert.ok(!map.get('4'));
assert.ok(!map.get('3'));
assert.ok(!map.get('2'));
assert.ok(!map.get('1'));
map.set('1', 1);
map.set('2', '2');
map.set('3', true);
assert.ok(map.has('1'));
assert.equal(map.get('1'), 1);
assert.equal(map.get('2'), '2');
assert.equal(map.get('3'), true);
map.clear();
assert.equal(map.size, 0);
assert.ok(!map.get('1'));
assert.ok(!map.get('2'));
assert.ok(!map.get('3'));
assert.ok(!map.has('1'));
});
test('LinkedMap - Iterators', () => {
const map = new LinkedMap<number, any>();
map.set(1, 1);
map.set(2, 2);
map.set(3, 3);
for (const elem of map.keys()) {
assert.ok(elem);
}
for (const elem of map.values()) {
assert.ok(elem);
}
for (const elem of map.entries()) {
assert.ok(elem);
}
{
const keys = map.keys();
const values = map.values();
const entries = map.entries();
map.get(1);
keys.next();
values.next();
entries.next();
}
{
const keys = map.keys();
const values = map.values();
const entries = map.entries();
map.get(1, Touch.AsNew);
let exceptions: number = 0;
try {
keys.next();
} catch (err) {
exceptions++;
}
try {
values.next();
} catch (err) {
exceptions++;
}
try {
entries.next();
} catch (err) {
exceptions++;
}
assert.strictEqual(exceptions, 3);
}
});
test('LinkedMap - LRU Cache simple', () => {
const cache = new LRUCache<number, number>(5);
[1, 2, 3, 4, 5].forEach(value => cache.set(value, value));
assert.strictEqual(cache.size, 5);
cache.set(6, 6);
assert.strictEqual(cache.size, 5);
assert.deepStrictEqual([...cache.keys()], [2, 3, 4, 5, 6]);
cache.set(7, 7);
assert.strictEqual(cache.size, 5);
assert.deepStrictEqual([...cache.keys()], [3, 4, 5, 6, 7]);
let values: number[] = [];
[3, 4, 5, 6, 7].forEach(key => values.push(cache.get(key)!));
assert.deepStrictEqual(values, [3, 4, 5, 6, 7]);
});
test('LinkedMap - LRU Cache get', () => {
const cache = new LRUCache<number, number>(5);
[1, 2, 3, 4, 5].forEach(value => cache.set(value, value));
assert.strictEqual(cache.size, 5);
assert.deepStrictEqual([...cache.keys()], [1, 2, 3, 4, 5]);
cache.get(3);
assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]);
cache.peek(4);
assert.deepStrictEqual([...cache.keys()], [1, 2, 4, 5, 3]);
let values: number[] = [];
[1, 2, 3, 4, 5].forEach(key => values.push(cache.get(key)!));
assert.deepStrictEqual(values, [1, 2, 3, 4, 5]);
});
test('LinkedMap - LRU Cache limit', () => {
const cache = new LRUCache<number, number>(10);
for (let i = 1; i <= 10; i++) {
cache.set(i, i);
}
assert.strictEqual(cache.size, 10);
cache.limit = 5;
assert.strictEqual(cache.size, 5);
assert.deepStrictEqual([...cache.keys()], [6, 7, 8, 9, 10]);
cache.limit = 20;
assert.strictEqual(cache.size, 5);
for (let i = 11; i <= 20; i++) {
cache.set(i, i);
}
assert.deepEqual(cache.size, 15);
let values: number[] = [];
for (let i = 6; i <= 20; i++) {
values.push(cache.get(i)!);
assert.strictEqual(cache.get(i), i);
}
assert.deepStrictEqual([...cache.values()], values);
});
test('LinkedMap - LRU Cache limit with ratio', () => {
const cache = new LRUCache<number, number>(10, 0.5);
for (let i = 1; i <= 10; i++) {
cache.set(i, i);
}
assert.strictEqual(cache.size, 10);
cache.set(11, 11);
assert.strictEqual(cache.size, 5);
assert.deepStrictEqual([...cache.keys()], [7, 8, 9, 10, 11]);
let values: number[] = [];
[...cache.keys()].forEach(key => values.push(cache.get(key)!));
assert.deepStrictEqual(values, [7, 8, 9, 10, 11]);
assert.deepStrictEqual([...cache.values()], values);
});
test('LinkedMap - toJSON / fromJSON', () => {
let map = new LinkedMap<string, string>();
map.set('ak', 'av');
map.set('bk', 'bv');
map.set('ck', 'cv');
const json = map.toJSON();
map = new LinkedMap<string, string>();
map.fromJSON(json);
let i = 0;
map.forEach((value, key) => {
if (i === 0) {
assert.equal(key, 'ak');
assert.equal(value, 'av');
} else if (i === 1) {
assert.equal(key, 'bk');
assert.equal(value, 'bv');
} else if (i === 2) {
assert.equal(key, 'ck');
assert.equal(value, 'cv');
}
i++;
});
});
test('LinkedMap - delete Head and Tail', function () {
const map = new LinkedMap<string, number>();
assert.equal(map.size, 0);
map.set('1', 1);
assert.equal(map.size, 1);
map.delete('1');
assert.equal(map.get('1'), undefined);
assert.equal(map.size, 0);
assert.equal([...map.keys()].length, 0);
});
test('LinkedMap - delete Head', function () {
const map = new LinkedMap<string, number>();
assert.equal(map.size, 0);
map.set('1', 1);
map.set('2', 2);
assert.equal(map.size, 2);
map.delete('1');
assert.equal(map.get('2'), 2);
assert.equal(map.size, 1);
assert.equal([...map.keys()].length, 1);
assert.equal([...map.keys()][0], 2);
});
test('LinkedMap - delete Tail', function () {
const map = new LinkedMap<string, number>();
assert.equal(map.size, 0);
map.set('1', 1);
map.set('2', 2);
assert.equal(map.size, 2);
map.delete('2');
assert.equal(map.get('1'), 1);
assert.equal(map.size, 1);
assert.equal([...map.keys()].length, 1);
assert.equal([...map.keys()][0], 1);
});
test('PathIterator', () => {
const iter = new PathIterator();
iter.reset('file:///usr/bin/file.txt');
assert.equal(iter.value(), 'file:');
assert.equal(iter.hasNext(), true);
assert.equal(iter.cmp('file:'), 0);
assert.ok(iter.cmp('a') < 0);
assert.ok(iter.cmp('aile:') < 0);
assert.ok(iter.cmp('z') > 0);
assert.ok(iter.cmp('zile:') > 0);
iter.next();
assert.equal(iter.value(), 'usr');
assert.equal(iter.hasNext(), true);
iter.next();
assert.equal(iter.value(), 'bin');
assert.equal(iter.hasNext(), true);
iter.next();
assert.equal(iter.value(), 'file.txt');
assert.equal(iter.hasNext(), false);
iter.next();
assert.equal(iter.value(), '');
assert.equal(iter.hasNext(), false);
iter.next();
assert.equal(iter.value(), '');
assert.equal(iter.hasNext(), false);
//
iter.reset('/foo/bar/');
assert.equal(iter.value(), 'foo');
assert.equal(iter.hasNext(), true);
iter.next();
assert.equal(iter.value(), 'bar');
assert.equal(iter.hasNext(), false);
});
test('URIIterator', function () {
const iter = new UriIterator(false);
iter.reset(URI.parse('file:///usr/bin/file.txt'));
assert.equal(iter.value(), 'file');
// assert.equal(iter.cmp('FILE'), 0);
assert.equal(iter.cmp('file'), 0);
assert.equal(iter.hasNext(), true);
iter.next();
assert.equal(iter.value(), 'usr');
assert.equal(iter.hasNext(), true);
iter.next();
assert.equal(iter.value(), 'bin');
assert.equal(iter.hasNext(), true);
iter.next();
assert.equal(iter.value(), 'file.txt');
assert.equal(iter.hasNext(), false);
iter.reset(URI.parse('file://share/usr/bin/file.txt?foo'));
// scheme
assert.equal(iter.value(), 'file');
// assert.equal(iter.cmp('FILE'), 0);
assert.equal(iter.cmp('file'), 0);
assert.equal(iter.hasNext(), true);
iter.next();
// authority
assert.equal(iter.value(), 'share');
assert.equal(iter.cmp('SHARe'), 0);
assert.equal(iter.hasNext(), true);
iter.next();
// path
assert.equal(iter.value(), 'usr');
assert.equal(iter.hasNext(), true);
iter.next();
// path
assert.equal(iter.value(), 'bin');
assert.equal(iter.hasNext(), true);
iter.next();
// path
assert.equal(iter.value(), 'file.txt');
assert.equal(iter.hasNext(), true);
iter.next();
// query
assert.equal(iter.value(), 'foo');
assert.equal(iter.cmp('z') > 0, true);
assert.equal(iter.cmp('a') < 0, true);
assert.equal(iter.hasNext(), false);
});
function assertTernarySearchTree<E>(trie: TernarySearchTree<string, E>, ...elements: [string, E][]) {
const map = new Map<string, E>();
for (const [key, value] of elements) {
map.set(key, value);
}
map.forEach((value, key) => {
assert.equal(trie.get(key), value);
});
// forEach
let forEachCount = 0;
trie.forEach((element, key) => {
assert.equal(element, map.get(key));
forEachCount++;
});
assert.equal(map.size, forEachCount);
// iterator
let iterCount = 0;
for (let [key, value] of trie) {
assert.equal(value, map.get(key));
iterCount++;
}
assert.equal(map.size, iterCount);
}
test('TernarySearchTree - set', function () {
let trie = TernarySearchTree.forStrings<number>();
trie.set('foobar', 1);
trie.set('foobaz', 2);
assertTernarySearchTree(trie, ['foobar', 1], ['foobaz', 2]); // longer
trie = TernarySearchTree.forStrings<number>();
trie.set('foobar', 1);
trie.set('fooba', 2);
assertTernarySearchTree(trie, ['foobar', 1], ['fooba', 2]); // shorter
trie = TernarySearchTree.forStrings<number>();
trie.set('foo', 1);
trie.set('foo', 2);
assertTernarySearchTree(trie, ['foo', 2]);
trie = TernarySearchTree.forStrings<number>();
trie.set('foo', 1);
trie.set('foobar', 2);
trie.set('bar', 3);
trie.set('foob', 4);
trie.set('bazz', 5);
assertTernarySearchTree(trie,
['foo', 1],
['foobar', 2],
['bar', 3],
['foob', 4],
['bazz', 5]
);
});
test('TernarySearchTree - findLongestMatch', function () {
let trie = TernarySearchTree.forStrings<number>();
trie.set('foo', 1);
trie.set('foobar', 2);
trie.set('foobaz', 3);
assert.equal(trie.findSubstr('f'), undefined);
assert.equal(trie.findSubstr('z'), undefined);
assert.equal(trie.findSubstr('foo'), 1);
assert.equal(trie.findSubstr('fooö'), 1);
assert.equal(trie.findSubstr('fooba'), 1);
assert.equal(trie.findSubstr('foobarr'), 2);
assert.equal(trie.findSubstr('foobazrr'), 3);
});
test('TernarySearchTree - basics', function () {
let trie = new TernarySearchTree<string, number>(new StringIterator());
trie.set('foo', 1);
trie.set('bar', 2);
trie.set('foobar', 3);
assert.equal(trie.get('foo'), 1);
assert.equal(trie.get('bar'), 2);
assert.equal(trie.get('foobar'), 3);
assert.equal(trie.get('foobaz'), undefined);
assert.equal(trie.get('foobarr'), undefined);
assert.equal(trie.findSubstr('fo'), undefined);
assert.equal(trie.findSubstr('foo'), 1);
assert.equal(trie.findSubstr('foooo'), 1);
trie.delete('foobar');
trie.delete('bar');
assert.equal(trie.get('foobar'), undefined);
assert.equal(trie.get('bar'), undefined);
trie.set('foobar', 17);
trie.set('barr', 18);
assert.equal(trie.get('foobar'), 17);
assert.equal(trie.get('barr'), 18);
assert.equal(trie.get('bar'), undefined);
});
test('TernarySearchTree - delete & cleanup', function () {
// normal delete
let trie = new TernarySearchTree<string, number>(new StringIterator());
trie.set('foo', 1);
trie.set('foobar', 2);
trie.set('bar', 3);
assertTernarySearchTree(trie, ['foo', 1], ['foobar', 2], ['bar', 3]);
trie.delete('foo');
assertTernarySearchTree(trie, ['foobar', 2], ['bar', 3]);
trie.delete('foobar');
assertTernarySearchTree(trie, ['bar', 3]);
// superstr-delete
trie = new TernarySearchTree<string, number>(new StringIterator());
trie.set('foo', 1);
trie.set('foobar', 2);
trie.set('bar', 3);
trie.deleteSuperstr('foo');
assertTernarySearchTree(trie, ['bar', 3]);
trie = new TernarySearchTree<string, number>(new StringIterator());
trie.set('foo', 1);
trie.set('foobar', 2);
trie.set('bar', 3);
trie.deleteSuperstr('fo');
assertTernarySearchTree(trie, ['bar', 3]);
// trie = new TernarySearchTree<string, number>(new StringIterator());
// trie.set('foo', 1);
// trie.set('foobar', 2);
// trie.set('bar', 3);
// trie.deleteSuperStr('f');
// assertTernarySearchTree(trie, ['bar', 3]);
});
test('TernarySearchTree (PathSegments) - basics', function () {
let trie = new TernarySearchTree<string, number>(new PathIterator());
trie.set('/user/foo/bar', 1);
trie.set('/user/foo', 2);
trie.set('/user/foo/flip/flop', 3);
assert.equal(trie.get('/user/foo/bar'), 1);
assert.equal(trie.get('/user/foo'), 2);
assert.equal(trie.get('/user//foo'), 2);
assert.equal(trie.get('/user\\foo'), 2);
assert.equal(trie.get('/user/foo/flip/flop'), 3);
assert.equal(trie.findSubstr('/user/bar'), undefined);
assert.equal(trie.findSubstr('/user/foo'), 2);
assert.equal(trie.findSubstr('\\user\\foo'), 2);
assert.equal(trie.findSubstr('/user//foo'), 2);
assert.equal(trie.findSubstr('/user/foo/ba'), 2);
assert.equal(trie.findSubstr('/user/foo/far/boo'), 2);
assert.equal(trie.findSubstr('/user/foo/bar'), 1);
assert.equal(trie.findSubstr('/user/foo/bar/far/boo'), 1);
});
test('TernarySearchTree (PathSegments) - lookup', function () {
const map = new TernarySearchTree<string, number>(new PathIterator());
map.set('/user/foo/bar', 1);
map.set('/user/foo', 2);
map.set('/user/foo/flip/flop', 3);
assert.equal(map.get('/foo'), undefined);
assert.equal(map.get('/user'), undefined);
assert.equal(map.get('/user/foo'), 2);
assert.equal(map.get('/user/foo/bar'), 1);
assert.equal(map.get('/user/foo/bar/boo'), undefined);
});
test('TernarySearchTree (PathSegments) - superstr', function () {
const map = new TernarySearchTree<string, number>(new PathIterator());
map.set('/user/foo/bar', 1);
map.set('/user/foo', 2);
map.set('/user/foo/flip/flop', 3);
map.set('/usr/foo', 4);
let item: IteratorResult<number>;
let iter = map.findSuperstr('/user');
item = iter!.next();
assert.equal(item.value, 2);
assert.equal(item.done, false);
item = iter!.next();
assert.equal(item.value, 1);
assert.equal(item.done, false);
item = iter!.next();
assert.equal(item.value, 3);
assert.equal(item.done, false);
item = iter!.next();
assert.equal(item.value, undefined);
assert.equal(item.done, true);
iter = map.findSuperstr('/usr');
item = iter!.next();
assert.equal(item.value, 4);
assert.equal(item.done, false);
item = iter!.next();
assert.equal(item.value, undefined);
assert.equal(item.done, true);
assert.equal(map.findSuperstr('/not'), undefined);
assert.equal(map.findSuperstr('/us'), undefined);
assert.equal(map.findSuperstr('/usrr'), undefined);
assert.equal(map.findSuperstr('/userr'), undefined);
});
test('TernarySearchTree (PathSegments) - delete_superstr', function () {
const map = new TernarySearchTree<string, number>(new PathIterator());
map.set('/user/foo/bar', 1);
map.set('/user/foo', 2);
map.set('/user/foo/flip/flop', 3);
map.set('/usr/foo', 4);
assertTernarySearchTree(map,
['/user/foo/bar', 1],
['/user/foo', 2],
['/user/foo/flip/flop', 3],
['/usr/foo', 4],
);
// not a segment
map.deleteSuperstr('/user/fo');
assertTernarySearchTree(map,
['/user/foo/bar', 1],
['/user/foo', 2],
['/user/foo/flip/flop', 3],
['/usr/foo', 4],
);
// delete a segment
map.set('/user/foo/bar', 1);
map.set('/user/foo', 2);
map.set('/user/foo/flip/flop', 3);
map.set('/usr/foo', 4);
map.deleteSuperstr('/user/foo');
assertTernarySearchTree(map,
['/usr/foo', 4],
);
});
test('TernarySearchTree (URI) - basics', function () {
let trie = new TernarySearchTree<URI, number>(new UriIterator(false));
trie.set(URI.file('/user/foo/bar'), 1);
trie.set(URI.file('/user/foo'), 2);
trie.set(URI.file('/user/foo/flip/flop'), 3);
assert.equal(trie.get(URI.file('/user/foo/bar')), 1);
assert.equal(trie.get(URI.file('/user/foo')), 2);
assert.equal(trie.get(URI.file('/user/foo/flip/flop')), 3);
assert.equal(trie.findSubstr(URI.file('/user/bar')), undefined);
assert.equal(trie.findSubstr(URI.file('/user/foo')), 2);
assert.equal(trie.findSubstr(URI.file('/user/foo/ba')), 2);
assert.equal(trie.findSubstr(URI.file('/user/foo/far/boo')), 2);
assert.equal(trie.findSubstr(URI.file('/user/foo/bar')), 1);
assert.equal(trie.findSubstr(URI.file('/user/foo/bar/far/boo')), 1);
});
test('TernarySearchTree (URI) - lookup', function () {
const map = new TernarySearchTree<URI, number>(new UriIterator(false));
map.set(URI.parse('http://foo.bar/user/foo/bar'), 1);
map.set(URI.parse('http://foo.bar/user/foo?query'), 2);
map.set(URI.parse('http://foo.bar/user/foo?QUERY'), 3);
map.set(URI.parse('http://foo.bar/user/foo/flip/flop'), 3);
assert.equal(map.get(URI.parse('http://foo.bar/foo')), undefined);
assert.equal(map.get(URI.parse('http://foo.bar/user')), undefined);
assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar')), 1);
assert.equal(map.get(URI.parse('http://foo.bar/user/foo?query')), 2);
assert.equal(map.get(URI.parse('http://foo.bar/user/foo?Query')), undefined);
assert.equal(map.get(URI.parse('http://foo.bar/user/foo?QUERY')), 3);
assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar/boo')), undefined);
});
test('TernarySearchTree (PathSegments) - superstr', function () {
const map = new TernarySearchTree<URI, number>(new UriIterator(false));
map.set(URI.file('/user/foo/bar'), 1);
map.set(URI.file('/user/foo'), 2);
map.set(URI.file('/user/foo/flip/flop'), 3);
map.set(URI.file('/usr/foo'), 4);
let item: IteratorResult<number>;
let iter = map.findSuperstr(URI.file('/user'))!;
item = iter.next();
assert.equal(item.value, 2);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, 1);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, 3);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, undefined);
assert.equal(item.done, true);
iter = map.findSuperstr(URI.file('/usr'))!;
item = iter.next();
assert.equal(item.value, 4);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, undefined);
assert.equal(item.done, true);
iter = map.findSuperstr(URI.file('/'))!;
item = iter.next();
assert.equal(item.value, 2);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, 1);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, 3);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, 4);
assert.equal(item.done, false);
item = iter.next();
assert.equal(item.value, undefined);
assert.equal(item.done, true);
assert.equal(map.findSuperstr(URI.file('/not')), undefined);
assert.equal(map.findSuperstr(URI.file('/us')), undefined);
assert.equal(map.findSuperstr(URI.file('/usrr')), undefined);
assert.equal(map.findSuperstr(URI.file('/userr')), undefined);
});
test('ResourceMap - basics', function () {
const map = new ResourceMap<any>();
const resource1 = URI.parse('some://1');
const resource2 = URI.parse('some://2');
const resource3 = URI.parse('some://3');
const resource4 = URI.parse('some://4');
const resource5 = URI.parse('some://5');
const resource6 = URI.parse('some://6');
assert.equal(map.size, 0);
let res = map.set(resource1, 1);
assert.ok(res === map);
map.set(resource2, '2');
map.set(resource3, true);
const values = [...map.values()];
assert.equal(values[0], 1);
assert.equal(values[1], '2');
assert.equal(values[2], true);
let counter = 0;
map.forEach((value, key, mapObj) => {
assert.equal(value, values[counter++]);
assert.ok(URI.isUri(key));
assert.ok(map === mapObj);
});
const obj = Object.create(null);
map.set(resource4, obj);
const date = Date.now();
map.set(resource5, date);
assert.equal(map.size, 5);
assert.equal(map.get(resource1), 1);
assert.equal(map.get(resource2), '2');
assert.equal(map.get(resource3), true);
assert.equal(map.get(resource4), obj);
assert.equal(map.get(resource5), date);
assert.ok(!map.get(resource6));
map.delete(resource6);
assert.equal(map.size, 5);
assert.ok(map.delete(resource1));
assert.ok(map.delete(resource2));
assert.ok(map.delete(resource3));
assert.ok(map.delete(resource4));
assert.ok(map.delete(resource5));
assert.equal(map.size, 0);
assert.ok(!map.get(resource5));
assert.ok(!map.get(resource4));
assert.ok(!map.get(resource3));
assert.ok(!map.get(resource2));
assert.ok(!map.get(resource1));
map.set(resource1, 1);
map.set(resource2, '2');
map.set(resource3, true);
assert.ok(map.has(resource1));
assert.equal(map.get(resource1), 1);
assert.equal(map.get(resource2), '2');
assert.equal(map.get(resource3), true);
map.clear();
assert.equal(map.size, 0);
assert.ok(!map.get(resource1));
assert.ok(!map.get(resource2));
assert.ok(!map.get(resource3));
assert.ok(!map.has(resource1));
map.set(resource1, false);
map.set(resource2, 0);
assert.ok(map.has(resource1));
assert.ok(map.has(resource2));
});
test('ResourceMap - files (do NOT ignorecase)', function () {
const map = new ResourceMap<any>();
const fileA = URI.parse('file://some/filea');
const fileB = URI.parse('some://some/other/fileb');
const fileAUpper = URI.parse('file://SOME/FILEA');
map.set(fileA, 'true');
assert.equal(map.get(fileA), 'true');
assert.ok(!map.get(fileAUpper));
assert.ok(!map.get(fileB));
map.set(fileAUpper, 'false');
assert.equal(map.get(fileAUpper), 'false');
assert.equal(map.get(fileA), 'true');
const windowsFile = URI.file('c:\\test with %25\\c#code');
const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json');
map.set(windowsFile, 'true');
map.set(uncFile, 'true');
assert.equal(map.get(windowsFile), 'true');
assert.equal(map.get(uncFile), 'true');
});
test('ResourceMap - files (ignorecase)', function () {
const map = new ResourceMap<any>(uri => extUriIgnorePathCase.getComparisonKey(uri));
const fileA = URI.parse('file://some/filea');
const fileB = URI.parse('some://some/other/fileb');
const fileAUpper = URI.parse('file://SOME/FILEA');
map.set(fileA, 'true');
assert.equal(map.get(fileA), 'true');
assert.equal(map.get(fileAUpper), 'true');
assert.ok(!map.get(fileB));
map.set(fileAUpper, 'false');
assert.equal(map.get(fileAUpper), 'false');
assert.equal(map.get(fileA), 'false');
const windowsFile = URI.file('c:\\test with %25\\c#code');
const uncFile = URI.file('\\\\shäres\\path\\c#\\plugin.json');
map.set(windowsFile, 'true');
map.set(uncFile, 'true');
assert.equal(map.get(windowsFile), 'true');
assert.equal(map.get(uncFile), 'true');
});
});

View File

@@ -0,0 +1,72 @@
/*---------------------------------------------------------------------------------------------
* 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 { MarkdownString } from 'vs/base/common/htmlContent';
suite('MarkdownString', () => {
test('appendText', () => {
const mds = new MarkdownString();
mds.appendText('# foo\n*bar*');
assert.equal(mds.value, '\\# foo\n\n\\*bar\\*');
});
suite('ThemeIcons', () => {
suite('Support On', () => {
test('appendText', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: true });
mds.appendText('$(zap) $(not a theme icon) $(add)');
assert.equal(mds.value, '\\\\$\\(zap\\) $\\(not a theme icon\\) \\\\$\\(add\\)');
});
test('appendMarkdown', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: true });
mds.appendMarkdown('$(zap) $(not a theme icon) $(add)');
assert.equal(mds.value, '$(zap) $(not a theme icon) $(add)');
});
test('appendMarkdown with escaped icon', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: true });
mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)');
assert.equal(mds.value, '\\$(zap) $(not a theme icon) $(add)');
});
});
suite('Support Off', () => {
test('appendText', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: false });
mds.appendText('$(zap) $(not a theme icon) $(add)');
assert.equal(mds.value, '$\\(zap\\) $\\(not a theme icon\\) $\\(add\\)');
});
test('appendMarkdown', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: false });
mds.appendMarkdown('$(zap) $(not a theme icon) $(add)');
assert.equal(mds.value, '$(zap) $(not a theme icon) $(add)');
});
test('appendMarkdown with escaped icon', () => {
const mds = new MarkdownString(undefined, { supportThemeIcons: true });
mds.appendMarkdown('\\$(zap) $(not a theme icon) $(add)');
assert.equal(mds.value, '\\$(zap) $(not a theme icon) $(add)');
});
});
});
});

View File

@@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* 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 { parse, stringify } from 'vs/base/common/marshalling';
suite('Marshalling', () => {
test('RegExp', () => {
let value = /foo/img;
let raw = stringify(value);
let clone = <RegExp>parse(raw);
assert.equal(value.source, clone.source);
assert.equal(value.global, clone.global);
assert.equal(value.ignoreCase, clone.ignoreCase);
assert.equal(value.multiline, clone.multiline);
});
test('URI', () => {
const value = URI.from({ scheme: 'file', authority: 'server', path: '/shares/c#files', query: 'q', fragment: 'f' });
const raw = stringify(value);
const clone = <URI>parse(raw);
assert.equal(value.scheme, clone.scheme);
assert.equal(value.authority, clone.authority);
assert.equal(value.path, clone.path);
assert.equal(value.query, clone.query);
assert.equal(value.fragment, clone.fragment);
});
test('Bug 16793:# in folder name => mirror models get out of sync', () => {
const uri1 = URI.file('C:\\C#\\file.txt');
assert.equal(parse(stringify(uri1)).toString(), uri1.toString());
});
});

View File

@@ -0,0 +1,129 @@
/*---------------------------------------------------------------------------------------------
* 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 { guessMimeTypes, registerTextMime } from 'vs/base/common/mime';
import { URI } from 'vs/base/common/uri';
suite('Mime', () => {
test('Dynamically Register Text Mime', () => {
let guess = guessMimeTypes(URI.file('foo.monaco'));
assert.deepEqual(guess, ['application/unknown']);
registerTextMime({ id: 'monaco', extension: '.monaco', mime: 'text/monaco' });
guess = guessMimeTypes(URI.file('foo.monaco'));
assert.deepEqual(guess, ['text/monaco', 'text/plain']);
guess = guessMimeTypes(URI.file('.monaco'));
assert.deepEqual(guess, ['text/monaco', 'text/plain']);
registerTextMime({ id: 'codefile', filename: 'Codefile', mime: 'text/code' });
guess = guessMimeTypes(URI.file('Codefile'));
assert.deepEqual(guess, ['text/code', 'text/plain']);
guess = guessMimeTypes(URI.file('foo.Codefile'));
assert.deepEqual(guess, ['application/unknown']);
registerTextMime({ id: 'docker', filepattern: 'Docker*', mime: 'text/docker' });
guess = guessMimeTypes(URI.file('Docker-debug'));
assert.deepEqual(guess, ['text/docker', 'text/plain']);
guess = guessMimeTypes(URI.file('docker-PROD'));
assert.deepEqual(guess, ['text/docker', 'text/plain']);
registerTextMime({ id: 'niceregex', mime: 'text/nice-regex', firstline: /RegexesAreNice/ });
guess = guessMimeTypes(URI.file('Randomfile.noregistration'), 'RegexesAreNice');
assert.deepEqual(guess, ['text/nice-regex', 'text/plain']);
guess = guessMimeTypes(URI.file('Randomfile.noregistration'), 'RegexesAreNotNice');
assert.deepEqual(guess, ['application/unknown']);
guess = guessMimeTypes(URI.file('Codefile'), 'RegexesAreNice');
assert.deepEqual(guess, ['text/code', 'text/plain']);
});
test('Mimes Priority', () => {
registerTextMime({ id: 'monaco', extension: '.monaco', mime: 'text/monaco' });
registerTextMime({ id: 'foobar', mime: 'text/foobar', firstline: /foobar/ });
let guess = guessMimeTypes(URI.file('foo.monaco'));
assert.deepEqual(guess, ['text/monaco', 'text/plain']);
guess = guessMimeTypes(URI.file('foo.monaco'), 'foobar');
assert.deepEqual(guess, ['text/monaco', 'text/plain']);
registerTextMime({ id: 'docker', filename: 'dockerfile', mime: 'text/winner' });
registerTextMime({ id: 'docker', filepattern: 'dockerfile*', mime: 'text/looser' });
guess = guessMimeTypes(URI.file('dockerfile'));
assert.deepEqual(guess, ['text/winner', 'text/plain']);
registerTextMime({ id: 'azure-looser', mime: 'text/azure-looser', firstline: /azure/ });
registerTextMime({ id: 'azure-winner', mime: 'text/azure-winner', firstline: /azure/ });
guess = guessMimeTypes(URI.file('azure'), 'azure');
assert.deepEqual(guess, ['text/azure-winner', 'text/plain']);
});
test('Specificity priority 1', () => {
registerTextMime({ id: 'monaco2', extension: '.monaco2', mime: 'text/monaco2' });
registerTextMime({ id: 'monaco2', filename: 'specific.monaco2', mime: 'text/specific-monaco2' });
assert.deepEqual(guessMimeTypes(URI.file('specific.monaco2')), ['text/specific-monaco2', 'text/plain']);
assert.deepEqual(guessMimeTypes(URI.file('foo.monaco2')), ['text/monaco2', 'text/plain']);
});
test('Specificity priority 2', () => {
registerTextMime({ id: 'monaco3', filename: 'specific.monaco3', mime: 'text/specific-monaco3' });
registerTextMime({ id: 'monaco3', extension: '.monaco3', mime: 'text/monaco3' });
assert.deepEqual(guessMimeTypes(URI.file('specific.monaco3')), ['text/specific-monaco3', 'text/plain']);
assert.deepEqual(guessMimeTypes(URI.file('foo.monaco3')), ['text/monaco3', 'text/plain']);
});
test('Mimes Priority - Longest Extension wins', () => {
registerTextMime({ id: 'monaco', extension: '.monaco', mime: 'text/monaco' });
registerTextMime({ id: 'monaco', extension: '.monaco.xml', mime: 'text/monaco-xml' });
registerTextMime({ id: 'monaco', extension: '.monaco.xml.build', mime: 'text/monaco-xml-build' });
let guess = guessMimeTypes(URI.file('foo.monaco'));
assert.deepEqual(guess, ['text/monaco', 'text/plain']);
guess = guessMimeTypes(URI.file('foo.monaco.xml'));
assert.deepEqual(guess, ['text/monaco-xml', 'text/plain']);
guess = guessMimeTypes(URI.file('foo.monaco.xml.build'));
assert.deepEqual(guess, ['text/monaco-xml-build', 'text/plain']);
});
test('Mimes Priority - User configured wins', () => {
registerTextMime({ id: 'monaco', extension: '.monaco.xnl', mime: 'text/monaco', userConfigured: true });
registerTextMime({ id: 'monaco', extension: '.monaco.xml', mime: 'text/monaco-xml' });
let guess = guessMimeTypes(URI.file('foo.monaco.xnl'));
assert.deepEqual(guess, ['text/monaco', 'text/plain']);
});
test('Mimes Priority - Pattern matches on path if specified', () => {
registerTextMime({ id: 'monaco', filepattern: '**/dot.monaco.xml', mime: 'text/monaco' });
registerTextMime({ id: 'other', filepattern: '*ot.other.xml', mime: 'text/other' });
let guess = guessMimeTypes(URI.file('/some/path/dot.monaco.xml'));
assert.deepEqual(guess, ['text/monaco', 'text/plain']);
});
test('Mimes Priority - Last registered mime wins', () => {
registerTextMime({ id: 'monaco', filepattern: '**/dot.monaco.xml', mime: 'text/monaco' });
registerTextMime({ id: 'other', filepattern: '**/dot.monaco.xml', mime: 'text/other' });
let guess = guessMimeTypes(URI.file('/some/path/dot.monaco.xml'));
assert.deepEqual(guess, ['text/other', 'text/plain']);
});
test('Data URIs', () => {
registerTextMime({ id: 'data', extension: '.data', mime: 'text/data' });
assert.deepEqual(guessMimeTypes(URI.parse(`data:;label:something.data;description:data,`)), ['text/data', 'text/plain']);
});
});

View File

@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface Ctor<T> {
new(): T;
}
export function mock<T>(): Ctor<T> {
return function () { } as any;
}

View File

@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* 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 { removeAccents } from 'vs/base/common/normalization';
suite('Normalization', () => {
test('removeAccents', function () {
assert.equal(removeAccents('joào'), 'joao');
assert.equal(removeAccents('joáo'), 'joao');
assert.equal(removeAccents('joâo'), 'joao');
assert.equal(removeAccents('joäo'), 'joao');
// assert.equal(strings.removeAccents('joæo'), 'joao'); // not an accent
assert.equal(removeAccents('joão'), 'joao');
assert.equal(removeAccents('joåo'), 'joao');
assert.equal(removeAccents('joåo'), 'joao');
assert.equal(removeAccents('joāo'), 'joao');
assert.equal(removeAccents('fôo'), 'foo');
assert.equal(removeAccents('föo'), 'foo');
assert.equal(removeAccents('fòo'), 'foo');
assert.equal(removeAccents('fóo'), 'foo');
// assert.equal(strings.removeAccents('fœo'), 'foo');
// assert.equal(strings.removeAccents('føo'), 'foo');
assert.equal(removeAccents('fōo'), 'foo');
assert.equal(removeAccents('fõo'), 'foo');
assert.equal(removeAccents('andrè'), 'andre');
assert.equal(removeAccents('andré'), 'andre');
assert.equal(removeAccents('andrê'), 'andre');
assert.equal(removeAccents('andrë'), 'andre');
assert.equal(removeAccents('andrē'), 'andre');
assert.equal(removeAccents('andrė'), 'andre');
assert.equal(removeAccents('andrę'), 'andre');
assert.equal(removeAccents('hvîc'), 'hvic');
assert.equal(removeAccents('hvïc'), 'hvic');
assert.equal(removeAccents('hvíc'), 'hvic');
assert.equal(removeAccents('hvīc'), 'hvic');
assert.equal(removeAccents('hvįc'), 'hvic');
assert.equal(removeAccents('hvìc'), 'hvic');
assert.equal(removeAccents('ûdo'), 'udo');
assert.equal(removeAccents('üdo'), 'udo');
assert.equal(removeAccents('ùdo'), 'udo');
assert.equal(removeAccents('údo'), 'udo');
assert.equal(removeAccents('ūdo'), 'udo');
assert.equal(removeAccents('heÿ'), 'hey');
// assert.equal(strings.removeAccents('gruß'), 'grus');
assert.equal(removeAccents('gruś'), 'grus');
assert.equal(removeAccents('gruš'), 'grus');
assert.equal(removeAccents('çool'), 'cool');
assert.equal(removeAccents('ćool'), 'cool');
assert.equal(removeAccents('čool'), 'cool');
assert.equal(removeAccents('ñice'), 'nice');
assert.equal(removeAccents('ńice'), 'nice');
});
});

View File

@@ -0,0 +1,228 @@
/*---------------------------------------------------------------------------------------------
* 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 * as objects from 'vs/base/common/objects';
let check = (one: any, other: any, msg: string) => {
assert(objects.equals(one, other), msg);
assert(objects.equals(other, one), '[reverse] ' + msg);
};
let checkNot = (one: any, other: any, msg: string) => {
assert(!objects.equals(one, other), msg);
assert(!objects.equals(other, one), '[reverse] ' + msg);
};
suite('Objects', () => {
test('equals', () => {
check(null, null, 'null');
check(undefined, undefined, 'undefined');
check(1234, 1234, 'numbers');
check('', '', 'empty strings');
check('1234', '1234', 'strings');
check([], [], 'empty arrays');
// check(['', 123], ['', 123], 'arrays');
check([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]], 'nested arrays');
check({}, {}, 'empty objects');
check({ a: 1, b: '123' }, { a: 1, b: '123' }, 'objects');
check({ a: 1, b: '123' }, { b: '123', a: 1 }, 'objects (key order)');
check({ a: { b: 1, c: 2 }, b: 3 }, { a: { b: 1, c: 2 }, b: 3 }, 'nested objects');
checkNot(null, undefined, 'null != undefined');
checkNot(null, '', 'null != empty string');
checkNot(null, [], 'null != empty array');
checkNot(null, {}, 'null != empty object');
checkNot(null, 0, 'null != zero');
checkNot(undefined, '', 'undefined != empty string');
checkNot(undefined, [], 'undefined != empty array');
checkNot(undefined, {}, 'undefined != empty object');
checkNot(undefined, 0, 'undefined != zero');
checkNot('', [], 'empty string != empty array');
checkNot('', {}, 'empty string != empty object');
checkNot('', 0, 'empty string != zero');
checkNot([], {}, 'empty array != empty object');
checkNot([], 0, 'empty array != zero');
checkNot(0, [], 'zero != empty array');
checkNot('1234', 1234, 'string !== number');
checkNot([[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6000]], 'arrays');
checkNot({ a: { b: 1, c: 2 }, b: 3 }, { b: 3, a: { b: 9, c: 2 } }, 'objects');
});
test('mixin - array', function () {
let foo: any = {};
objects.mixin(foo, { bar: [1, 2, 3] });
assert(foo.bar);
assert(Array.isArray(foo.bar));
assert.equal(foo.bar.length, 3);
assert.equal(foo.bar[0], 1);
assert.equal(foo.bar[1], 2);
assert.equal(foo.bar[2], 3);
});
test('mixin - no overwrite', function () {
let foo: any = {
bar: '123'
};
let bar: any = {
bar: '456'
};
objects.mixin(foo, bar, false);
assert.equal(foo.bar, '123');
});
test('cloneAndChange', () => {
let o1 = { something: 'hello' };
let o = {
o1: o1,
o2: o1
};
assert.deepEqual(objects.cloneAndChange(o, () => { }), o);
});
test('safeStringify', () => {
let obj1: any = {
friend: null
};
let obj2: any = {
friend: null
};
obj1.friend = obj2;
obj2.friend = obj1;
let arr: any = [1];
arr.push(arr);
let circular: any = {
a: 42,
b: null,
c: [
obj1, obj2
],
d: null
};
arr.push(circular);
circular.b = circular;
circular.d = arr;
let result = objects.safeStringify(circular);
assert.deepEqual(JSON.parse(result), {
a: 42,
b: '[Circular]',
c: [
{
friend: {
friend: '[Circular]'
}
},
'[Circular]'
],
d: [1, '[Circular]', '[Circular]']
});
});
test('distinct', () => {
let base = {
one: 'one',
two: 2,
three: {
3: true
},
four: false
};
let diff = objects.distinct(base, base);
assert.deepEqual(diff, {});
let obj = {};
diff = objects.distinct(base, obj);
assert.deepEqual(diff, {});
obj = {
one: 'one',
two: 2
};
diff = objects.distinct(base, obj);
assert.deepEqual(diff, {});
obj = {
three: {
3: true
},
four: false
};
diff = objects.distinct(base, obj);
assert.deepEqual(diff, {});
obj = {
one: 'two',
two: 2,
three: {
3: true
},
four: true
};
diff = objects.distinct(base, obj);
assert.deepEqual(diff, {
one: 'two',
four: true
});
obj = {
one: null,
two: 2,
three: {
3: true
},
four: undefined
};
diff = objects.distinct(base, obj);
assert.deepEqual(diff, {
one: null,
four: undefined
});
obj = {
one: 'two',
two: 3,
three: { 3: false },
four: true
};
diff = objects.distinct(base, obj);
assert.deepEqual(diff, obj);
});
test('getCaseInsensitive', () => {
const obj1 = {
lowercase: 123,
mIxEdCaSe: 456
};
assert.equal(obj1.lowercase, objects.getCaseInsensitive(obj1, 'lowercase'));
assert.equal(obj1.lowercase, objects.getCaseInsensitive(obj1, 'lOwErCaSe'));
assert.equal(obj1.mIxEdCaSe, objects.getCaseInsensitive(obj1, 'MIXEDCASE'));
assert.equal(obj1.mIxEdCaSe, objects.getCaseInsensitive(obj1, 'mixedcase'));
});
});

View File

@@ -0,0 +1,184 @@
/*---------------------------------------------------------------------------------------------
* 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 { IPager, PagedModel } from 'vs/base/common/paging';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { isPromiseCanceledError, canceled } from 'vs/base/common/errors';
function getPage(pageIndex: number, cancellationToken: CancellationToken): Promise<number[]> {
if (cancellationToken.isCancellationRequested) {
return Promise.reject(canceled());
}
return Promise.resolve([0, 1, 2, 3, 4].map(i => i + (pageIndex * 5)));
}
class TestPager implements IPager<number> {
readonly firstPage = [0, 1, 2, 3, 4];
readonly pageSize = 5;
readonly total = 100;
readonly getPage: (pageIndex: number, cancellationToken: CancellationToken) => Promise<number[]>;
constructor(getPageFn?: (pageIndex: number, cancellationToken: CancellationToken) => Promise<number[]>) {
this.getPage = getPageFn || getPage;
}
}
suite('PagedModel', () => {
test('isResolved', () => {
const pager = new TestPager();
const model = new PagedModel(pager);
assert(model.isResolved(0));
assert(model.isResolved(1));
assert(model.isResolved(2));
assert(model.isResolved(3));
assert(model.isResolved(4));
assert(!model.isResolved(5));
assert(!model.isResolved(6));
assert(!model.isResolved(7));
assert(!model.isResolved(8));
assert(!model.isResolved(9));
assert(!model.isResolved(10));
assert(!model.isResolved(99));
});
test('resolve single', async () => {
const pager = new TestPager();
const model = new PagedModel(pager);
assert(!model.isResolved(5));
await model.resolve(5, CancellationToken.None);
assert(model.isResolved(5));
});
test('resolve page', async () => {
const pager = new TestPager();
const model = new PagedModel(pager);
assert(!model.isResolved(5));
assert(!model.isResolved(6));
assert(!model.isResolved(7));
assert(!model.isResolved(8));
assert(!model.isResolved(9));
assert(!model.isResolved(10));
await model.resolve(5, CancellationToken.None);
assert(model.isResolved(5));
assert(model.isResolved(6));
assert(model.isResolved(7));
assert(model.isResolved(8));
assert(model.isResolved(9));
assert(!model.isResolved(10));
});
test('resolve page 2', async () => {
const pager = new TestPager();
const model = new PagedModel(pager);
assert(!model.isResolved(5));
assert(!model.isResolved(6));
assert(!model.isResolved(7));
assert(!model.isResolved(8));
assert(!model.isResolved(9));
assert(!model.isResolved(10));
await model.resolve(10, CancellationToken.None);
assert(!model.isResolved(5));
assert(!model.isResolved(6));
assert(!model.isResolved(7));
assert(!model.isResolved(8));
assert(!model.isResolved(9));
assert(model.isResolved(10));
});
test('preemptive cancellation works', async function () {
const pager = new TestPager(() => {
assert(false);
return Promise.resolve([]);
});
const model = new PagedModel(pager);
try {
await model.resolve(5, CancellationToken.Cancelled);
return assert(false);
}
catch (err) {
return assert(isPromiseCanceledError(err));
}
});
test('cancellation works', function () {
const pager = new TestPager((_, token) => new Promise((_, e) => {
token.onCancellationRequested(() => e(canceled()));
}));
const model = new PagedModel(pager);
const tokenSource = new CancellationTokenSource();
const promise = model.resolve(5, tokenSource.token).then(
() => assert(false),
err => assert(isPromiseCanceledError(err))
);
setTimeout(() => tokenSource.cancel(), 10);
return promise;
});
test('same page cancellation works', function () {
let state = 'idle';
const pager = new TestPager((pageIndex, token) => {
state = 'resolving';
return new Promise((_, e) => {
token.onCancellationRequested(() => {
state = 'idle';
e(canceled());
});
});
});
const model = new PagedModel(pager);
assert.equal(state, 'idle');
const tokenSource1 = new CancellationTokenSource();
const promise1 = model.resolve(5, tokenSource1.token).then(
() => assert(false),
err => assert(isPromiseCanceledError(err))
);
assert.equal(state, 'resolving');
const tokenSource2 = new CancellationTokenSource();
const promise2 = model.resolve(6, tokenSource2.token).then(
() => assert(false),
err => assert(isPromiseCanceledError(err))
);
assert.equal(state, 'resolving');
setTimeout(() => {
assert.equal(state, 'resolving');
tokenSource1.cancel();
assert.equal(state, 'resolving');
setTimeout(() => {
assert.equal(state, 'resolving');
tokenSource2.cancel();
assert.equal(state, 'idle');
}, 10);
}, 10);
return Promise.all([promise1, promise2]);
});
});

View File

@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* 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 * as processes from 'vs/base/common/processes';
suite('Processes', () => {
test('sanitizeProcessEnvironment', () => {
let env = {
FOO: 'bar',
ELECTRON_ENABLE_STACK_DUMPING: 'x',
ELECTRON_ENABLE_LOGGING: 'x',
ELECTRON_NO_ASAR: 'x',
ELECTRON_NO_ATTACH_CONSOLE: 'x',
ELECTRON_RUN_AS_NODE: 'x',
GOOGLE_API_KEY: 'x',
VSCODE_CLI: 'x',
VSCODE_DEV: 'x',
VSCODE_IPC_HOOK: 'x',
VSCODE_LOGS: 'x',
VSCODE_NLS_CONFIG: 'x',
VSCODE_PORTABLE: 'x',
VSCODE_PID: 'x',
VSCODE_NODE_CACHED_DATA_DIR: 'x',
VSCODE_NEW_VAR: 'x',
GDK_PIXBUF_MODULE_FILE: 'x',
GDK_PIXBUF_MODULEDIR: 'x',
};
processes.sanitizeProcessEnvironment(env);
assert.equal(env['FOO'], 'bar');
assert.equal(Object.keys(env).length, 1);
});
});

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* 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 { ResourceTree } from 'vs/base/common/resourceTree';
import { URI } from 'vs/base/common/uri';
suite('ResourceTree', function () {
test('ctor', function () {
const tree = new ResourceTree<string, null>(null);
assert.equal(tree.root.childrenCount, 0);
});
test('simple', function () {
const tree = new ResourceTree<string, null>(null);
tree.add(URI.file('/foo/bar.txt'), 'bar contents');
assert.equal(tree.root.childrenCount, 1);
let foo = tree.root.get('foo')!;
assert(foo);
assert.equal(foo.childrenCount, 1);
let bar = foo.get('bar.txt')!;
assert(bar);
assert.equal(bar.element, 'bar contents');
tree.add(URI.file('/hello.txt'), 'hello contents');
assert.equal(tree.root.childrenCount, 2);
let hello = tree.root.get('hello.txt')!;
assert(hello);
assert.equal(hello.element, 'hello contents');
tree.delete(URI.file('/foo/bar.txt'));
assert.equal(tree.root.childrenCount, 1);
hello = tree.root.get('hello.txt')!;
assert(hello);
assert.equal(hello.element, 'hello contents');
});
test('folders with data', function () {
const tree = new ResourceTree<string, null>(null);
assert.equal(tree.root.childrenCount, 0);
tree.add(URI.file('/foo'), 'foo');
assert.equal(tree.root.childrenCount, 1);
assert.equal(tree.root.get('foo')!.element, 'foo');
tree.add(URI.file('/bar'), 'bar');
assert.equal(tree.root.childrenCount, 2);
assert.equal(tree.root.get('bar')!.element, 'bar');
tree.add(URI.file('/foo/file.txt'), 'file');
assert.equal(tree.root.childrenCount, 2);
assert.equal(tree.root.get('foo')!.element, 'foo');
assert.equal(tree.root.get('bar')!.element, 'bar');
assert.equal(tree.root.get('foo')!.get('file.txt')!.element, 'file');
tree.delete(URI.file('/foo'));
assert.equal(tree.root.childrenCount, 1);
assert(!tree.root.get('foo'));
assert.equal(tree.root.get('bar')!.element, 'bar');
tree.delete(URI.file('/bar'));
assert.equal(tree.root.childrenCount, 0);
assert(!tree.root.get('foo'));
assert(!tree.root.get('bar'));
});
});

View File

@@ -0,0 +1,432 @@
/*---------------------------------------------------------------------------------------------
* 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 { dirname, basename, distinctParents, joinPath, normalizePath, isAbsolutePath, relativePath, removeTrailingPathSeparator, hasTrailingPathSeparator, resolvePath, addTrailingPathSeparator, extUri, extUriIgnorePathCase } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { isWindows } from 'vs/base/common/platform';
import { toSlashes } from 'vs/base/common/extpath';
import { win32, posix } from 'vs/base/common/path';
suite('Resources', () => {
test('distinctParents', () => {
// Basic
let resources = [
URI.file('/some/folderA/file.txt'),
URI.file('/some/folderB/file.txt'),
URI.file('/some/folderC/file.txt')
];
let distinct = distinctParents(resources, r => r);
assert.equal(distinct.length, 3);
assert.equal(distinct[0].toString(), resources[0].toString());
assert.equal(distinct[1].toString(), resources[1].toString());
assert.equal(distinct[2].toString(), resources[2].toString());
// Parent / Child
resources = [
URI.file('/some/folderA'),
URI.file('/some/folderA/file.txt'),
URI.file('/some/folderA/child/file.txt'),
URI.file('/some/folderA2/file.txt'),
URI.file('/some/file.txt')
];
distinct = distinctParents(resources, r => r);
assert.equal(distinct.length, 3);
assert.equal(distinct[0].toString(), resources[0].toString());
assert.equal(distinct[1].toString(), resources[3].toString());
assert.equal(distinct[2].toString(), resources[4].toString());
});
test('dirname', () => {
if (isWindows) {
assert.equal(dirname(URI.file('c:\\some\\file\\test.txt')).toString(), 'file:///c%3A/some/file');
assert.equal(dirname(URI.file('c:\\some\\file')).toString(), 'file:///c%3A/some');
assert.equal(dirname(URI.file('c:\\some\\file\\')).toString(), 'file:///c%3A/some');
assert.equal(dirname(URI.file('c:\\some')).toString(), 'file:///c%3A/');
assert.equal(dirname(URI.file('C:\\some')).toString(), 'file:///c%3A/');
assert.equal(dirname(URI.file('c:\\')).toString(), 'file:///c%3A/');
} else {
assert.equal(dirname(URI.file('/some/file/test.txt')).toString(), 'file:///some/file');
assert.equal(dirname(URI.file('/some/file/')).toString(), 'file:///some');
assert.equal(dirname(URI.file('/some/file')).toString(), 'file:///some');
}
assert.equal(dirname(URI.parse('foo://a/some/file/test.txt')).toString(), 'foo://a/some/file');
assert.equal(dirname(URI.parse('foo://a/some/file/')).toString(), 'foo://a/some');
assert.equal(dirname(URI.parse('foo://a/some/file')).toString(), 'foo://a/some');
assert.equal(dirname(URI.parse('foo://a/some')).toString(), 'foo://a/');
assert.equal(dirname(URI.parse('foo://a/')).toString(), 'foo://a/');
assert.equal(dirname(URI.parse('foo://a')).toString(), 'foo://a');
// does not explode (https://github.com/microsoft/vscode/issues/41987)
dirname(URI.from({ scheme: 'file', authority: '/users/someone/portal.h' }));
assert.equal(dirname(URI.parse('foo://a/b/c?q')).toString(), 'foo://a/b?q');
});
test('basename', () => {
if (isWindows) {
assert.equal(basename(URI.file('c:\\some\\file\\test.txt')), 'test.txt');
assert.equal(basename(URI.file('c:\\some\\file')), 'file');
assert.equal(basename(URI.file('c:\\some\\file\\')), 'file');
assert.equal(basename(URI.file('C:\\some\\file\\')), 'file');
} else {
assert.equal(basename(URI.file('/some/file/test.txt')), 'test.txt');
assert.equal(basename(URI.file('/some/file/')), 'file');
assert.equal(basename(URI.file('/some/file')), 'file');
assert.equal(basename(URI.file('/some')), 'some');
}
assert.equal(basename(URI.parse('foo://a/some/file/test.txt')), 'test.txt');
assert.equal(basename(URI.parse('foo://a/some/file/')), 'file');
assert.equal(basename(URI.parse('foo://a/some/file')), 'file');
assert.equal(basename(URI.parse('foo://a/some')), 'some');
assert.equal(basename(URI.parse('foo://a/')), '');
assert.equal(basename(URI.parse('foo://a')), '');
});
test('joinPath', () => {
if (isWindows) {
assert.equal(joinPath(URI.file('c:\\foo\\bar'), '/file.js').toString(), 'file:///c%3A/foo/bar/file.js');
assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), 'file.js').toString(), 'file:///c%3A/foo/bar/file.js');
assert.equal(joinPath(URI.file('c:\\foo\\bar\\'), '/file.js').toString(), 'file:///c%3A/foo/bar/file.js');
assert.equal(joinPath(URI.file('c:\\'), '/file.js').toString(), 'file:///c%3A/file.js');
assert.equal(joinPath(URI.file('c:\\'), 'bar/file.js').toString(), 'file:///c%3A/bar/file.js');
assert.equal(joinPath(URI.file('c:\\foo'), './file.js').toString(), 'file:///c%3A/foo/file.js');
assert.equal(joinPath(URI.file('c:\\foo'), '/./file.js').toString(), 'file:///c%3A/foo/file.js');
assert.equal(joinPath(URI.file('C:\\foo'), '../file.js').toString(), 'file:///c%3A/file.js');
assert.equal(joinPath(URI.file('C:\\foo\\.'), '../file.js').toString(), 'file:///c%3A/file.js');
} else {
assert.equal(joinPath(URI.file('/foo/bar'), '/file.js').toString(), 'file:///foo/bar/file.js');
assert.equal(joinPath(URI.file('/foo/bar'), 'file.js').toString(), 'file:///foo/bar/file.js');
assert.equal(joinPath(URI.file('/foo/bar/'), '/file.js').toString(), 'file:///foo/bar/file.js');
assert.equal(joinPath(URI.file('/'), '/file.js').toString(), 'file:///file.js');
assert.equal(joinPath(URI.file('/foo/bar'), './file.js').toString(), 'file:///foo/bar/file.js');
assert.equal(joinPath(URI.file('/foo/bar'), '/./file.js').toString(), 'file:///foo/bar/file.js');
assert.equal(joinPath(URI.file('/foo/bar'), '../file.js').toString(), 'file:///foo/file.js');
}
assert.equal(joinPath(URI.parse('foo://a/foo/bar')).toString(), 'foo://a/foo/bar');
assert.equal(joinPath(URI.parse('foo://a/foo/bar'), '/file.js').toString(), 'foo://a/foo/bar/file.js');
assert.equal(joinPath(URI.parse('foo://a/foo/bar'), 'file.js').toString(), 'foo://a/foo/bar/file.js');
assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '/file.js').toString(), 'foo://a/foo/bar/file.js');
assert.equal(joinPath(URI.parse('foo://a/'), '/file.js').toString(), 'foo://a/file.js');
assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), './file.js').toString(), 'foo://a/foo/bar/file.js');
assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '/./file.js').toString(), 'foo://a/foo/bar/file.js');
assert.equal(joinPath(URI.parse('foo://a/foo/bar/'), '../file.js').toString(), 'foo://a/foo/file.js');
assert.equal(
joinPath(URI.from({ scheme: 'myScheme', authority: 'authority', path: '/path', query: 'query', fragment: 'fragment' }), '/file.js').toString(),
'myScheme://authority/path/file.js?query#fragment');
});
test('normalizePath', () => {
if (isWindows) {
assert.equal(normalizePath(URI.file('c:\\foo\\.\\bar')).toString(), 'file:///c%3A/foo/bar');
assert.equal(normalizePath(URI.file('c:\\foo\\.')).toString(), 'file:///c%3A/foo');
assert.equal(normalizePath(URI.file('c:\\foo\\.\\')).toString(), 'file:///c%3A/foo/');
assert.equal(normalizePath(URI.file('c:\\foo\\..')).toString(), 'file:///c%3A/');
assert.equal(normalizePath(URI.file('c:\\foo\\..\\bar')).toString(), 'file:///c%3A/bar');
assert.equal(normalizePath(URI.file('c:\\foo\\..\\..\\bar')).toString(), 'file:///c%3A/bar');
assert.equal(normalizePath(URI.file('c:\\foo\\foo\\..\\..\\bar')).toString(), 'file:///c%3A/bar');
assert.equal(normalizePath(URI.file('C:\\foo\\foo\\.\\..\\..\\bar')).toString(), 'file:///c%3A/bar');
assert.equal(normalizePath(URI.file('C:\\foo\\foo\\.\\..\\some\\..\\bar')).toString(), 'file:///c%3A/foo/bar');
} else {
assert.equal(normalizePath(URI.file('/foo/./bar')).toString(), 'file:///foo/bar');
assert.equal(normalizePath(URI.file('/foo/.')).toString(), 'file:///foo');
assert.equal(normalizePath(URI.file('/foo/./')).toString(), 'file:///foo/');
assert.equal(normalizePath(URI.file('/foo/..')).toString(), 'file:///');
assert.equal(normalizePath(URI.file('/foo/../bar')).toString(), 'file:///bar');
assert.equal(normalizePath(URI.file('/foo/../../bar')).toString(), 'file:///bar');
assert.equal(normalizePath(URI.file('/foo/foo/../../bar')).toString(), 'file:///bar');
assert.equal(normalizePath(URI.file('/foo/foo/./../../bar')).toString(), 'file:///bar');
assert.equal(normalizePath(URI.file('/foo/foo/./../some/../bar')).toString(), 'file:///foo/bar');
assert.equal(normalizePath(URI.file('/f')).toString(), 'file:///f');
}
assert.equal(normalizePath(URI.parse('foo://a/foo/./bar')).toString(), 'foo://a/foo/bar');
assert.equal(normalizePath(URI.parse('foo://a/foo/.')).toString(), 'foo://a/foo');
assert.equal(normalizePath(URI.parse('foo://a/foo/./')).toString(), 'foo://a/foo/');
assert.equal(normalizePath(URI.parse('foo://a/foo/..')).toString(), 'foo://a/');
assert.equal(normalizePath(URI.parse('foo://a/foo/../bar')).toString(), 'foo://a/bar');
assert.equal(normalizePath(URI.parse('foo://a/foo/../../bar')).toString(), 'foo://a/bar');
assert.equal(normalizePath(URI.parse('foo://a/foo/foo/../../bar')).toString(), 'foo://a/bar');
assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../../bar')).toString(), 'foo://a/bar');
assert.equal(normalizePath(URI.parse('foo://a/foo/foo/./../some/../bar')).toString(), 'foo://a/foo/bar');
assert.equal(normalizePath(URI.parse('foo://a')).toString(), 'foo://a');
assert.equal(normalizePath(URI.parse('foo://a/')).toString(), 'foo://a/');
assert.equal(normalizePath(URI.parse('foo://a/foo/./bar?q=1')).toString(), URI.parse('foo://a/foo/bar?q%3D1').toString());
});
test('isAbsolute', () => {
if (isWindows) {
assert.equal(isAbsolutePath(URI.file('c:\\foo\\')), true);
assert.equal(isAbsolutePath(URI.file('C:\\foo\\')), true);
assert.equal(isAbsolutePath(URI.file('bar')), true); // URI normalizes all file URIs to be absolute
} else {
assert.equal(isAbsolutePath(URI.file('/foo/bar')), true);
assert.equal(isAbsolutePath(URI.file('bar')), true); // URI normalizes all file URIs to be absolute
}
assert.equal(isAbsolutePath(URI.parse('foo:foo')), false);
assert.equal(isAbsolutePath(URI.parse('foo://a/foo/.')), true);
});
function assertTrailingSeparator(u1: URI, expected: boolean) {
assert.equal(hasTrailingPathSeparator(u1), expected, u1.toString());
}
function assertRemoveTrailingSeparator(u1: URI, expected: URI) {
assertEqualURI(removeTrailingPathSeparator(u1), expected, u1.toString());
}
function assertAddTrailingSeparator(u1: URI, expected: URI) {
assertEqualURI(addTrailingPathSeparator(u1), expected, u1.toString());
}
test('trailingPathSeparator', () => {
assertTrailingSeparator(URI.parse('foo://a/foo'), false);
assertTrailingSeparator(URI.parse('foo://a/foo/'), true);
assertTrailingSeparator(URI.parse('foo://a/'), false);
assertTrailingSeparator(URI.parse('foo://a'), false);
assertRemoveTrailingSeparator(URI.parse('foo://a/foo'), URI.parse('foo://a/foo'));
assertRemoveTrailingSeparator(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo'));
assertRemoveTrailingSeparator(URI.parse('foo://a/'), URI.parse('foo://a/'));
assertRemoveTrailingSeparator(URI.parse('foo://a'), URI.parse('foo://a'));
assertAddTrailingSeparator(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/'));
assertAddTrailingSeparator(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo/'));
assertAddTrailingSeparator(URI.parse('foo://a/'), URI.parse('foo://a/'));
assertAddTrailingSeparator(URI.parse('foo://a'), URI.parse('foo://a/'));
if (isWindows) {
assertTrailingSeparator(URI.file('c:\\a\\foo'), false);
assertTrailingSeparator(URI.file('c:\\a\\foo\\'), true);
assertTrailingSeparator(URI.file('c:\\'), false);
assertTrailingSeparator(URI.file('\\\\server\\share\\some\\'), true);
assertTrailingSeparator(URI.file('\\\\server\\share\\'), false);
assertRemoveTrailingSeparator(URI.file('c:\\a\\foo'), URI.file('c:\\a\\foo'));
assertRemoveTrailingSeparator(URI.file('c:\\a\\foo\\'), URI.file('c:\\a\\foo'));
assertRemoveTrailingSeparator(URI.file('c:\\'), URI.file('c:\\'));
assertRemoveTrailingSeparator(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some'));
assertRemoveTrailingSeparator(URI.file('\\\\server\\share\\'), URI.file('\\\\server\\share\\'));
assertAddTrailingSeparator(URI.file('c:\\a\\foo'), URI.file('c:\\a\\foo\\'));
assertAddTrailingSeparator(URI.file('c:\\a\\foo\\'), URI.file('c:\\a\\foo\\'));
assertAddTrailingSeparator(URI.file('c:\\'), URI.file('c:\\'));
assertAddTrailingSeparator(URI.file('\\\\server\\share\\some'), URI.file('\\\\server\\share\\some\\'));
assertAddTrailingSeparator(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some\\'));
} else {
assertTrailingSeparator(URI.file('/foo/bar'), false);
assertTrailingSeparator(URI.file('/foo/bar/'), true);
assertTrailingSeparator(URI.file('/'), false);
assertRemoveTrailingSeparator(URI.file('/foo/bar'), URI.file('/foo/bar'));
assertRemoveTrailingSeparator(URI.file('/foo/bar/'), URI.file('/foo/bar'));
assertRemoveTrailingSeparator(URI.file('/'), URI.file('/'));
assertAddTrailingSeparator(URI.file('/foo/bar'), URI.file('/foo/bar/'));
assertAddTrailingSeparator(URI.file('/foo/bar/'), URI.file('/foo/bar/'));
assertAddTrailingSeparator(URI.file('/'), URI.file('/'));
}
});
function assertEqualURI(actual: URI, expected: URI, message?: string, ignoreCase?: boolean) {
let util = ignoreCase ? extUriIgnorePathCase : extUri;
if (!util.isEqual(expected, actual)) {
assert.equal(actual.toString(), expected.toString(), message);
}
}
function assertRelativePath(u1: URI, u2: URI, expectedPath: string | undefined, ignoreJoin?: boolean, ignoreCase?: boolean) {
let util = ignoreCase ? extUriIgnorePathCase : extUri;
assert.equal(util.relativePath(u1, u2), expectedPath, `from ${u1.toString()} to ${u2.toString()}`);
if (expectedPath !== undefined && !ignoreJoin) {
assertEqualURI(removeTrailingPathSeparator(joinPath(u1, expectedPath)), removeTrailingPathSeparator(u2), 'joinPath on relativePath should be equal', ignoreCase);
}
}
test('relativePath', () => {
assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/bar'), 'bar');
assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/bar/'), 'bar');
assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/bar/goo'), 'bar/goo');
assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a/foo/bar/goo'), 'foo/bar/goo');
assertRelativePath(URI.parse('foo://a/foo/xoo'), URI.parse('foo://a/foo/bar'), '../bar');
assertRelativePath(URI.parse('foo://a/foo/xoo/yoo'), URI.parse('foo://a'), '../../..', true);
assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo/'), '');
assertRelativePath(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo'), '');
assertRelativePath(URI.parse('foo://a/foo/'), URI.parse('foo://a/foo/'), '');
assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://a/foo'), '');
assertRelativePath(URI.parse('foo://a'), URI.parse('foo://a'), '', true);
assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a/'), '');
assertRelativePath(URI.parse('foo://a/'), URI.parse('foo://a'), '', true);
assertRelativePath(URI.parse('foo://a/foo?q'), URI.parse('foo://a/foo/bar#h'), 'bar', true);
assertRelativePath(URI.parse('foo://'), URI.parse('foo://a/b'), undefined);
assertRelativePath(URI.parse('foo://a2/b'), URI.parse('foo://a/b'), undefined);
assertRelativePath(URI.parse('goo://a/b'), URI.parse('foo://a/b'), undefined);
assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://A/FOO/bar/goo'), 'bar/goo', false, true);
assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://A/FOO/BAR/GOO'), 'BAR/GOO', false, true);
assertRelativePath(URI.parse('foo://a/foo/xoo'), URI.parse('foo://A/FOO/BAR/GOO'), '../BAR/GOO', false, true);
assertRelativePath(URI.parse('foo:///c:/a/foo'), URI.parse('foo:///C:/a/foo/xoo/'), 'xoo', false, true);
if (isWindows) {
assertRelativePath(URI.file('c:\\foo\\bar'), URI.file('c:\\foo\\bar'), '');
assertRelativePath(URI.file('c:\\foo\\bar\\huu'), URI.file('c:\\foo\\bar'), '..');
assertRelativePath(URI.file('c:\\foo\\bar\\a1\\a2'), URI.file('c:\\foo\\bar'), '../..');
assertRelativePath(URI.file('c:\\foo\\bar\\'), URI.file('c:\\foo\\bar\\a1\\a2'), 'a1/a2');
assertRelativePath(URI.file('c:\\foo\\bar\\'), URI.file('c:\\foo\\bar\\a1\\a2\\'), 'a1/a2');
assertRelativePath(URI.file('c:\\'), URI.file('c:\\foo\\bar'), 'foo/bar');
assertRelativePath(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share\\some\\path'), 'path');
assertRelativePath(URI.file('\\\\server\\share\\some\\'), URI.file('\\\\server\\share2\\some\\path'), '../../share2/some/path', true); // ignore joinPath assert: path.join is not root aware
} else {
assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/bar'), 'bar');
assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/bar/'), 'bar');
assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/bar/goo'), 'bar/goo');
assertRelativePath(URI.file('/a/'), URI.file('/a/foo/bar/goo'), 'foo/bar/goo');
assertRelativePath(URI.file('/'), URI.file('/a/foo/bar/goo'), 'a/foo/bar/goo');
assertRelativePath(URI.file('/a/foo/xoo'), URI.file('/a/foo/bar'), '../bar');
assertRelativePath(URI.file('/a/foo/xoo/yoo'), URI.file('/a'), '../../..');
assertRelativePath(URI.file('/a/foo'), URI.file('/a/foo/'), '');
assertRelativePath(URI.file('/a/foo'), URI.file('/b/foo/'), '../../b/foo');
}
});
function assertResolve(u1: URI, path: string, expected: URI) {
const actual = resolvePath(u1, path);
assertEqualURI(actual, expected, `from ${u1.toString()} and ${path}`);
const p = path.indexOf('/') !== -1 ? posix : win32;
if (!p.isAbsolute(path)) {
let expectedPath = isWindows ? toSlashes(path) : path;
expectedPath = expectedPath.startsWith('./') ? expectedPath.substr(2) : expectedPath;
assert.equal(relativePath(u1, actual), expectedPath, `relativePath (${u1.toString()}) on actual (${actual.toString()}) should be to path (${expectedPath})`);
}
}
test('resolve', () => {
if (isWindows) {
assertResolve(URI.file('c:\\foo\\bar'), 'file.js', URI.file('c:\\foo\\bar\\file.js'));
assertResolve(URI.file('c:\\foo\\bar'), 't\\file.js', URI.file('c:\\foo\\bar\\t\\file.js'));
assertResolve(URI.file('c:\\foo\\bar'), '.\\t\\file.js', URI.file('c:\\foo\\bar\\t\\file.js'));
assertResolve(URI.file('c:\\foo\\bar'), 'a1/file.js', URI.file('c:\\foo\\bar\\a1\\file.js'));
assertResolve(URI.file('c:\\foo\\bar'), './a1/file.js', URI.file('c:\\foo\\bar\\a1\\file.js'));
assertResolve(URI.file('c:\\foo\\bar'), '\\b1\\file.js', URI.file('c:\\b1\\file.js'));
assertResolve(URI.file('c:\\foo\\bar'), '/b1/file.js', URI.file('c:\\b1\\file.js'));
assertResolve(URI.file('c:\\foo\\bar\\'), 'file.js', URI.file('c:\\foo\\bar\\file.js'));
assertResolve(URI.file('c:\\'), 'file.js', URI.file('c:\\file.js'));
assertResolve(URI.file('c:\\'), '\\b1\\file.js', URI.file('c:\\b1\\file.js'));
assertResolve(URI.file('c:\\'), '/b1/file.js', URI.file('c:\\b1\\file.js'));
assertResolve(URI.file('c:\\'), 'd:\\foo\\bar.txt', URI.file('d:\\foo\\bar.txt'));
assertResolve(URI.file('\\\\server\\share\\some\\'), 'b1\\file.js', URI.file('\\\\server\\share\\some\\b1\\file.js'));
assertResolve(URI.file('\\\\server\\share\\some\\'), '\\file.js', URI.file('\\\\server\\share\\file.js'));
assertResolve(URI.file('c:\\'), '\\\\server\\share\\some\\', URI.file('\\\\server\\share\\some'));
assertResolve(URI.file('\\\\server\\share\\some\\'), 'c:\\', URI.file('c:\\'));
} else {
assertResolve(URI.file('/foo/bar'), 'file.js', URI.file('/foo/bar/file.js'));
assertResolve(URI.file('/foo/bar'), './file.js', URI.file('/foo/bar/file.js'));
assertResolve(URI.file('/foo/bar'), '/file.js', URI.file('/file.js'));
assertResolve(URI.file('/foo/bar/'), 'file.js', URI.file('/foo/bar/file.js'));
assertResolve(URI.file('/'), 'file.js', URI.file('/file.js'));
assertResolve(URI.file(''), './file.js', URI.file('/file.js'));
assertResolve(URI.file(''), '/file.js', URI.file('/file.js'));
}
assertResolve(URI.parse('foo://server/foo/bar'), 'file.js', URI.parse('foo://server/foo/bar/file.js'));
assertResolve(URI.parse('foo://server/foo/bar'), './file.js', URI.parse('foo://server/foo/bar/file.js'));
assertResolve(URI.parse('foo://server/foo/bar'), './file.js', URI.parse('foo://server/foo/bar/file.js'));
assertResolve(URI.parse('foo://server/foo/bar'), 'c:\\a1\\b1', URI.parse('foo://server/c:/a1/b1'));
assertResolve(URI.parse('foo://server/foo/bar'), 'c:\\', URI.parse('foo://server/c:'));
});
function assertIsEqual(u1: URI, u2: URI, ignoreCase: boolean | undefined, expected: boolean) {
let util = ignoreCase ? extUriIgnorePathCase : extUri;
assert.equal(util.isEqual(u1, u2), expected, `${u1.toString()}${expected ? '===' : '!=='}${u2.toString()}`);
assert.equal(util.compare(u1, u2) === 0, expected);
assert.equal(util.getComparisonKey(u1) === util.getComparisonKey(u2), expected, `comparison keys ${u1.toString()}, ${u2.toString()}`);
assert.equal(util.isEqualOrParent(u1, u2), expected, `isEqualOrParent ${u1.toString()}, ${u2.toString()}`);
if (!ignoreCase) {
assert.equal(u1.toString() === u2.toString(), expected);
}
}
test('isEqual', () => {
let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar');
let fileURI2 = isWindows ? URI.file('C:\\foo\\Bar') : URI.file('/foo/Bar');
assertIsEqual(fileURI, fileURI, true, true);
assertIsEqual(fileURI, fileURI, false, true);
assertIsEqual(fileURI, fileURI, undefined, true);
assertIsEqual(fileURI, fileURI2, true, true);
assertIsEqual(fileURI, fileURI2, false, false);
let fileURI3 = URI.parse('foo://server:453/foo/bar');
let fileURI4 = URI.parse('foo://server:453/foo/Bar');
assertIsEqual(fileURI3, fileURI3, true, true);
assertIsEqual(fileURI3, fileURI3, false, true);
assertIsEqual(fileURI3, fileURI3, undefined, true);
assertIsEqual(fileURI3, fileURI4, true, true);
assertIsEqual(fileURI3, fileURI4, false, false);
assertIsEqual(fileURI, fileURI3, true, false);
assertIsEqual(URI.parse('file://server'), URI.parse('file://server/'), true, true);
assertIsEqual(URI.parse('http://server'), URI.parse('http://server/'), true, true);
assertIsEqual(URI.parse('foo://server'), URI.parse('foo://server/'), true, false); // only selected scheme have / as the default path
assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo/'), true, false);
assertIsEqual(URI.parse('foo://server/foo'), URI.parse('foo://server/foo?'), true, true);
let fileURI5 = URI.parse('foo://server:453/foo/bar?q=1');
let fileURI6 = URI.parse('foo://server:453/foo/bar#xy');
assertIsEqual(fileURI5, fileURI5, true, true);
assertIsEqual(fileURI5, fileURI3, true, false);
assertIsEqual(fileURI6, fileURI6, true, true);
assertIsEqual(fileURI6, fileURI5, true, false);
assertIsEqual(fileURI6, fileURI3, true, false);
});
test('isEqualOrParent', () => {
let fileURI = isWindows ? URI.file('c:\\foo\\bar') : URI.file('/foo/bar');
let fileURI2 = isWindows ? URI.file('c:\\foo') : URI.file('/foo');
let fileURI2b = isWindows ? URI.file('C:\\Foo\\') : URI.file('/Foo/');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI), true, '1');
assert.equal(extUri.isEqualOrParent(fileURI, fileURI), true, '2');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI2), true, '3');
assert.equal(extUri.isEqualOrParent(fileURI, fileURI2), true, '4');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI, fileURI2b), true, '5');
assert.equal(extUri.isEqualOrParent(fileURI, fileURI2b), false, '6');
assert.equal(extUri.isEqualOrParent(fileURI2, fileURI), false, '7');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI2b, fileURI2), true, '8');
let fileURI3 = URI.parse('foo://server:453/foo/bar/goo');
let fileURI4 = URI.parse('foo://server:453/foo/');
let fileURI5 = URI.parse('foo://server:453/foo');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI3, true), true, '11');
assert.equal(extUri.isEqualOrParent(fileURI3, fileURI3), true, '12');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI4, true), true, '13');
assert.equal(extUri.isEqualOrParent(fileURI3, fileURI4), true, '14');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI3, fileURI, true), false, '15');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI5, fileURI5, true), true, '16');
let fileURI6 = URI.parse('foo://server:453/foo?q=1');
let fileURI7 = URI.parse('foo://server:453/foo/bar?q=1');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI5), false, '17');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI6, fileURI6), true, '18');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI6), true, '19');
assert.equal(extUriIgnorePathCase.isEqualOrParent(fileURI7, fileURI5), false, '20');
});
});

View File

@@ -0,0 +1,118 @@
/*---------------------------------------------------------------------------------------------
* 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 { SmoothScrollingOperation, SmoothScrollingUpdate } from 'vs/base/common/scrollable';
class TestSmoothScrollingOperation extends SmoothScrollingOperation {
constructor(from: number, to: number, viewportSize: number, startTime: number, duration: number) {
duration = duration + 10;
startTime = startTime - 10;
super(
{ scrollLeft: 0, scrollTop: from, width: 0, height: viewportSize },
{ scrollLeft: 0, scrollTop: to, width: 0, height: viewportSize },
startTime,
duration
);
}
public testTick(now: number): SmoothScrollingUpdate {
return this._tick(now);
}
}
suite('SmoothScrollingOperation', () => {
const VIEWPORT_HEIGHT = 800;
const ANIMATION_DURATION = 125;
const LINE_HEIGHT = 20;
function extractLines(scrollable: TestSmoothScrollingOperation, now: number): [number, number] {
let scrollTop = scrollable.testTick(now).scrollTop;
let scrollBottom = scrollTop + VIEWPORT_HEIGHT;
const startLineNumber = Math.floor(scrollTop / LINE_HEIGHT);
const endLineNumber = Math.ceil(scrollBottom / LINE_HEIGHT);
return [startLineNumber, endLineNumber];
}
function simulateSmoothScroll(from: number, to: number): [number, number][] {
const scrollable = new TestSmoothScrollingOperation(from, to, VIEWPORT_HEIGHT, 0, ANIMATION_DURATION);
let result: [number, number][] = [], resultLen = 0;
result[resultLen++] = extractLines(scrollable, 0);
result[resultLen++] = extractLines(scrollable, 25);
result[resultLen++] = extractLines(scrollable, 50);
result[resultLen++] = extractLines(scrollable, 75);
result[resultLen++] = extractLines(scrollable, 100);
result[resultLen++] = extractLines(scrollable, 125);
return result;
}
function assertSmoothScroll(from: number, to: number, expected: [number, number][]): void {
const actual = simulateSmoothScroll(from, to);
assert.deepEqual(actual, expected);
}
test('scroll 25 lines (40 fit)', () => {
assertSmoothScroll(0, 500, [
[5, 46],
[14, 55],
[20, 61],
[23, 64],
[24, 65],
[25, 65],
]);
});
test('scroll 75 lines (40 fit)', () => {
assertSmoothScroll(0, 1500, [
[15, 56],
[44, 85],
[62, 103],
[71, 112],
[74, 115],
[75, 115],
]);
});
test('scroll 100 lines (40 fit)', () => {
assertSmoothScroll(0, 2000, [
[20, 61],
[59, 100],
[82, 123],
[94, 135],
[99, 140],
[100, 140],
]);
});
test('scroll 125 lines (40 fit)', () => {
assertSmoothScroll(0, 2500, [
[16, 57],
[29, 70],
[107, 148],
[119, 160],
[124, 165],
[125, 165],
]);
});
test('scroll 500 lines (40 fit)', () => {
assertSmoothScroll(0, 10000, [
[16, 57],
[29, 70],
[482, 523],
[494, 535],
[499, 540],
[500, 540],
]);
});
});

View File

@@ -0,0 +1,218 @@
/*---------------------------------------------------------------------------------------------
* 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 { SkipList } from 'vs/base/common/skipList';
import { StopWatch } from 'vs/base/common/stopwatch';
import { binarySearch } from 'vs/base/common/arrays';
suite('SkipList', function () {
function assertValues<V>(list: SkipList<any, V>, expected: V[]) {
assert.equal(list.size, expected.length);
assert.deepEqual([...list.values()], expected);
let valuesFromEntries = [...list.entries()].map(entry => entry[1]);
assert.deepEqual(valuesFromEntries, expected);
let valuesFromIter = [...list].map(entry => entry[1]);
assert.deepEqual(valuesFromIter, expected);
let i = 0;
list.forEach((value, _key, map) => {
assert.ok(map === list);
assert.deepEqual(value, expected[i++]);
});
}
function assertKeys<K>(list: SkipList<K, any>, expected: K[]) {
assert.equal(list.size, expected.length);
assert.deepEqual([...list.keys()], expected);
let keysFromEntries = [...list.entries()].map(entry => entry[0]);
assert.deepEqual(keysFromEntries, expected);
let keysFromIter = [...list].map(entry => entry[0]);
assert.deepEqual(keysFromIter, expected);
let i = 0;
list.forEach((_value, key, map) => {
assert.ok(map === list);
assert.deepEqual(key, expected[i++]);
});
}
test('set/get/delete', function () {
let list = new SkipList<number, number>((a, b) => a - b);
assert.equal(list.get(3), undefined);
list.set(3, 1);
assert.equal(list.get(3), 1);
assertValues(list, [1]);
list.set(3, 3);
assertValues(list, [3]);
list.set(1, 1);
list.set(4, 4);
assert.equal(list.get(3), 3);
assert.equal(list.get(1), 1);
assert.equal(list.get(4), 4);
assertValues(list, [1, 3, 4]);
assert.equal(list.delete(17), false);
assert.equal(list.delete(1), true);
assert.equal(list.get(1), undefined);
assert.equal(list.get(3), 3);
assert.equal(list.get(4), 4);
assertValues(list, [3, 4]);
});
test('Figure 3', function () {
let list = new SkipList<number, boolean>((a, b) => a - b);
list.set(3, true);
list.set(6, true);
list.set(7, true);
list.set(9, true);
list.set(12, true);
list.set(19, true);
list.set(21, true);
list.set(25, true);
assertKeys(list, [3, 6, 7, 9, 12, 19, 21, 25]);
list.set(17, true);
assert.deepEqual(list.size, 9);
assertKeys(list, [3, 6, 7, 9, 12, 17, 19, 21, 25]);
});
test('capacity max', function () {
let list = new SkipList<number, boolean>((a, b) => a - b, 10);
list.set(1, true);
list.set(2, true);
list.set(3, true);
list.set(4, true);
list.set(5, true);
list.set(6, true);
list.set(7, true);
list.set(8, true);
list.set(9, true);
list.set(10, true);
list.set(11, true);
list.set(12, true);
assertKeys(list, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
});
const cmp = (a: number, b: number): number => {
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
};
function insertArraySorted(array: number[], element: number) {
let idx = binarySearch(array, element, cmp);
if (idx >= 0) {
array[idx] = element;
} else {
idx = ~idx;
// array = array.slice(0, idx).concat(element, array.slice(idx));
array.splice(idx, 0, element);
}
return array;
}
function delArraySorted(array: number[], element: number) {
let idx = binarySearch(array, element, cmp);
if (idx >= 0) {
// array = array.slice(0, idx).concat(array.slice(idx));
array.splice(idx, 1);
}
return array;
}
test('perf', function () {
this.skip();
// data
const max = 2 ** 16;
const values = new Set<number>();
for (let i = 0; i < max; i++) {
let value = Math.floor(Math.random() * max);
values.add(value);
}
console.log(values.size);
// init
let list = new SkipList<number, boolean>(cmp, max);
let sw = new StopWatch(true);
values.forEach(value => list.set(value, true));
sw.stop();
console.log(`[LIST] ${list.size} elements after ${sw.elapsed()}ms`);
let array: number[] = [];
sw = new StopWatch(true);
values.forEach(value => array = insertArraySorted(array, value));
sw.stop();
console.log(`[ARRAY] ${array.length} elements after ${sw.elapsed()}ms`);
// get
sw = new StopWatch(true);
let someValues = [...values].slice(0, values.size / 4);
someValues.forEach(key => {
let value = list.get(key); // find
console.assert(value, '[LIST] must have ' + key);
list.get(-key); // miss
});
sw.stop();
console.log(`[LIST] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
sw = new StopWatch(true);
someValues.forEach(key => {
let idx = binarySearch(array, key, cmp); // find
console.assert(idx >= 0, '[ARRAY] must have ' + key);
binarySearch(array, -key, cmp); // miss
});
sw.stop();
console.log(`[ARRAY] retrieve ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
// insert
sw = new StopWatch(true);
someValues.forEach(key => {
list.set(-key, false);
});
sw.stop();
console.log(`[LIST] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
sw = new StopWatch(true);
someValues.forEach(key => {
array = insertArraySorted(array, -key);
});
sw.stop();
console.log(`[ARRAY] insert ${sw.elapsed()}ms (${(sw.elapsed() / someValues.length).toPrecision(4)}ms/op)`);
// delete
sw = new StopWatch(true);
someValues.forEach(key => {
list.delete(key); // find
list.delete(-key); // miss
});
sw.stop();
console.log(`[LIST] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
sw = new StopWatch(true);
someValues.forEach(key => {
array = delArraySorted(array, key); // find
array = delArraySorted(array, -key); // miss
});
sw.stop();
console.log(`[ARRAY] delete ${sw.elapsed()}ms (${(sw.elapsed() / (someValues.length * 2)).toPrecision(4)}ms/op)`);
});
});

View File

@@ -0,0 +1,335 @@
/*---------------------------------------------------------------------------------------------
* 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 { isReadableStream, newWriteableStream, Readable, consumeReadable, peekReadable, consumeStream, ReadableStream, toStream, toReadable, transform, peekStream, isReadableBufferedStream } from 'vs/base/common/stream';
import { timeout } from 'vs/base/common/async';
suite('Stream', () => {
test('isReadableStream', () => {
assert.ok(!isReadableStream(Object.create(null)));
assert.ok(isReadableStream(newWriteableStream(d => d)));
});
test('isReadableBufferedStream', async () => {
assert.ok(!isReadableBufferedStream(Object.create(null)));
const stream = newWriteableStream(d => d);
stream.end();
const bufferedStream = await peekStream(stream, 1);
assert.ok(isReadableBufferedStream(bufferedStream));
});
test('WriteableStream - basics', () => {
const stream = newWriteableStream<string>(strings => strings.join());
let error = false;
stream.on('error', e => {
error = true;
});
let end = false;
stream.on('end', () => {
end = true;
});
stream.write('Hello');
const chunks: string[] = [];
stream.on('data', data => {
chunks.push(data);
});
assert.equal(chunks[0], 'Hello');
stream.write('World');
assert.equal(chunks[1], 'World');
assert.equal(error, false);
assert.equal(end, false);
stream.pause();
stream.write('1');
stream.write('2');
stream.write('3');
assert.equal(chunks.length, 2);
stream.resume();
assert.equal(chunks.length, 3);
assert.equal(chunks[2], '1,2,3');
stream.error(new Error());
assert.equal(error, true);
stream.end('Final Bit');
assert.equal(chunks.length, 4);
assert.equal(chunks[3], 'Final Bit');
stream.destroy();
stream.write('Unexpected');
assert.equal(chunks.length, 4);
});
test('WriteableStream - removeListener', () => {
const stream = newWriteableStream<string>(strings => strings.join());
let error = false;
const errorListener = (e: Error) => {
error = true;
};
stream.on('error', errorListener);
let data = false;
const dataListener = () => {
data = true;
};
stream.on('data', dataListener);
stream.write('Hello');
assert.equal(data, true);
data = false;
stream.removeListener('data', dataListener);
stream.write('World');
assert.equal(data, false);
stream.error(new Error());
assert.equal(error, true);
error = false;
stream.removeListener('error', errorListener);
stream.error(new Error());
assert.equal(error, false);
});
test('WriteableStream - highWaterMark', async () => {
const stream = newWriteableStream<string>(strings => strings.join(), { highWaterMark: 3 });
let res = stream.write('1');
assert.ok(!res);
res = stream.write('2');
assert.ok(!res);
res = stream.write('3');
assert.ok(!res);
let promise1 = stream.write('4');
assert.ok(promise1 instanceof Promise);
let promise2 = stream.write('5');
assert.ok(promise2 instanceof Promise);
let drained1 = false;
(async () => {
await promise1;
drained1 = true;
})();
let drained2 = false;
(async () => {
await promise2;
drained2 = true;
})();
let data: string | undefined = undefined;
stream.on('data', chunk => {
data = chunk;
});
assert.ok(data);
await timeout(0);
assert.equal(drained1, true);
assert.equal(drained2, true);
});
test('consumeReadable', () => {
const readable = arrayToReadable(['1', '2', '3', '4', '5']);
const consumed = consumeReadable(readable, strings => strings.join());
assert.equal(consumed, '1,2,3,4,5');
});
test('peekReadable', () => {
for (let i = 0; i < 5; i++) {
const readable = arrayToReadable(['1', '2', '3', '4', '5']);
const consumedOrReadable = peekReadable(readable, strings => strings.join(), i);
if (typeof consumedOrReadable === 'string') {
assert.fail('Unexpected result');
} else {
const consumed = consumeReadable(consumedOrReadable, strings => strings.join());
assert.equal(consumed, '1,2,3,4,5');
}
}
let readable = arrayToReadable(['1', '2', '3', '4', '5']);
let consumedOrReadable = peekReadable(readable, strings => strings.join(), 5);
assert.equal(consumedOrReadable, '1,2,3,4,5');
readable = arrayToReadable(['1', '2', '3', '4', '5']);
consumedOrReadable = peekReadable(readable, strings => strings.join(), 6);
assert.equal(consumedOrReadable, '1,2,3,4,5');
});
test('peekReadable - error handling', async () => {
// 0 Chunks
let stream = newWriteableStream(data => data);
let error: Error | undefined = undefined;
let promise = (async () => {
try {
await peekStream(stream, 1);
} catch (err) {
error = err;
}
})();
stream.error(new Error());
await promise;
assert.ok(error);
// 1 Chunk
stream = newWriteableStream(data => data);
error = undefined;
promise = (async () => {
try {
await peekStream(stream, 1);
} catch (err) {
error = err;
}
})();
stream.write('foo');
stream.error(new Error());
await promise;
assert.ok(error);
// 2 Chunks
stream = newWriteableStream(data => data);
error = undefined;
promise = (async () => {
try {
await peekStream(stream, 1);
} catch (err) {
error = err;
}
})();
stream.write('foo');
stream.write('bar');
stream.error(new Error());
await promise;
assert.ok(!error);
stream.on('error', err => error = err);
stream.on('data', chunk => { });
assert.ok(error);
});
function arrayToReadable<T>(array: T[]): Readable<T> {
return {
read: () => array.shift() || null
};
}
function readableToStream(readable: Readable<string>): ReadableStream<string> {
const stream = newWriteableStream<string>(strings => strings.join());
// Simulate async behavior
setTimeout(() => {
let chunk: string | null = null;
while ((chunk = readable.read()) !== null) {
stream.write(chunk);
}
stream.end();
}, 0);
return stream;
}
test('consumeStream', async () => {
const stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5']));
const consumed = await consumeStream(stream, strings => strings.join());
assert.equal(consumed, '1,2,3,4,5');
});
test('peekStream', async () => {
for (let i = 0; i < 5; i++) {
const stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5']));
const result = await peekStream(stream, i);
assert.equal(stream, result.stream);
if (result.ended) {
assert.fail('Unexpected result, stream should not have ended yet');
} else {
assert.equal(result.buffer.length, i + 1, `maxChunks: ${i}`);
const additionalResult: string[] = [];
await consumeStream(stream, strings => {
additionalResult.push(...strings);
return strings.join();
});
assert.equal([...result.buffer, ...additionalResult].join(), '1,2,3,4,5');
}
}
let stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5']));
let result = await peekStream(stream, 5);
assert.equal(stream, result.stream);
assert.equal(result.buffer.join(), '1,2,3,4,5');
assert.equal(result.ended, true);
stream = readableToStream(arrayToReadable(['1', '2', '3', '4', '5']));
result = await peekStream(stream, 6);
assert.equal(stream, result.stream);
assert.equal(result.buffer.join(), '1,2,3,4,5');
assert.equal(result.ended, true);
});
test('toStream', async () => {
const stream = toStream('1,2,3,4,5', strings => strings.join());
const consumed = await consumeStream(stream, strings => strings.join());
assert.equal(consumed, '1,2,3,4,5');
});
test('toReadable', async () => {
const readable = toReadable('1,2,3,4,5');
const consumed = await consumeReadable(readable, strings => strings.join());
assert.equal(consumed, '1,2,3,4,5');
});
test('transform', async () => {
const source = newWriteableStream<string>(strings => strings.join());
const result = transform(source, { data: string => string + string }, strings => strings.join());
// Simulate async behavior
setTimeout(() => {
source.write('1');
source.write('2');
source.write('3');
source.write('4');
source.end('5');
}, 0);
const consumed = await consumeStream(result, strings => strings.join());
assert.equal(consumed, '11,22,33,44,55');
});
});

View File

@@ -0,0 +1,420 @@
/*---------------------------------------------------------------------------------------------
* 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 * as strings from 'vs/base/common/strings';
suite('Strings', () => {
test('equalsIgnoreCase', () => {
assert(strings.equalsIgnoreCase('', ''));
assert(!strings.equalsIgnoreCase('', '1'));
assert(!strings.equalsIgnoreCase('1', ''));
assert(strings.equalsIgnoreCase('a', 'a'));
assert(strings.equalsIgnoreCase('abc', 'Abc'));
assert(strings.equalsIgnoreCase('abc', 'ABC'));
assert(strings.equalsIgnoreCase('Höhenmeter', 'HÖhenmeter'));
assert(strings.equalsIgnoreCase('ÖL', 'Öl'));
});
test('beginsWithIgnoreCase', () => {
assert(strings.startsWithIgnoreCase('', ''));
assert(!strings.startsWithIgnoreCase('', '1'));
assert(strings.startsWithIgnoreCase('1', ''));
assert(strings.startsWithIgnoreCase('a', 'a'));
assert(strings.startsWithIgnoreCase('abc', 'Abc'));
assert(strings.startsWithIgnoreCase('abc', 'ABC'));
assert(strings.startsWithIgnoreCase('Höhenmeter', 'HÖhenmeter'));
assert(strings.startsWithIgnoreCase('ÖL', 'Öl'));
assert(strings.startsWithIgnoreCase('alles klar', 'a'));
assert(strings.startsWithIgnoreCase('alles klar', 'A'));
assert(strings.startsWithIgnoreCase('alles klar', 'alles k'));
assert(strings.startsWithIgnoreCase('alles klar', 'alles K'));
assert(strings.startsWithIgnoreCase('alles klar', 'ALLES K'));
assert(strings.startsWithIgnoreCase('alles klar', 'alles klar'));
assert(strings.startsWithIgnoreCase('alles klar', 'ALLES KLAR'));
assert(!strings.startsWithIgnoreCase('alles klar', ' ALLES K'));
assert(!strings.startsWithIgnoreCase('alles klar', 'ALLES K '));
assert(!strings.startsWithIgnoreCase('alles klar', 'öALLES K '));
assert(!strings.startsWithIgnoreCase('alles klar', ' '));
assert(!strings.startsWithIgnoreCase('alles klar', 'ö'));
});
test('compareIgnoreCase', () => {
function assertCompareIgnoreCase(a: string, b: string, recurse = true): void {
let actual = strings.compareIgnoreCase(a, b);
actual = actual > 0 ? 1 : actual < 0 ? -1 : actual;
let expected = strings.compare(a.toLowerCase(), b.toLowerCase());
expected = expected > 0 ? 1 : expected < 0 ? -1 : expected;
assert.equal(actual, expected, `${a} <> ${b}`);
if (recurse) {
assertCompareIgnoreCase(b, a, false);
}
}
assertCompareIgnoreCase('', '');
assertCompareIgnoreCase('abc', 'ABC');
assertCompareIgnoreCase('abc', 'ABc');
assertCompareIgnoreCase('abc', 'ABcd');
assertCompareIgnoreCase('abc', 'abcd');
assertCompareIgnoreCase('foo', 'föo');
assertCompareIgnoreCase('Code', 'code');
assertCompareIgnoreCase('Code', 'cöde');
assertCompareIgnoreCase('B', 'a');
assertCompareIgnoreCase('a', 'B');
assertCompareIgnoreCase('b', 'a');
assertCompareIgnoreCase('a', 'b');
assertCompareIgnoreCase('aa', 'ab');
assertCompareIgnoreCase('aa', 'aB');
assertCompareIgnoreCase('aa', 'aA');
assertCompareIgnoreCase('a', 'aa');
assertCompareIgnoreCase('ab', 'aA');
assertCompareIgnoreCase('O', '/');
});
test('compareIgnoreCase (substring)', () => {
function assertCompareIgnoreCase(a: string, b: string, aStart: number, aEnd: number, bStart: number, bEnd: number, recurse = true): void {
let actual = strings.compareSubstringIgnoreCase(a, b, aStart, aEnd, bStart, bEnd);
actual = actual > 0 ? 1 : actual < 0 ? -1 : actual;
let expected = strings.compare(a.toLowerCase().substring(aStart, aEnd), b.toLowerCase().substring(bStart, bEnd));
expected = expected > 0 ? 1 : expected < 0 ? -1 : expected;
assert.equal(actual, expected, `${a} <> ${b}`);
if (recurse) {
assertCompareIgnoreCase(b, a, bStart, bEnd, aStart, aEnd, false);
}
}
assertCompareIgnoreCase('', '', 0, 0, 0, 0);
assertCompareIgnoreCase('abc', 'ABC', 0, 1, 0, 1);
assertCompareIgnoreCase('abc', 'Aabc', 0, 3, 1, 4);
assertCompareIgnoreCase('abcABc', 'ABcd', 3, 6, 0, 4);
});
test('format', () => {
assert.strictEqual(strings.format('Foo Bar'), 'Foo Bar');
assert.strictEqual(strings.format('Foo {0} Bar'), 'Foo {0} Bar');
assert.strictEqual(strings.format('Foo {0} Bar', 'yes'), 'Foo yes Bar');
assert.strictEqual(strings.format('Foo {0} Bar {0}', 'yes'), 'Foo yes Bar yes');
assert.strictEqual(strings.format('Foo {0} Bar {1}{2}', 'yes'), 'Foo yes Bar {1}{2}');
assert.strictEqual(strings.format('Foo {0} Bar {1}{2}', 'yes', undefined), 'Foo yes Bar undefined{2}');
assert.strictEqual(strings.format('Foo {0} Bar {1}{2}', 'yes', 5, false), 'Foo yes Bar 5false');
assert.strictEqual(strings.format('Foo {0} Bar. {1}', '(foo)', '.test'), 'Foo (foo) Bar. .test');
});
test('lcut', () => {
assert.strictEqual(strings.lcut('foo bar', 0), '');
assert.strictEqual(strings.lcut('foo bar', 1), 'bar');
assert.strictEqual(strings.lcut('foo bar', 3), 'bar');
assert.strictEqual(strings.lcut('foo bar', 4), 'bar'); // Leading whitespace trimmed
assert.strictEqual(strings.lcut('foo bar', 5), 'foo bar');
assert.strictEqual(strings.lcut('test string 0.1.2.3', 3), '2.3');
assert.strictEqual(strings.lcut('', 10), '');
assert.strictEqual(strings.lcut('a', 10), 'a');
});
test('escape', () => {
assert.strictEqual(strings.escape(''), '');
assert.strictEqual(strings.escape('foo'), 'foo');
assert.strictEqual(strings.escape('foo bar'), 'foo bar');
assert.strictEqual(strings.escape('<foo bar>'), '&lt;foo bar&gt;');
assert.strictEqual(strings.escape('<foo>Hello</foo>'), '&lt;foo&gt;Hello&lt;/foo&gt;');
});
test('ltrim', () => {
assert.strictEqual(strings.ltrim('foo', 'f'), 'oo');
assert.strictEqual(strings.ltrim('foo', 'o'), 'foo');
assert.strictEqual(strings.ltrim('http://www.test.de', 'http://'), 'www.test.de');
assert.strictEqual(strings.ltrim('/foo/', '/'), 'foo/');
assert.strictEqual(strings.ltrim('//foo/', '/'), 'foo/');
assert.strictEqual(strings.ltrim('/', ''), '/');
assert.strictEqual(strings.ltrim('/', '/'), '');
assert.strictEqual(strings.ltrim('///', '/'), '');
assert.strictEqual(strings.ltrim('', ''), '');
assert.strictEqual(strings.ltrim('', '/'), '');
});
test('rtrim', () => {
assert.strictEqual(strings.rtrim('foo', 'o'), 'f');
assert.strictEqual(strings.rtrim('foo', 'f'), 'foo');
assert.strictEqual(strings.rtrim('http://www.test.de', '.de'), 'http://www.test');
assert.strictEqual(strings.rtrim('/foo/', '/'), '/foo');
assert.strictEqual(strings.rtrim('/foo//', '/'), '/foo');
assert.strictEqual(strings.rtrim('/', ''), '/');
assert.strictEqual(strings.rtrim('/', '/'), '');
assert.strictEqual(strings.rtrim('///', '/'), '');
assert.strictEqual(strings.rtrim('', ''), '');
assert.strictEqual(strings.rtrim('', '/'), '');
});
test('trim', () => {
assert.strictEqual(strings.trim(' foo '), 'foo');
assert.strictEqual(strings.trim(' foo'), 'foo');
assert.strictEqual(strings.trim('bar '), 'bar');
assert.strictEqual(strings.trim(' '), '');
assert.strictEqual(strings.trim('foo bar', 'bar'), 'foo ');
});
test('trimWhitespace', () => {
assert.strictEqual(' foo '.trim(), 'foo');
assert.strictEqual(' foo '.trim(), 'foo');
assert.strictEqual(' foo'.trim(), 'foo');
assert.strictEqual('bar '.trim(), 'bar');
assert.strictEqual(' '.trim(), '');
assert.strictEqual(' '.trim(), '');
});
test('lastNonWhitespaceIndex', () => {
assert.strictEqual(strings.lastNonWhitespaceIndex('abc \t \t '), 2);
assert.strictEqual(strings.lastNonWhitespaceIndex('abc'), 2);
assert.strictEqual(strings.lastNonWhitespaceIndex('abc\t'), 2);
assert.strictEqual(strings.lastNonWhitespaceIndex('abc '), 2);
assert.strictEqual(strings.lastNonWhitespaceIndex('abc \t \t '), 2);
assert.strictEqual(strings.lastNonWhitespaceIndex('abc \t \t abc \t \t '), 11);
assert.strictEqual(strings.lastNonWhitespaceIndex('abc \t \t abc \t \t ', 8), 2);
assert.strictEqual(strings.lastNonWhitespaceIndex(' \t \t '), -1);
});
test('containsRTL', () => {
assert.equal(strings.containsRTL('a'), false);
assert.equal(strings.containsRTL(''), false);
assert.equal(strings.containsRTL(strings.UTF8_BOM_CHARACTER + 'a'), false);
assert.equal(strings.containsRTL('hello world!'), false);
assert.equal(strings.containsRTL('a📚📚b'), false);
assert.equal(strings.containsRTL('هناك حقيقة مثبتة منذ زمن طويل'), true);
assert.equal(strings.containsRTL('זוהי עובדה מבוססת שדעתו'), true);
});
test('containsEmoji', () => {
assert.equal(strings.containsEmoji('a'), false);
assert.equal(strings.containsEmoji(''), false);
assert.equal(strings.containsEmoji(strings.UTF8_BOM_CHARACTER + 'a'), false);
assert.equal(strings.containsEmoji('hello world!'), false);
assert.equal(strings.containsEmoji('هناك حقيقة مثبتة منذ زمن طويل'), false);
assert.equal(strings.containsEmoji('זוהי עובדה מבוססת שדעתו'), false);
assert.equal(strings.containsEmoji('a📚📚b'), true);
assert.equal(strings.containsEmoji('1F600 # 😀 grinning face'), true);
assert.equal(strings.containsEmoji('1F47E # 👾 alien monster'), true);
assert.equal(strings.containsEmoji('1F467 1F3FD # 👧🏽 girl: medium skin tone'), true);
assert.equal(strings.containsEmoji('26EA # ⛪ church'), true);
assert.equal(strings.containsEmoji('231B # ⌛ hourglass'), true);
assert.equal(strings.containsEmoji('2702 # ✂ scissors'), true);
assert.equal(strings.containsEmoji('1F1F7 1F1F4 # 🇷🇴 Romania'), true);
});
test('isBasicASCII', () => {
function assertIsBasicASCII(str: string, expected: boolean): void {
assert.equal(strings.isBasicASCII(str), expected, str + ` (${str.charCodeAt(0)})`);
}
assertIsBasicASCII('abcdefghijklmnopqrstuvwxyz', true);
assertIsBasicASCII('ABCDEFGHIJKLMNOPQRSTUVWXYZ', true);
assertIsBasicASCII('1234567890', true);
assertIsBasicASCII('`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?', true);
assertIsBasicASCII(' ', true);
assertIsBasicASCII('\t', true);
assertIsBasicASCII('\n', true);
assertIsBasicASCII('\r', true);
let ALL = '\r\t\n';
for (let i = 32; i < 127; i++) {
ALL += String.fromCharCode(i);
}
assertIsBasicASCII(ALL, true);
assertIsBasicASCII(String.fromCharCode(31), false);
assertIsBasicASCII(String.fromCharCode(127), false);
assertIsBasicASCII('ü', false);
assertIsBasicASCII('a📚📚b', false);
});
test('createRegExp', () => {
// Empty
assert.throws(() => strings.createRegExp('', false));
// Escapes appropriately
assert.equal(strings.createRegExp('abc', false).source, 'abc');
assert.equal(strings.createRegExp('([^ ,.]*)', false).source, '\\(\\[\\^ ,\\.\\]\\*\\)');
assert.equal(strings.createRegExp('([^ ,.]*)', true).source, '([^ ,.]*)');
// Whole word
assert.equal(strings.createRegExp('abc', false, { wholeWord: true }).source, '\\babc\\b');
assert.equal(strings.createRegExp('abc', true, { wholeWord: true }).source, '\\babc\\b');
assert.equal(strings.createRegExp(' abc', true, { wholeWord: true }).source, ' abc\\b');
assert.equal(strings.createRegExp('abc ', true, { wholeWord: true }).source, '\\babc ');
assert.equal(strings.createRegExp(' abc ', true, { wholeWord: true }).source, ' abc ');
const regExpWithoutFlags = strings.createRegExp('abc', true);
assert(!regExpWithoutFlags.global);
assert(regExpWithoutFlags.ignoreCase);
assert(!regExpWithoutFlags.multiline);
const regExpWithFlags = strings.createRegExp('abc', true, { global: true, matchCase: true, multiline: true });
assert(regExpWithFlags.global);
assert(!regExpWithFlags.ignoreCase);
assert(regExpWithFlags.multiline);
});
test('regExpContainsBackreference', () => {
assert(strings.regExpContainsBackreference('foo \\5 bar'));
assert(strings.regExpContainsBackreference('\\2'));
assert(strings.regExpContainsBackreference('(\\d)(\\n)(\\1)'));
assert(strings.regExpContainsBackreference('(A).*?\\1'));
assert(strings.regExpContainsBackreference('\\\\\\1'));
assert(strings.regExpContainsBackreference('foo \\\\\\1'));
assert(!strings.regExpContainsBackreference(''));
assert(!strings.regExpContainsBackreference('\\\\1'));
assert(!strings.regExpContainsBackreference('foo \\\\1'));
assert(!strings.regExpContainsBackreference('(A).*?\\\\1'));
assert(!strings.regExpContainsBackreference('foo \\d1 bar'));
assert(!strings.regExpContainsBackreference('123'));
});
test('getLeadingWhitespace', () => {
assert.equal(strings.getLeadingWhitespace(' foo'), ' ');
assert.equal(strings.getLeadingWhitespace(' foo', 2), '');
assert.equal(strings.getLeadingWhitespace(' foo', 1, 1), '');
assert.equal(strings.getLeadingWhitespace(' foo', 0, 1), ' ');
assert.equal(strings.getLeadingWhitespace(' '), ' ');
assert.equal(strings.getLeadingWhitespace(' ', 1), ' ');
assert.equal(strings.getLeadingWhitespace(' ', 0, 1), ' ');
assert.equal(strings.getLeadingWhitespace('\t\tfunction foo(){', 0, 1), '\t');
assert.equal(strings.getLeadingWhitespace('\t\tfunction foo(){', 0, 2), '\t\t');
});
test('fuzzyContains', () => {
assert.ok(!strings.fuzzyContains((undefined)!, null!));
assert.ok(strings.fuzzyContains('hello world', 'h'));
assert.ok(!strings.fuzzyContains('hello world', 'q'));
assert.ok(strings.fuzzyContains('hello world', 'hw'));
assert.ok(strings.fuzzyContains('hello world', 'horl'));
assert.ok(strings.fuzzyContains('hello world', 'd'));
assert.ok(!strings.fuzzyContains('hello world', 'wh'));
assert.ok(!strings.fuzzyContains('d', 'dd'));
});
test('startsWithUTF8BOM', () => {
assert(strings.startsWithUTF8BOM(strings.UTF8_BOM_CHARACTER));
assert(strings.startsWithUTF8BOM(strings.UTF8_BOM_CHARACTER + 'a'));
assert(strings.startsWithUTF8BOM(strings.UTF8_BOM_CHARACTER + 'aaaaaaaaaa'));
assert(!strings.startsWithUTF8BOM(' ' + strings.UTF8_BOM_CHARACTER));
assert(!strings.startsWithUTF8BOM('foo'));
assert(!strings.startsWithUTF8BOM(''));
});
test('stripUTF8BOM', () => {
assert.equal(strings.stripUTF8BOM(strings.UTF8_BOM_CHARACTER), '');
assert.equal(strings.stripUTF8BOM(strings.UTF8_BOM_CHARACTER + 'foobar'), 'foobar');
assert.equal(strings.stripUTF8BOM('foobar' + strings.UTF8_BOM_CHARACTER), 'foobar' + strings.UTF8_BOM_CHARACTER);
assert.equal(strings.stripUTF8BOM('abc'), 'abc');
assert.equal(strings.stripUTF8BOM(''), '');
});
test('containsUppercaseCharacter', () => {
[
[null, false],
['', false],
['foo', false],
['föö', false],
['ناك', false],
['מבוססת', false],
['😀', false],
['(#@()*&%()@*#&09827340982374}{:">?></\'\\~`', false],
['Foo', true],
['FOO', true],
['FöÖ', true],
['FöÖ', true],
['\\Foo', true],
].forEach(([str, result]) => {
assert.equal(strings.containsUppercaseCharacter(<string>str), result, `Wrong result for ${str}`);
});
});
test('containsUppercaseCharacter (ignoreEscapedChars)', () => {
[
['\\Woo', false],
['f\\S\\S', false],
['foo', false],
['Foo', true],
].forEach(([str, result]) => {
assert.equal(strings.containsUppercaseCharacter(<string>str, true), result, `Wrong result for ${str}`);
});
});
test('uppercaseFirstLetter', () => {
[
['', ''],
['foo', 'Foo'],
['f', 'F'],
['123', '123'],
['.a', '.a'],
].forEach(([inStr, result]) => {
assert.equal(strings.uppercaseFirstLetter(inStr), result, `Wrong result for ${inStr}`);
});
});
test('getNLines', () => {
assert.equal(strings.getNLines('', 5), '');
assert.equal(strings.getNLines('foo', 5), 'foo');
assert.equal(strings.getNLines('foo\nbar', 5), 'foo\nbar');
assert.equal(strings.getNLines('foo\nbar', 2), 'foo\nbar');
assert.equal(strings.getNLines('foo\nbar', 1), 'foo');
assert.equal(strings.getNLines('foo\nbar'), 'foo');
assert.equal(strings.getNLines('foo\nbar\nsomething', 2), 'foo\nbar');
assert.equal(strings.getNLines('foo', 0), '');
});
test('encodeUTF8', function () {
function assertEncodeUTF8(str: string, expected: number[]): void {
const actual = strings.encodeUTF8(str);
const actualArr: number[] = [];
for (let offset = 0; offset < actual.byteLength; offset++) {
actualArr[offset] = actual[offset];
}
assert.deepEqual(actualArr, expected);
}
function assertDecodeUTF8(data: number[], expected: string): void {
const actual = strings.decodeUTF8(new Uint8Array(data));
assert.deepEqual(actual, expected);
}
function assertEncodeDecodeUTF8(str: string, buff: number[]): void {
assertEncodeUTF8(str, buff);
assertDecodeUTF8(buff, str);
}
assertEncodeDecodeUTF8('\u0000', [0]);
assertEncodeDecodeUTF8('!', [33]);
assertEncodeDecodeUTF8('\u007F', [127]);
assertEncodeDecodeUTF8('\u0080', [194, 128]);
assertEncodeDecodeUTF8('Ɲ', [198, 157]);
assertEncodeDecodeUTF8('\u07FF', [223, 191]);
assertEncodeDecodeUTF8('\u0800', [224, 160, 128]);
assertEncodeDecodeUTF8('ஂ', [224, 174, 130]);
assertEncodeDecodeUTF8('\uffff', [239, 191, 191]);
assertEncodeDecodeUTF8('\u10000', [225, 128, 128, 48]);
assertEncodeDecodeUTF8('🧝', [240, 159, 167, 157]);
});
test('getGraphemeBreakType', () => {
assert.equal(strings.getGraphemeBreakType(0xBC1), strings.GraphemeBreakType.SpacingMark);
});
});

View File

@@ -0,0 +1,212 @@
/*---------------------------------------------------------------------------------------------
* 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 * as types from 'vs/base/common/types';
suite('Types', () => {
test('isFunction', () => {
assert(!types.isFunction(undefined));
assert(!types.isFunction(null));
assert(!types.isFunction('foo'));
assert(!types.isFunction(5));
assert(!types.isFunction(true));
assert(!types.isFunction([]));
assert(!types.isFunction([1, 2, '3']));
assert(!types.isFunction({}));
assert(!types.isFunction({ foo: 'bar' }));
assert(!types.isFunction(/test/));
assert(!types.isFunction(new RegExp('')));
assert(!types.isFunction(new Date()));
assert(types.isFunction(assert));
assert(types.isFunction(function foo() { /**/ }));
});
test('areFunctions', () => {
assert(!types.areFunctions());
assert(!types.areFunctions(null));
assert(!types.areFunctions('foo'));
assert(!types.areFunctions(5));
assert(!types.areFunctions(true));
assert(!types.areFunctions([]));
assert(!types.areFunctions([1, 2, '3']));
assert(!types.areFunctions({}));
assert(!types.areFunctions({ foo: 'bar' }));
assert(!types.areFunctions(/test/));
assert(!types.areFunctions(new RegExp('')));
assert(!types.areFunctions(new Date()));
assert(!types.areFunctions(assert, ''));
assert(types.areFunctions(assert));
assert(types.areFunctions(assert, assert));
assert(types.areFunctions(function foo() { /**/ }));
});
test('isObject', () => {
assert(!types.isObject(undefined));
assert(!types.isObject(null));
assert(!types.isObject('foo'));
assert(!types.isObject(5));
assert(!types.isObject(true));
assert(!types.isObject([]));
assert(!types.isObject([1, 2, '3']));
assert(!types.isObject(/test/));
assert(!types.isObject(new RegExp('')));
assert(!types.isFunction(new Date()));
assert(!types.isObject(assert));
assert(!types.isObject(function foo() { }));
assert(types.isObject({}));
assert(types.isObject({ foo: 'bar' }));
});
test('isEmptyObject', () => {
assert(!types.isEmptyObject(undefined));
assert(!types.isEmptyObject(null));
assert(!types.isEmptyObject('foo'));
assert(!types.isEmptyObject(5));
assert(!types.isEmptyObject(true));
assert(!types.isEmptyObject([]));
assert(!types.isEmptyObject([1, 2, '3']));
assert(!types.isEmptyObject(/test/));
assert(!types.isEmptyObject(new RegExp('')));
assert(!types.isEmptyObject(new Date()));
assert(!types.isEmptyObject(assert));
assert(!types.isEmptyObject(function foo() { /**/ }));
assert(!types.isEmptyObject({ foo: 'bar' }));
assert(types.isEmptyObject({}));
});
test('isArray', () => {
assert(!types.isArray(undefined));
assert(!types.isArray(null));
assert(!types.isArray('foo'));
assert(!types.isArray(5));
assert(!types.isArray(true));
assert(!types.isArray({}));
assert(!types.isArray(/test/));
assert(!types.isArray(new RegExp('')));
assert(!types.isArray(new Date()));
assert(!types.isArray(assert));
assert(!types.isArray(function foo() { /**/ }));
assert(!types.isArray({ foo: 'bar' }));
assert(types.isArray([]));
assert(types.isArray([1, 2, '3']));
});
test('isString', () => {
assert(!types.isString(undefined));
assert(!types.isString(null));
assert(!types.isString(5));
assert(!types.isString([]));
assert(!types.isString([1, 2, '3']));
assert(!types.isString(true));
assert(!types.isString({}));
assert(!types.isString(/test/));
assert(!types.isString(new RegExp('')));
assert(!types.isString(new Date()));
assert(!types.isString(assert));
assert(!types.isString(function foo() { /**/ }));
assert(!types.isString({ foo: 'bar' }));
assert(types.isString('foo'));
});
test('isNumber', () => {
assert(!types.isNumber(undefined));
assert(!types.isNumber(null));
assert(!types.isNumber('foo'));
assert(!types.isNumber([]));
assert(!types.isNumber([1, 2, '3']));
assert(!types.isNumber(true));
assert(!types.isNumber({}));
assert(!types.isNumber(/test/));
assert(!types.isNumber(new RegExp('')));
assert(!types.isNumber(new Date()));
assert(!types.isNumber(assert));
assert(!types.isNumber(function foo() { /**/ }));
assert(!types.isNumber({ foo: 'bar' }));
assert(!types.isNumber(parseInt('A', 10)));
assert(types.isNumber(5));
});
test('isUndefined', () => {
assert(!types.isUndefined(null));
assert(!types.isUndefined('foo'));
assert(!types.isUndefined([]));
assert(!types.isUndefined([1, 2, '3']));
assert(!types.isUndefined(true));
assert(!types.isUndefined({}));
assert(!types.isUndefined(/test/));
assert(!types.isUndefined(new RegExp('')));
assert(!types.isUndefined(new Date()));
assert(!types.isUndefined(assert));
assert(!types.isUndefined(function foo() { /**/ }));
assert(!types.isUndefined({ foo: 'bar' }));
assert(types.isUndefined(undefined));
});
test('isUndefinedOrNull', () => {
assert(!types.isUndefinedOrNull('foo'));
assert(!types.isUndefinedOrNull([]));
assert(!types.isUndefinedOrNull([1, 2, '3']));
assert(!types.isUndefinedOrNull(true));
assert(!types.isUndefinedOrNull({}));
assert(!types.isUndefinedOrNull(/test/));
assert(!types.isUndefinedOrNull(new RegExp('')));
assert(!types.isUndefinedOrNull(new Date()));
assert(!types.isUndefinedOrNull(assert));
assert(!types.isUndefinedOrNull(function foo() { /**/ }));
assert(!types.isUndefinedOrNull({ foo: 'bar' }));
assert(types.isUndefinedOrNull(undefined));
assert(types.isUndefinedOrNull(null));
});
test('assertIsDefined / assertAreDefined', () => {
assert.throws(() => types.assertIsDefined(undefined));
assert.throws(() => types.assertIsDefined(null));
assert.throws(() => types.assertAllDefined(null, undefined));
assert.throws(() => types.assertAllDefined(true, undefined));
assert.throws(() => types.assertAllDefined(undefined, false));
assert.equal(types.assertIsDefined(true), true);
assert.equal(types.assertIsDefined(false), false);
assert.equal(types.assertIsDefined('Hello'), 'Hello');
assert.equal(types.assertIsDefined(''), '');
const res = types.assertAllDefined(1, true, 'Hello');
assert.equal(res[0], 1);
assert.equal(res[1], true);
assert.equal(res[2], 'Hello');
});
test('validateConstraints', () => {
types.validateConstraints([1, 'test', true], [Number, String, Boolean]);
types.validateConstraints([1, 'test', true], ['number', 'string', 'boolean']);
types.validateConstraints([console.log], [Function]);
types.validateConstraints([undefined], [types.isUndefined]);
types.validateConstraints([1], [types.isNumber]);
class Foo { }
types.validateConstraints([new Foo()], [Foo]);
function isFoo(f: any) { }
assert.throws(() => types.validateConstraints([new Foo()], [isFoo]));
function isFoo2(f: any) { return true; }
types.validateConstraints([new Foo()], [isFoo2]);
assert.throws(() => types.validateConstraints([1, true], [types.isNumber, types.isString]));
assert.throws(() => types.validateConstraints(['2'], [types.isNumber]));
assert.throws(() => types.validateConstraints([1, 'test', true], [Number, String, Number]));
});
});

View File

@@ -0,0 +1,570 @@
/*---------------------------------------------------------------------------------------------
* 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, UriComponents } from 'vs/base/common/uri';
import { isWindows } from 'vs/base/common/platform';
suite('URI', () => {
test('file#toString', () => {
assert.equal(URI.file('c:/win/path').toString(), 'file:///c%3A/win/path');
assert.equal(URI.file('C:/win/path').toString(), 'file:///c%3A/win/path');
assert.equal(URI.file('c:/win/path/').toString(), 'file:///c%3A/win/path/');
assert.equal(URI.file('/c:/win/path').toString(), 'file:///c%3A/win/path');
});
test('URI.file (win-special)', () => {
if (isWindows) {
assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c%3A/win/path');
assert.equal(URI.file('c:\\win/path').toString(), 'file:///c%3A/win/path');
} else {
assert.equal(URI.file('c:\\win\\path').toString(), 'file:///c%3A%5Cwin%5Cpath');
assert.equal(URI.file('c:\\win/path').toString(), 'file:///c%3A%5Cwin/path');
}
});
test('file#fsPath (win-special)', () => {
if (isWindows) {
assert.equal(URI.file('c:\\win\\path').fsPath, 'c:\\win\\path');
assert.equal(URI.file('c:\\win/path').fsPath, 'c:\\win\\path');
assert.equal(URI.file('c:/win/path').fsPath, 'c:\\win\\path');
assert.equal(URI.file('c:/win/path/').fsPath, 'c:\\win\\path\\');
assert.equal(URI.file('C:/win/path').fsPath, 'c:\\win\\path');
assert.equal(URI.file('/c:/win/path').fsPath, 'c:\\win\\path');
assert.equal(URI.file('./c/win/path').fsPath, '\\.\\c\\win\\path');
} else {
assert.equal(URI.file('c:/win/path').fsPath, 'c:/win/path');
assert.equal(URI.file('c:/win/path/').fsPath, 'c:/win/path/');
assert.equal(URI.file('C:/win/path').fsPath, 'c:/win/path');
assert.equal(URI.file('/c:/win/path').fsPath, 'c:/win/path');
assert.equal(URI.file('./c/win/path').fsPath, '/./c/win/path');
}
});
test('URI#fsPath - no `fsPath` when no `path`', () => {
const value = URI.parse('file://%2Fhome%2Fticino%2Fdesktop%2Fcpluscplus%2Ftest.cpp');
assert.equal(value.authority, '/home/ticino/desktop/cpluscplus/test.cpp');
assert.equal(value.path, '/');
if (isWindows) {
assert.equal(value.fsPath, '\\');
} else {
assert.equal(value.fsPath, '/');
}
});
test('http#toString', () => {
assert.equal(URI.from({ scheme: 'http', authority: 'www.msft.com', path: '/my/path' }).toString(), 'http://www.msft.com/my/path');
assert.equal(URI.from({ scheme: 'http', authority: 'www.msft.com', path: '/my/path' }).toString(), 'http://www.msft.com/my/path');
assert.equal(URI.from({ scheme: 'http', authority: 'www.MSFT.com', path: '/my/path' }).toString(), 'http://www.msft.com/my/path');
assert.equal(URI.from({ scheme: 'http', authority: '', path: 'my/path' }).toString(), 'http:/my/path');
assert.equal(URI.from({ scheme: 'http', authority: '', path: '/my/path' }).toString(), 'http:/my/path');
//http://a-test-site.com/#test=true
assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(), 'http://a-test-site.com/?test%3Dtrue');
assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(), 'http://a-test-site.com/#test%3Dtrue');
});
test('http#toString, encode=FALSE', () => {
assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: 'test=true' }).toString(true), 'http://a-test-site.com/?test=true');
assert.equal(URI.from({ scheme: 'http', authority: 'a-test-site.com', path: '/', query: '', fragment: 'test=true' }).toString(true), 'http://a-test-site.com/#test=true');
assert.equal(URI.from({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(true), 'http:/api/files/test.me?t=1234');
const value = URI.parse('file://shares/pröjects/c%23/#l12');
assert.equal(value.authority, 'shares');
assert.equal(value.path, '/pröjects/c#/');
assert.equal(value.fragment, 'l12');
assert.equal(value.toString(), 'file://shares/pr%C3%B6jects/c%23/#l12');
assert.equal(value.toString(true), 'file://shares/pröjects/c%23/#l12');
const uri2 = URI.parse(value.toString(true));
const uri3 = URI.parse(value.toString());
assert.equal(uri2.authority, uri3.authority);
assert.equal(uri2.path, uri3.path);
assert.equal(uri2.query, uri3.query);
assert.equal(uri2.fragment, uri3.fragment);
});
test('with, identity', () => {
let uri = URI.parse('foo:bar/path');
let uri2 = uri.with(null!);
assert.ok(uri === uri2);
uri2 = uri.with(undefined!);
assert.ok(uri === uri2);
uri2 = uri.with({});
assert.ok(uri === uri2);
uri2 = uri.with({ scheme: 'foo', path: 'bar/path' });
assert.ok(uri === uri2);
});
test('with, changes', () => {
assert.equal(URI.parse('before:some/file/path').with({ scheme: 'after' }).toString(), 'after:some/file/path');
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', path: '/api/files/test.me', query: 't=1234' }).toString(), 'http:/api/files/test.me?t%3D1234');
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'http', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'http:/api/files/test.me?t%3D1234');
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'https', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'https:/api/files/test.me?t%3D1234');
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTP', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTP:/api/files/test.me?t%3D1234');
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'HTTPS', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'HTTPS:/api/files/test.me?t%3D1234');
assert.equal(URI.from({ scheme: 's' }).with({ scheme: 'boo', authority: '', path: '/api/files/test.me', query: 't=1234', fragment: '' }).toString(), 'boo:/api/files/test.me?t%3D1234');
});
test('with, remove components #8465', () => {
assert.equal(URI.parse('scheme://authority/path').with({ authority: '' }).toString(), 'scheme:/path');
assert.equal(URI.parse('scheme:/path').with({ authority: 'authority' }).with({ authority: '' }).toString(), 'scheme:/path');
assert.equal(URI.parse('scheme:/path').with({ authority: 'authority' }).with({ authority: null }).toString(), 'scheme:/path');
assert.equal(URI.parse('scheme:/path').with({ authority: 'authority' }).with({ path: '' }).toString(), 'scheme://authority');
assert.equal(URI.parse('scheme:/path').with({ authority: 'authority' }).with({ path: null }).toString(), 'scheme://authority');
assert.equal(URI.parse('scheme:/path').with({ authority: '' }).toString(), 'scheme:/path');
assert.equal(URI.parse('scheme:/path').with({ authority: null }).toString(), 'scheme:/path');
});
test('with, validation', () => {
let uri = URI.parse('foo:bar/path');
assert.throws(() => uri.with({ scheme: 'fai:l' }));
assert.throws(() => uri.with({ scheme: 'fäil' }));
assert.throws(() => uri.with({ authority: 'fail' }));
assert.throws(() => uri.with({ path: '//fail' }));
});
test('parse', () => {
let value = URI.parse('http:/api/files/test.me?t=1234');
assert.equal(value.scheme, 'http');
assert.equal(value.authority, '');
assert.equal(value.path, '/api/files/test.me');
assert.equal(value.query, 't=1234');
assert.equal(value.fragment, '');
value = URI.parse('http://api/files/test.me?t=1234');
assert.equal(value.scheme, 'http');
assert.equal(value.authority, 'api');
assert.equal(value.path, '/files/test.me');
assert.equal(value.query, 't=1234');
assert.equal(value.fragment, '');
value = URI.parse('file:///c:/test/me');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/c:/test/me');
assert.equal(value.fragment, '');
assert.equal(value.query, '');
assert.equal(value.fsPath, isWindows ? 'c:\\test\\me' : 'c:/test/me');
value = URI.parse('file://shares/files/c%23/p.cs');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, 'shares');
assert.equal(value.path, '/files/c#/p.cs');
assert.equal(value.fragment, '');
assert.equal(value.query, '');
assert.equal(value.fsPath, isWindows ? '\\\\shares\\files\\c#\\p.cs' : '//shares/files/c#/p.cs');
value = URI.parse('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins/c%23/plugin.json');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/c:/Source/Zürich or Zurich (ˈzjʊərɪk,/Code/resources/app/plugins/c#/plugin.json');
assert.equal(value.fragment, '');
assert.equal(value.query, '');
value = URI.parse('file:///c:/test %25/path');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/c:/test %/path');
assert.equal(value.fragment, '');
assert.equal(value.query, '');
value = URI.parse('inmemory:');
assert.equal(value.scheme, 'inmemory');
assert.equal(value.authority, '');
assert.equal(value.path, '');
assert.equal(value.query, '');
assert.equal(value.fragment, '');
value = URI.parse('foo:api/files/test');
assert.equal(value.scheme, 'foo');
assert.equal(value.authority, '');
assert.equal(value.path, 'api/files/test');
assert.equal(value.query, '');
assert.equal(value.fragment, '');
value = URI.parse('file:?q');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/');
assert.equal(value.query, 'q');
assert.equal(value.fragment, '');
value = URI.parse('file:#d');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/');
assert.equal(value.query, '');
assert.equal(value.fragment, 'd');
value = URI.parse('f3ile:#d');
assert.equal(value.scheme, 'f3ile');
assert.equal(value.authority, '');
assert.equal(value.path, '');
assert.equal(value.query, '');
assert.equal(value.fragment, 'd');
value = URI.parse('foo+bar:path');
assert.equal(value.scheme, 'foo+bar');
assert.equal(value.authority, '');
assert.equal(value.path, 'path');
assert.equal(value.query, '');
assert.equal(value.fragment, '');
value = URI.parse('foo-bar:path');
assert.equal(value.scheme, 'foo-bar');
assert.equal(value.authority, '');
assert.equal(value.path, 'path');
assert.equal(value.query, '');
assert.equal(value.fragment, '');
value = URI.parse('foo.bar:path');
assert.equal(value.scheme, 'foo.bar');
assert.equal(value.authority, '');
assert.equal(value.path, 'path');
assert.equal(value.query, '');
assert.equal(value.fragment, '');
});
test('parse, disallow //path when no authority', () => {
assert.throws(() => URI.parse('file:////shares/files/p.cs'));
});
test('URI#file, win-speciale', () => {
if (isWindows) {
let value = URI.file('c:\\test\\drive');
assert.equal(value.path, '/c:/test/drive');
assert.equal(value.toString(), 'file:///c%3A/test/drive');
value = URI.file('\\\\shäres\\path\\c#\\plugin.json');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, 'shäres');
assert.equal(value.path, '/path/c#/plugin.json');
assert.equal(value.fragment, '');
assert.equal(value.query, '');
assert.equal(value.toString(), 'file://sh%C3%A4res/path/c%23/plugin.json');
value = URI.file('\\\\localhost\\c$\\GitDevelopment\\express');
assert.equal(value.scheme, 'file');
assert.equal(value.path, '/c$/GitDevelopment/express');
assert.equal(value.fsPath, '\\\\localhost\\c$\\GitDevelopment\\express');
assert.equal(value.query, '');
assert.equal(value.fragment, '');
assert.equal(value.toString(), 'file://localhost/c%24/GitDevelopment/express');
value = URI.file('c:\\test with %\\path');
assert.equal(value.path, '/c:/test with %/path');
assert.equal(value.toString(), 'file:///c%3A/test%20with%20%25/path');
value = URI.file('c:\\test with %25\\path');
assert.equal(value.path, '/c:/test with %25/path');
assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/path');
value = URI.file('c:\\test with %25\\c#code');
assert.equal(value.path, '/c:/test with %25/c#code');
assert.equal(value.toString(), 'file:///c%3A/test%20with%20%2525/c%23code');
value = URI.file('\\\\shares');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, 'shares');
assert.equal(value.path, '/'); // slash is always there
value = URI.file('\\\\shares\\');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, 'shares');
assert.equal(value.path, '/');
}
});
test('VSCode URI module\'s driveLetterPath regex is incorrect, #32961', function () {
let uri = URI.parse('file:///_:/path');
assert.equal(uri.fsPath, isWindows ? '\\_:\\path' : '/_:/path');
});
test('URI#file, no path-is-uri check', () => {
// we don't complain here
let value = URI.file('file://path/to/file');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/file://path/to/file');
});
test('URI#file, always slash', () => {
let value = URI.file('a.file');
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/a.file');
assert.equal(value.toString(), 'file:///a.file');
value = URI.parse(value.toString());
assert.equal(value.scheme, 'file');
assert.equal(value.authority, '');
assert.equal(value.path, '/a.file');
assert.equal(value.toString(), 'file:///a.file');
});
test('URI.toString, only scheme and query', () => {
const value = URI.parse('stuff:?qüery');
assert.equal(value.toString(), 'stuff:?q%C3%BCery');
});
test('URI#toString, upper-case percent espaces', () => {
const value = URI.parse('file://sh%c3%a4res/path');
assert.equal(value.toString(), 'file://sh%C3%A4res/path');
});
test('URI#toString, lower-case windows drive letter', () => {
assert.equal(URI.parse('untitled:c:/Users/jrieken/Code/abc.txt').toString(), 'untitled:c%3A/Users/jrieken/Code/abc.txt');
assert.equal(URI.parse('untitled:C:/Users/jrieken/Code/abc.txt').toString(), 'untitled:c%3A/Users/jrieken/Code/abc.txt');
});
test('URI#toString, escape all the bits', () => {
const value = URI.file('/Users/jrieken/Code/_samples/18500/Mödel + Other Thîngß/model.js');
assert.equal(value.toString(), 'file:///Users/jrieken/Code/_samples/18500/M%C3%B6del%20%2B%20Other%20Th%C3%AEng%C3%9F/model.js');
});
test('URI#toString, don\'t encode port', () => {
let value = URI.parse('http://localhost:8080/far');
assert.equal(value.toString(), 'http://localhost:8080/far');
value = URI.from({ scheme: 'http', authority: 'löcalhost:8080', path: '/far', query: undefined, fragment: undefined });
assert.equal(value.toString(), 'http://l%C3%B6calhost:8080/far');
});
test('URI#toString, user information in authority', () => {
let value = URI.parse('http://foo:bar@localhost/far');
assert.equal(value.toString(), 'http://foo:bar@localhost/far');
value = URI.parse('http://foo@localhost/far');
assert.equal(value.toString(), 'http://foo@localhost/far');
value = URI.parse('http://foo:bAr@localhost:8080/far');
assert.equal(value.toString(), 'http://foo:bAr@localhost:8080/far');
value = URI.parse('http://foo@localhost:8080/far');
assert.equal(value.toString(), 'http://foo@localhost:8080/far');
value = URI.from({ scheme: 'http', authority: 'föö:bör@löcalhost:8080', path: '/far', query: undefined, fragment: undefined });
assert.equal(value.toString(), 'http://f%C3%B6%C3%B6:b%C3%B6r@l%C3%B6calhost:8080/far');
});
test('correctFileUriToFilePath2', () => {
const test = (input: string, expected: string) => {
const value = URI.parse(input);
assert.equal(value.fsPath, expected, 'Result for ' + input);
const value2 = URI.file(value.fsPath);
assert.equal(value2.fsPath, expected, 'Result for ' + input);
assert.equal(value.toString(), value2.toString());
};
test('file:///c:/alex.txt', isWindows ? 'c:\\alex.txt' : 'c:/alex.txt');
test('file:///c:/Source/Z%C3%BCrich%20or%20Zurich%20(%CB%88zj%CA%8A%C9%99r%C9%AAk,/Code/resources/app/plugins', isWindows ? 'c:\\Source\\Zürich or Zurich (ˈzjʊərɪk,\\Code\\resources\\app\\plugins' : 'c:/Source/Zürich or Zurich (ˈzjʊərɪk,/Code/resources/app/plugins');
test('file://monacotools/folder/isi.txt', isWindows ? '\\\\monacotools\\folder\\isi.txt' : '//monacotools/folder/isi.txt');
test('file://monacotools1/certificates/SSL/', isWindows ? '\\\\monacotools1\\certificates\\SSL\\' : '//monacotools1/certificates/SSL/');
});
test('URI - http, query & toString', function () {
let uri = URI.parse('https://go.microsoft.com/fwlink/?LinkId=518008');
assert.equal(uri.query, 'LinkId=518008');
assert.equal(uri.toString(true), 'https://go.microsoft.com/fwlink/?LinkId=518008');
assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId%3D518008');
let uri2 = URI.parse(uri.toString());
assert.equal(uri2.query, 'LinkId=518008');
assert.equal(uri2.query, uri.query);
uri = URI.parse('https://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü');
assert.equal(uri.query, 'LinkId=518008&foö&ké¥=üü');
assert.equal(uri.toString(true), 'https://go.microsoft.com/fwlink/?LinkId=518008&foö&ké¥=üü');
assert.equal(uri.toString(), 'https://go.microsoft.com/fwlink/?LinkId%3D518008%26fo%C3%B6%26k%C3%A9%C2%A5%3D%C3%BC%C3%BC');
uri2 = URI.parse(uri.toString());
assert.equal(uri2.query, 'LinkId=518008&foö&ké¥=üü');
assert.equal(uri2.query, uri.query);
// #24849
uri = URI.parse('https://twitter.com/search?src=typd&q=%23tag');
assert.equal(uri.toString(true), 'https://twitter.com/search?src=typd&q=%23tag');
});
test('class URI cannot represent relative file paths #34449', function () {
let path = '/foo/bar';
assert.equal(URI.file(path).path, path);
path = 'foo/bar';
assert.equal(URI.file(path).path, '/foo/bar');
path = './foo/bar';
assert.equal(URI.file(path).path, '/./foo/bar'); // missing normalization
const fileUri1 = URI.parse(`file:foo/bar`);
assert.equal(fileUri1.path, '/foo/bar');
assert.equal(fileUri1.authority, '');
const uri = fileUri1.toString();
assert.equal(uri, 'file:///foo/bar');
const fileUri2 = URI.parse(uri);
assert.equal(fileUri2.path, '/foo/bar');
assert.equal(fileUri2.authority, '');
});
test('Ctrl click to follow hash query param url gets urlencoded #49628', function () {
let input = 'http://localhost:3000/#/foo?bar=baz';
let uri = URI.parse(input);
assert.equal(uri.toString(true), input);
input = 'http://localhost:3000/foo?bar=baz';
uri = URI.parse(input);
assert.equal(uri.toString(true), input);
});
test('Unable to open \'%A0.txt\': URI malformed #76506', function () {
let uri = URI.file('/foo/%A0.txt');
let uri2 = URI.parse(uri.toString());
assert.equal(uri.scheme, uri2.scheme);
assert.equal(uri.path, uri2.path);
uri = URI.file('/foo/%2e.txt');
uri2 = URI.parse(uri.toString());
assert.equal(uri.scheme, uri2.scheme);
assert.equal(uri.path, uri2.path);
});
test('Unable to open \'%A0.txt\': URI malformed #76506', function () {
assert.equal(URI.parse('file://some/%.txt'), 'file://some/%25.txt');
assert.equal(URI.parse('file://some/%A0.txt'), 'file://some/%25A0.txt');
});
test('Links in markdown are broken if url contains encoded parameters #79474', function () {
this.skip();
let strIn = 'https://myhost.com/Redirect?url=http%3A%2F%2Fwww.bing.com%3Fsearch%3Dtom';
let uri1 = URI.parse(strIn);
let strOut = uri1.toString();
let uri2 = URI.parse(strOut);
assert.equal(uri1.scheme, uri2.scheme);
assert.equal(uri1.authority, uri2.authority);
assert.equal(uri1.path, uri2.path);
assert.equal(uri1.query, uri2.query);
assert.equal(uri1.fragment, uri2.fragment);
assert.equal(strIn, strOut); // fails here!!
});
test('Uri#parse can break path-component #45515', function () {
this.skip();
let strIn = 'https://firebasestorage.googleapis.com/v0/b/brewlangerie.appspot.com/o/products%2FzVNZkudXJyq8bPGTXUxx%2FBetterave-Sesame.jpg?alt=media&token=0b2310c4-3ea6-4207-bbde-9c3710ba0437';
let uri1 = URI.parse(strIn);
let strOut = uri1.toString();
let uri2 = URI.parse(strOut);
assert.equal(uri1.scheme, uri2.scheme);
assert.equal(uri1.authority, uri2.authority);
assert.equal(uri1.path, uri2.path);
assert.equal(uri1.query, uri2.query);
assert.equal(uri1.fragment, uri2.fragment);
assert.equal(strIn, strOut); // fails here!!
});
test('URI - (de)serialize', function () {
const values = [
URI.parse('http://localhost:8080/far'),
URI.file('c:\\test with %25\\c#code'),
URI.file('\\\\shäres\\path\\c#\\plugin.json'),
URI.parse('http://api/files/test.me?t=1234'),
URI.parse('http://api/files/test.me?t=1234#fff'),
URI.parse('http://api/files/test.me#fff'),
];
// console.profile();
// let c = 100000;
// while (c-- > 0) {
for (let value of values) {
let data = value.toJSON() as UriComponents;
let clone = URI.revive(data);
assert.equal(clone.scheme, value.scheme);
assert.equal(clone.authority, value.authority);
assert.equal(clone.path, value.path);
assert.equal(clone.query, value.query);
assert.equal(clone.fragment, value.fragment);
assert.equal(clone.fsPath, value.fsPath);
assert.equal(clone.toString(), value.toString());
}
// }
// console.profileEnd();
});
function assertJoined(base: string, fragment: string, expected: string, checkWithUrl: boolean = true) {
const baseUri = URI.parse(base);
const newUri = URI.joinPath(baseUri, fragment);
const actual = newUri.toString(true);
assert.equal(actual, expected);
if (checkWithUrl) {
const actualUrl = new URL(fragment, base).href;
assert.equal(actualUrl, expected, 'DIFFERENT from URL');
}
}
test('URI#joinPath', function () {
assertJoined(('file:///foo/'), '../../bazz', 'file:///bazz');
assertJoined(('file:///foo'), '../../bazz', 'file:///bazz');
assertJoined(('file:///foo'), '../../bazz', 'file:///bazz');
assertJoined(('file:///foo/bar/'), './bazz', 'file:///foo/bar/bazz');
assertJoined(('file:///foo/bar'), './bazz', 'file:///foo/bar/bazz', false);
assertJoined(('file:///foo/bar'), 'bazz', 'file:///foo/bar/bazz', false);
// "auto-path" scheme
assertJoined(('file:'), 'bazz', 'file:///bazz');
assertJoined(('http://domain'), 'bazz', 'http://domain/bazz');
assertJoined(('https://domain'), 'bazz', 'https://domain/bazz');
assertJoined(('http:'), 'bazz', 'http:/bazz', false);
assertJoined(('https:'), 'bazz', 'https:/bazz', false);
// no "auto-path" scheme with and w/o paths
assertJoined(('foo:/'), 'bazz', 'foo:/bazz');
assertJoined(('foo://bar/'), 'bazz', 'foo://bar/bazz');
// no "auto-path" + no path -> error
assert.throws(() => assertJoined(('foo:'), 'bazz', ''));
assert.throws(() => new URL('bazz', 'foo:'));
assert.throws(() => assertJoined(('foo://bar'), 'bazz', ''));
// assert.throws(() => new URL('bazz', 'foo://bar')); Edge, Chrome => THROW, Firefox, Safari => foo://bar/bazz
});
test('URI#joinPath (posix)', function () {
if (isWindows) {
this.skip();
}
assertJoined(('file:///c:/foo/'), '../../bazz', 'file:///bazz', false);
assertJoined(('file://server/share/c:/'), '../../bazz', 'file://server/bazz', false);
assertJoined(('file://server/share/c:'), '../../bazz', 'file://server/bazz', false);
assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/bazz', false); // Firefox -> Different, Edge, Chrome, Safar -> OK
assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/bazz', false); // Firefox -> Different, Edge, Chrome, Safar -> OK
});
test('URI#joinPath (windows)', function () {
if (!isWindows) {
this.skip();
}
assertJoined(('file:///c:/foo/'), '../../bazz', 'file:///c:/bazz', false);
assertJoined(('file://server/share/c:/'), '../../bazz', 'file://server/share/bazz', false);
assertJoined(('file://server/share/c:'), '../../bazz', 'file://server/share/bazz', false);
assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/foo/bazz', false);
assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/foo/bazz', false);
//https://github.com/microsoft/vscode/issues/93831
assertJoined('file:///c:/foo/bar', './other/foo.img', 'file:///c:/foo/bar/other/foo.img', false);
});
});

View File

@@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { canceled } from 'vs/base/common/errors';
import { isWindows } from 'vs/base/common/platform';
export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
export class DeferredPromise<T> {
private completeCallback!: ValueCallback<T>;
private errorCallback!: (err: any) => void;
public p: Promise<any>;
constructor() {
this.p = new Promise<any>((c, e) => {
this.completeCallback = c;
this.errorCallback = e;
});
}
public complete(value: T) {
return new Promise<void>(resolve => {
this.completeCallback(value);
resolve();
});
}
public error(err: any) {
return new Promise<void>(resolve => {
this.errorCallback(err);
resolve();
});
}
public cancel() {
new Promise<void>(resolve => {
this.errorCallback(canceled());
resolve();
});
}
}
export function toResource(this: any, path: string) {
if (isWindows) {
return URI.file(join('C:\\', btoa(this.test.fullTitle()), path));
}
return URI.file(join('/', btoa(this.test.fullTitle()), path));
}
export function suiteRepeat(n: number, description: string, callback: (this: any) => void): void {
for (let i = 0; i < n; i++) {
suite(`${description} (iteration ${i})`, callback);
}
}
export function testRepeat(n: number, description: string, callback: (this: any, done: MochaDone) => any): void {
for (let i = 0; i < n; i++) {
test(`${description} (iteration ${i})`, callback);
}
}

View File

@@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* 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 * as uuid from 'vs/base/common/uuid';
suite('UUID', () => {
test('generation', () => {
const asHex = uuid.generateUuid();
assert.equal(asHex.length, 36);
assert.equal(asHex[14], '4');
assert.ok(asHex[19] === '8' || asHex[19] === '9' || asHex[19] === 'a' || asHex[19] === 'b');
});
test('self-check', function () {
const t1 = Date.now();
while (Date.now() - t1 < 50) {
const value = uuid.generateUuid();
assert.ok(uuid.isUUID(value));
}
});
});

View File

@@ -0,0 +1,413 @@
/*---------------------------------------------------------------------------------------------
* 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 { VSBuffer, bufferToReadable, readableToBuffer, bufferToStream, streamToBuffer, newWriteableBufferStream, bufferedStreamToBuffer } from 'vs/base/common/buffer';
import { timeout } from 'vs/base/common/async';
import { peekStream } from 'vs/base/common/stream';
suite('Buffer', () => {
test('issue #71993 - VSBuffer#toString returns numbers', () => {
const data = new Uint8Array([1, 2, 3, 'h'.charCodeAt(0), 'i'.charCodeAt(0), 4, 5]).buffer;
const buffer = VSBuffer.wrap(new Uint8Array(data, 3, 2));
assert.deepEqual(buffer.toString(), 'hi');
});
test('bufferToReadable / readableToBuffer', () => {
const content = 'Hello World';
const readable = bufferToReadable(VSBuffer.fromString(content));
assert.equal(readableToBuffer(readable).toString(), content);
});
test('bufferToStream / streamToBuffer', async () => {
const content = 'Hello World';
const stream = bufferToStream(VSBuffer.fromString(content));
assert.equal((await streamToBuffer(stream)).toString(), content);
});
test('bufferedStreamToBuffer', async () => {
const content = 'Hello World';
const stream = await peekStream(bufferToStream(VSBuffer.fromString(content)), 1);
assert.equal((await bufferedStreamToBuffer(stream)).toString(), content);
});
test('bufferWriteableStream - basics (no error)', async () => {
const stream = newWriteableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 2);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(chunks[1].toString(), 'World');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - basics (error)', async () => {
const stream = newWriteableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(new Error());
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(ended, true);
assert.equal(errors.length, 1);
});
test('bufferWriteableStream - buffers data when no listener', async () => {
const stream = newWriteableBufferStream();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'HelloWorld');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - buffers errors when no listener', async () => {
const stream = newWriteableBufferStream();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.error(new Error());
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
stream.end();
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(ended, true);
assert.equal(errors.length, 1);
});
test('bufferWriteableStream - buffers end when no listener', async () => {
const stream = newWriteableBufferStream();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
let ended = false;
stream.on('end', () => {
ended = true;
});
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'HelloWorld');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - nothing happens after end()', async () => {
const stream = newWriteableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
let dataCalledAfterEnd = false;
stream.on('data', data => {
dataCalledAfterEnd = true;
});
let errorCalledAfterEnd = false;
stream.on('error', error => {
errorCalledAfterEnd = true;
});
let endCalledAfterEnd = false;
stream.on('end', () => {
endCalledAfterEnd = true;
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.error(new Error());
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(dataCalledAfterEnd, false);
assert.equal(errorCalledAfterEnd, false);
assert.equal(endCalledAfterEnd, false);
assert.equal(chunks.length, 2);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(chunks[1].toString(), 'World');
});
test('bufferWriteableStream - pause/resume (simple)', async () => {
const stream = newWriteableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
stream.pause();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 0);
assert.equal(errors.length, 0);
assert.equal(ended, false);
stream.resume();
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'HelloWorld');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - pause/resume (pause after first write)', async () => {
const stream = newWriteableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
stream.pause();
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(errors.length, 0);
assert.equal(ended, false);
stream.resume();
assert.equal(chunks.length, 2);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(chunks[1].toString(), 'World');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - pause/resume (error)', async () => {
const stream = newWriteableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
stream.pause();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(new Error());
assert.equal(chunks.length, 0);
assert.equal(ended, false);
assert.equal(errors.length, 0);
stream.resume();
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(ended, true);
assert.equal(errors.length, 1);
});
test('bufferWriteableStream - destroy', async () => {
const stream = newWriteableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
stream.destroy();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 0);
assert.equal(ended, false);
assert.equal(errors.length, 0);
});
test('Performance issue with VSBuffer#slice #76076', function () {
// Buffer#slice creates a view
{
const buff = Buffer.from([10, 20, 30, 40]);
const b2 = buff.slice(1, 3);
assert.equal(buff[1], 20);
assert.equal(b2[0], 20);
buff[1] = 17; // modify buff AND b2
assert.equal(buff[1], 17);
assert.equal(b2[0], 17);
}
// TypedArray#slice creates a copy
{
const unit = new Uint8Array([10, 20, 30, 40]);
const u2 = unit.slice(1, 3);
assert.equal(unit[1], 20);
assert.equal(u2[0], 20);
unit[1] = 17; // modify unit, NOT b2
assert.equal(unit[1], 17);
assert.equal(u2[0], 20);
}
// TypedArray#subarray creates a view
{
const unit = new Uint8Array([10, 20, 30, 40]);
const u2 = unit.subarray(1, 3);
assert.equal(unit[1], 20);
assert.equal(u2[0], 20);
unit[1] = 17; // modify unit AND b2
assert.equal(unit[1], 17);
assert.equal(u2[0], 17);
}
});
});

View File

@@ -0,0 +1,48 @@
/*---------------------------------------------------------------------------------------------
* 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 { getFirstFrame } from 'vs/base/common/console';
import { normalize } from 'vs/base/common/path';
suite('Console', () => {
test('getFirstFrame', () => {
let stack = 'at vscode.commands.registerCommand (/Users/someone/Desktop/test-ts/out/src/extension.js:18:17)';
let frame = getFirstFrame(stack)!;
assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js'));
assert.equal(frame.line, 18);
assert.equal(frame.column, 17);
stack = 'at /Users/someone/Desktop/test-ts/out/src/extension.js:18:17';
frame = getFirstFrame(stack)!;
assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js'));
assert.equal(frame.line, 18);
assert.equal(frame.column, 17);
stack = 'at c:\\Users\\someone\\Desktop\\end-js\\extension.js:18:17';
frame = getFirstFrame(stack)!;
assert.equal(frame.uri.fsPath, 'c:\\Users\\someone\\Desktop\\end-js\\extension.js');
assert.equal(frame.line, 18);
assert.equal(frame.column, 17);
stack = 'at e.$executeContributedCommand(c:\\Users\\someone\\Desktop\\end-js\\extension.js:18:17)';
frame = getFirstFrame(stack)!;
assert.equal(frame.uri.fsPath, 'c:\\Users\\someone\\Desktop\\end-js\\extension.js');
assert.equal(frame.line, 18);
assert.equal(frame.column, 17);
stack = 'at /Users/someone/Desktop/test-ts/out/src/extension.js:18:17\nat /Users/someone/Desktop/test-ts/out/src/other.js:28:27\nat /Users/someone/Desktop/test-ts/out/src/more.js:38:37';
frame = getFirstFrame(stack)!;
assert.equal(frame.uri.fsPath, normalize('/Users/someone/Desktop/test-ts/out/src/extension.js'));
assert.equal(frame.line, 18);
assert.equal(frame.column, 17);
});
});

View File

@@ -0,0 +1,27 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { checksum } from 'vs/base/node/crypto';
import { generateUuid } from 'vs/base/common/uuid';
import { join } from 'vs/base/common/path';
import { tmpdir } from 'os';
import { mkdirp, rimraf, RimRafMode, writeFile } from 'vs/base/node/pfs';
suite('Crypto', () => {
test('checksum', async () => {
const id = generateUuid();
const testDir = join(tmpdir(), 'vsctests', id);
const testFile = join(testDir, 'checksum.txt');
await mkdirp(testDir);
await writeFile(testFile, 'Hello World');
await checksum(testFile, '0a4d55a8d778e5022fab701977c5d840bbc486d0');
await rimraf(testDir, RimRafMode.MOVE);
});
});

View File

@@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* 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 * as decoder from 'vs/base/node/decoder';
suite('Decoder', () => {
test('decoding', () => {
const lineDecoder = new decoder.LineDecoder();
let res = lineDecoder.write(Buffer.from('hello'));
assert.equal(res.length, 0);
res = lineDecoder.write(Buffer.from('\nworld'));
assert.equal(res[0], 'hello');
assert.equal(res.length, 1);
assert.equal(lineDecoder.end(), 'world');
});
});

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* 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 * as os from 'os';
import * as path from 'vs/base/common/path';
import * as uuid from 'vs/base/common/uuid';
import * as pfs from 'vs/base/node/pfs';
import { realcaseSync, realpath, realpathSync } from 'vs/base/node/extpath';
suite('Extpath', () => {
test('realcase', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'extpath', id);
await pfs.mkdirp(newDir, 493);
// assume case insensitive file system
if (process.platform === 'win32' || process.platform === 'darwin') {
const upper = newDir.toUpperCase();
const real = realcaseSync(upper);
if (real) { // can be null in case of permission errors
assert.notEqual(real, upper);
assert.equal(real.toUpperCase(), upper);
assert.equal(real, newDir);
}
}
// linux, unix, etc. -> assume case sensitive file system
else {
const real = realcaseSync(newDir);
assert.equal(real, newDir);
}
await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
});
test('realpath', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'extpath', id);
await pfs.mkdirp(newDir, 493);
const realpathVal = await realpath(newDir);
assert.ok(realpathVal);
await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
});
test('realpathSync', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'extpath', id);
await pfs.mkdirp(newDir, 493);
let realpath!: string;
try {
realpath = realpathSync(newDir);
} catch (error) {
assert.ok(!error);
}
assert.ok(realpath!);
await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* 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 { getMachineId } from 'vs/base/node/id';
import { getMac } from 'vs/base/node/macAddress';
suite('ID', () => {
test('getMachineId', function () {
this.timeout(20000);
return getMachineId().then(id => {
assert.ok(id);
});
});
test('getMac', () => {
return getMac().then(macAddress => {
assert.ok(/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/.test(macAddress), `Expected a MAC address, got: ${macAddress}`);
});
});
});

View File

@@ -0,0 +1,37 @@
/*---------------------------------------------------------------------------------------------
* 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 * as platform from 'vs/base/common/platform';
suite('Keytar', () => {
test('loads and is functional', function (done) {
if (platform.isLinux) {
// Skip test due to set up issue with Travis.
this.skip();
return;
}
(async () => {
const keytar = await import('keytar');
const name = `VSCode Test ${Math.floor(Math.random() * 1e9)}`;
try {
await keytar.setPassword(name, 'foo', 'bar');
assert.equal(await keytar.findPassword(name), 'bar');
assert.equal((await keytar.findCredentials(name)).length, 1);
assert.equal(await keytar.getPassword(name, 'foo'), 'bar');
await keytar.deletePassword(name, 'foo');
assert.equal(await keytar.getPassword(name, 'foo'), undefined);
} catch (err) {
// try to clean up
try {
await keytar.deletePassword(name, 'foo');
} finally {
// eslint-disable-next-line no-unsafe-finally
throw err;
}
}
})().then(done, done);
});
});

View File

@@ -0,0 +1,819 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace
// Copied from: https://github.com/nodejs/node/tree/43dd49c9782848c25e5b03448c8a0f923f13c158
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
import * as assert from 'assert';
import * as path from 'vs/base/common/path';
import { isWindows } from 'vs/base/common/platform';
import * as process from 'vs/base/common/process';
suite('Paths (Node Implementation)', () => {
test('join', () => {
const failures = [] as string[];
const backslashRE = /\\/g;
const joinTests: any = [
[[path.posix.join, path.win32.join],
// arguments result
[[['.', 'x/b', '..', '/b/c.js'], 'x/b/c.js'],
[[], '.'],
[['/.', 'x/b', '..', '/b/c.js'], '/x/b/c.js'],
[['/foo', '../../../bar'], '/bar'],
[['foo', '../../../bar'], '../../bar'],
[['foo/', '../../../bar'], '../../bar'],
[['foo/x', '../../../bar'], '../bar'],
[['foo/x', './bar'], 'foo/x/bar'],
[['foo/x/', './bar'], 'foo/x/bar'],
[['foo/x/', '.', 'bar'], 'foo/x/bar'],
[['./'], './'],
[['.', './'], './'],
[['.', '.', '.'], '.'],
[['.', './', '.'], '.'],
[['.', '/./', '.'], '.'],
[['.', '/////./', '.'], '.'],
[['.'], '.'],
[['', '.'], '.'],
[['', 'foo'], 'foo'],
[['foo', '/bar'], 'foo/bar'],
[['', '/foo'], '/foo'],
[['', '', '/foo'], '/foo'],
[['', '', 'foo'], 'foo'],
[['foo', ''], 'foo'],
[['foo/', ''], 'foo/'],
[['foo', '', '/bar'], 'foo/bar'],
[['./', '..', '/foo'], '../foo'],
[['./', '..', '..', '/foo'], '../../foo'],
[['.', '..', '..', '/foo'], '../../foo'],
[['', '..', '..', '/foo'], '../../foo'],
[['/'], '/'],
[['/', '.'], '/'],
[['/', '..'], '/'],
[['/', '..', '..'], '/'],
[[''], '.'],
[['', ''], '.'],
[[' /foo'], ' /foo'],
[[' ', 'foo'], ' /foo'],
[[' ', '.'], ' '],
[[' ', '/'], ' /'],
[[' ', ''], ' '],
[['/', 'foo'], '/foo'],
[['/', '/foo'], '/foo'],
[['/', '//foo'], '/foo'],
[['/', '', '/foo'], '/foo'],
[['', '/', 'foo'], '/foo'],
[['', '/', '/foo'], '/foo']
]
]
];
// Windows-specific join tests
joinTests.push([
path.win32.join,
joinTests[0][1].slice(0).concat(
[// arguments result
// UNC path expected
[['//foo/bar'], '\\\\foo\\bar\\'],
[['\\/foo/bar'], '\\\\foo\\bar\\'],
[['\\\\foo/bar'], '\\\\foo\\bar\\'],
// UNC path expected - server and share separate
[['//foo', 'bar'], '\\\\foo\\bar\\'],
[['//foo/', 'bar'], '\\\\foo\\bar\\'],
[['//foo', '/bar'], '\\\\foo\\bar\\'],
// UNC path expected - questionable
[['//foo', '', 'bar'], '\\\\foo\\bar\\'],
[['//foo/', '', 'bar'], '\\\\foo\\bar\\'],
[['//foo/', '', '/bar'], '\\\\foo\\bar\\'],
// UNC path expected - even more questionable
[['', '//foo', 'bar'], '\\\\foo\\bar\\'],
[['', '//foo/', 'bar'], '\\\\foo\\bar\\'],
[['', '//foo/', '/bar'], '\\\\foo\\bar\\'],
// No UNC path expected (no double slash in first component)
[['\\', 'foo/bar'], '\\foo\\bar'],
[['\\', '/foo/bar'], '\\foo\\bar'],
[['', '/', '/foo/bar'], '\\foo\\bar'],
// No UNC path expected (no non-slashes in first component -
// questionable)
[['//', 'foo/bar'], '\\foo\\bar'],
[['//', '/foo/bar'], '\\foo\\bar'],
[['\\\\', '/', '/foo/bar'], '\\foo\\bar'],
[['//'], '\\'],
// No UNC path expected (share name missing - questionable).
[['//foo'], '\\foo'],
[['//foo/'], '\\foo\\'],
[['//foo', '/'], '\\foo\\'],
[['//foo', '', '/'], '\\foo\\'],
// No UNC path expected (too many leading slashes - questionable)
[['///foo/bar'], '\\foo\\bar'],
[['////foo', 'bar'], '\\foo\\bar'],
[['\\\\\\/foo/bar'], '\\foo\\bar'],
// Drive-relative vs drive-absolute paths. This merely describes the
// status quo, rather than being obviously right
[['c:'], 'c:.'],
[['c:.'], 'c:.'],
[['c:', ''], 'c:.'],
[['', 'c:'], 'c:.'],
[['c:.', '/'], 'c:.\\'],
[['c:.', 'file'], 'c:file'],
[['c:', '/'], 'c:\\'],
[['c:', 'file'], 'c:\\file']
]
)
]);
joinTests.forEach((test: any[]) => {
if (!Array.isArray(test[0])) {
test[0] = [test[0]];
}
test[0].forEach((join: any) => {
test[1].forEach((test: any) => {
const actual = join.apply(null, test[0]);
const expected = test[1];
// For non-Windows specific tests with the Windows join(), we need to try
// replacing the slashes since the non-Windows specific tests' `expected`
// use forward slashes
let actualAlt;
let os;
if (join === path.win32.join) {
actualAlt = actual.replace(backslashRE, '/');
os = 'win32';
} else {
os = 'posix';
}
const message =
`path.${os}.join(${test[0].map(JSON.stringify).join(',')})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
if (actual !== expected && actualAlt !== expected) {
failures.push(`\n${message}`);
}
});
});
});
assert.strictEqual(failures.length, 0, failures.join(''));
});
test('dirname', () => {
assert.strictEqual(path.dirname(path.normalize(__filename)).substr(-9),
isWindows ? 'test\\node' : 'test/node');
assert.strictEqual(path.posix.dirname('/a/b/'), '/a');
assert.strictEqual(path.posix.dirname('/a/b'), '/a');
assert.strictEqual(path.posix.dirname('/a'), '/');
assert.strictEqual(path.posix.dirname(''), '.');
assert.strictEqual(path.posix.dirname('/'), '/');
assert.strictEqual(path.posix.dirname('////'), '/');
assert.strictEqual(path.posix.dirname('//a'), '//');
assert.strictEqual(path.posix.dirname('foo'), '.');
assert.strictEqual(path.win32.dirname('c:\\'), 'c:\\');
assert.strictEqual(path.win32.dirname('c:\\foo'), 'c:\\');
assert.strictEqual(path.win32.dirname('c:\\foo\\'), 'c:\\');
assert.strictEqual(path.win32.dirname('c:\\foo\\bar'), 'c:\\foo');
assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\'), 'c:\\foo');
assert.strictEqual(path.win32.dirname('c:\\foo\\bar\\baz'), 'c:\\foo\\bar');
assert.strictEqual(path.win32.dirname('\\'), '\\');
assert.strictEqual(path.win32.dirname('\\foo'), '\\');
assert.strictEqual(path.win32.dirname('\\foo\\'), '\\');
assert.strictEqual(path.win32.dirname('\\foo\\bar'), '\\foo');
assert.strictEqual(path.win32.dirname('\\foo\\bar\\'), '\\foo');
assert.strictEqual(path.win32.dirname('\\foo\\bar\\baz'), '\\foo\\bar');
assert.strictEqual(path.win32.dirname('c:'), 'c:');
assert.strictEqual(path.win32.dirname('c:foo'), 'c:');
assert.strictEqual(path.win32.dirname('c:foo\\'), 'c:');
assert.strictEqual(path.win32.dirname('c:foo\\bar'), 'c:foo');
assert.strictEqual(path.win32.dirname('c:foo\\bar\\'), 'c:foo');
assert.strictEqual(path.win32.dirname('c:foo\\bar\\baz'), 'c:foo\\bar');
assert.strictEqual(path.win32.dirname('file:stream'), '.');
assert.strictEqual(path.win32.dirname('dir\\file:stream'), 'dir');
assert.strictEqual(path.win32.dirname('\\\\unc\\share'),
'\\\\unc\\share');
assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo'),
'\\\\unc\\share\\');
assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\'),
'\\\\unc\\share\\');
assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar'),
'\\\\unc\\share\\foo');
assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\'),
'\\\\unc\\share\\foo');
assert.strictEqual(path.win32.dirname('\\\\unc\\share\\foo\\bar\\baz'),
'\\\\unc\\share\\foo\\bar');
assert.strictEqual(path.win32.dirname('/a/b/'), '/a');
assert.strictEqual(path.win32.dirname('/a/b'), '/a');
assert.strictEqual(path.win32.dirname('/a'), '/');
assert.strictEqual(path.win32.dirname(''), '.');
assert.strictEqual(path.win32.dirname('/'), '/');
assert.strictEqual(path.win32.dirname('////'), '/');
assert.strictEqual(path.win32.dirname('foo'), '.');
// Tests from VSCode
function assertDirname(p: string, expected: string, win = false) {
const actual = win ? path.win32.dirname(p) : path.posix.dirname(p);
if (actual !== expected) {
assert.fail(`${p}: expected: ${expected}, ours: ${actual}`);
}
}
assertDirname('foo/bar', 'foo');
assertDirname('foo\\bar', 'foo', true);
assertDirname('/foo/bar', '/foo');
assertDirname('\\foo\\bar', '\\foo', true);
assertDirname('/foo', '/');
assertDirname('\\foo', '\\', true);
assertDirname('/', '/');
assertDirname('\\', '\\', true);
assertDirname('foo', '.');
assertDirname('f', '.');
assertDirname('f/', '.');
assertDirname('/folder/', '/');
assertDirname('c:\\some\\file.txt', 'c:\\some', true);
assertDirname('c:\\some', 'c:\\', true);
assertDirname('c:\\', 'c:\\', true);
assertDirname('c:', 'c:', true);
assertDirname('\\\\server\\share\\some\\path', '\\\\server\\share\\some', true);
assertDirname('\\\\server\\share\\some', '\\\\server\\share\\', true);
assertDirname('\\\\server\\share\\', '\\\\server\\share\\', true);
});
test('extname', () => {
const failures = [] as string[];
const slashRE = /\//g;
[
[__filename, '.js'],
['', ''],
['/path/to/file', ''],
['/path/to/file.ext', '.ext'],
['/path.to/file.ext', '.ext'],
['/path.to/file', ''],
['/path.to/.file', ''],
['/path.to/.file.ext', '.ext'],
['/path/to/f.ext', '.ext'],
['/path/to/..ext', '.ext'],
['/path/to/..', ''],
['file', ''],
['file.ext', '.ext'],
['.file', ''],
['.file.ext', '.ext'],
['/file', ''],
['/file.ext', '.ext'],
['/.file', ''],
['/.file.ext', '.ext'],
['.path/file.ext', '.ext'],
['file.ext.ext', '.ext'],
['file.', '.'],
['.', ''],
['./', ''],
['.file.ext', '.ext'],
['.file', ''],
['.file.', '.'],
['.file..', '.'],
['..', ''],
['../', ''],
['..file.ext', '.ext'],
['..file', '.file'],
['..file.', '.'],
['..file..', '.'],
['...', '.'],
['...ext', '.ext'],
['....', '.'],
['file.ext/', '.ext'],
['file.ext//', '.ext'],
['file/', ''],
['file//', ''],
['file./', '.'],
['file.//', '.'],
].forEach((test) => {
const expected = test[1];
[path.posix.extname, path.win32.extname].forEach((extname) => {
let input = test[0];
let os;
if (extname === path.win32.extname) {
input = input.replace(slashRE, '\\');
os = 'win32';
} else {
os = 'posix';
}
const actual = extname(input);
const message = `path.${os}.extname(${JSON.stringify(input)})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
if (actual !== expected) {
failures.push(`\n${message}`);
}
});
{
const input = `C:${test[0].replace(slashRE, '\\')}`;
const actual = path.win32.extname(input);
const message = `path.win32.extname(${JSON.stringify(input)})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
if (actual !== expected) {
failures.push(`\n${message}`);
}
}
});
assert.strictEqual(failures.length, 0, failures.join(''));
// On Windows, backslash is a path separator.
assert.strictEqual(path.win32.extname('.\\'), '');
assert.strictEqual(path.win32.extname('..\\'), '');
assert.strictEqual(path.win32.extname('file.ext\\'), '.ext');
assert.strictEqual(path.win32.extname('file.ext\\\\'), '.ext');
assert.strictEqual(path.win32.extname('file\\'), '');
assert.strictEqual(path.win32.extname('file\\\\'), '');
assert.strictEqual(path.win32.extname('file.\\'), '.');
assert.strictEqual(path.win32.extname('file.\\\\'), '.');
// On *nix, backslash is a valid name component like any other character.
assert.strictEqual(path.posix.extname('.\\'), '');
assert.strictEqual(path.posix.extname('..\\'), '.\\');
assert.strictEqual(path.posix.extname('file.ext\\'), '.ext\\');
assert.strictEqual(path.posix.extname('file.ext\\\\'), '.ext\\\\');
assert.strictEqual(path.posix.extname('file\\'), '');
assert.strictEqual(path.posix.extname('file\\\\'), '');
assert.strictEqual(path.posix.extname('file.\\'), '.\\');
assert.strictEqual(path.posix.extname('file.\\\\'), '.\\\\');
// Tests from VSCode
assert.equal(path.extname('far.boo'), '.boo');
assert.equal(path.extname('far.b'), '.b');
assert.equal(path.extname('far.'), '.');
assert.equal(path.extname('far.boo/boo.far'), '.far');
assert.equal(path.extname('far.boo/boo'), '');
});
test('resolve', () => {
const failures = [] as string[];
const slashRE = /\//g;
const backslashRE = /\\/g;
const resolveTests = [
[path.win32.resolve,
// arguments result
[[['c:/blah\\blah', 'd:/games', 'c:../a'], 'c:\\blah\\a'],
[['c:/ignore', 'd:\\a/b\\c/d', '\\e.exe'], 'd:\\e.exe'],
[['c:/ignore', 'c:/some/file'], 'c:\\some\\file'],
[['d:/ignore', 'd:some/dir//'], 'd:\\ignore\\some\\dir'],
[['.'], process.cwd()],
[['//server/share', '..', 'relative\\'], '\\\\server\\share\\relative'],
[['c:/', '//'], 'c:\\'],
[['c:/', '//dir'], 'c:\\dir'],
[['c:/', '//server/share'], '\\\\server\\share\\'],
[['c:/', '//server//share'], '\\\\server\\share\\'],
[['c:/', '///some//dir'], 'c:\\some\\dir'],
[['C:\\foo\\tmp.3\\', '..\\tmp.3\\cycles\\root.js'],
'C:\\foo\\tmp.3\\cycles\\root.js']
]
],
[path.posix.resolve,
// arguments result
[[['/var/lib', '../', 'file/'], '/var/file'],
[['/var/lib', '/../', 'file/'], '/file'],
[['a/b/c/', '../../..'], process.cwd()],
[['.'], process.cwd()],
[['/some/dir', '.', '/absolute/'], '/absolute'],
[['/foo/tmp.3/', '../tmp.3/cycles/root.js'], '/foo/tmp.3/cycles/root.js']
]
]
];
resolveTests.forEach((test) => {
const resolve = test[0];
//@ts-expect-error
test[1].forEach((test) => {
//@ts-expect-error
const actual = resolve.apply(null, test[0]);
let actualAlt;
const os = resolve === path.win32.resolve ? 'win32' : 'posix';
if (resolve === path.win32.resolve && !isWindows) {
actualAlt = actual.replace(backslashRE, '/');
}
else if (resolve !== path.win32.resolve && isWindows) {
actualAlt = actual.replace(slashRE, '\\');
}
const expected = test[1];
const message =
`path.${os}.resolve(${test[0].map(JSON.stringify).join(',')})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
if (actual !== expected && actualAlt !== expected) {
failures.push(`\n${message}`);
}
});
});
assert.strictEqual(failures.length, 0, failures.join(''));
// if (isWindows) {
// // Test resolving the current Windows drive letter from a spawned process.
// // See https://github.com/nodejs/node/issues/7215
// const currentDriveLetter = path.parse(process.cwd()).root.substring(0, 2);
// const resolveFixture = fixtures.path('path-resolve.js');
// const spawnResult = child.spawnSync(
// process.argv[0], [resolveFixture, currentDriveLetter]);
// const resolvedPath = spawnResult.stdout.toString().trim();
// assert.strictEqual(resolvedPath.toLowerCase(), process.cwd().toLowerCase());
// }
});
test('basename', () => {
assert.strictEqual(path.basename(__filename), 'path.test.js');
assert.strictEqual(path.basename(__filename, '.js'), 'path.test');
assert.strictEqual(path.basename('.js', '.js'), '');
assert.strictEqual(path.basename(''), '');
assert.strictEqual(path.basename('/dir/basename.ext'), 'basename.ext');
assert.strictEqual(path.basename('/basename.ext'), 'basename.ext');
assert.strictEqual(path.basename('basename.ext'), 'basename.ext');
assert.strictEqual(path.basename('basename.ext/'), 'basename.ext');
assert.strictEqual(path.basename('basename.ext//'), 'basename.ext');
assert.strictEqual(path.basename('aaa/bbb', '/bbb'), 'bbb');
assert.strictEqual(path.basename('aaa/bbb', 'a/bbb'), 'bbb');
assert.strictEqual(path.basename('aaa/bbb', 'bbb'), 'bbb');
assert.strictEqual(path.basename('aaa/bbb//', 'bbb'), 'bbb');
assert.strictEqual(path.basename('aaa/bbb', 'bb'), 'b');
assert.strictEqual(path.basename('aaa/bbb', 'b'), 'bb');
assert.strictEqual(path.basename('/aaa/bbb', '/bbb'), 'bbb');
assert.strictEqual(path.basename('/aaa/bbb', 'a/bbb'), 'bbb');
assert.strictEqual(path.basename('/aaa/bbb', 'bbb'), 'bbb');
assert.strictEqual(path.basename('/aaa/bbb//', 'bbb'), 'bbb');
assert.strictEqual(path.basename('/aaa/bbb', 'bb'), 'b');
assert.strictEqual(path.basename('/aaa/bbb', 'b'), 'bb');
assert.strictEqual(path.basename('/aaa/bbb'), 'bbb');
assert.strictEqual(path.basename('/aaa/'), 'aaa');
assert.strictEqual(path.basename('/aaa/b'), 'b');
assert.strictEqual(path.basename('/a/b'), 'b');
assert.strictEqual(path.basename('//a'), 'a');
assert.strictEqual(path.basename('a', 'a'), '');
// On Windows a backslash acts as a path separator.
assert.strictEqual(path.win32.basename('\\dir\\basename.ext'), 'basename.ext');
assert.strictEqual(path.win32.basename('\\basename.ext'), 'basename.ext');
assert.strictEqual(path.win32.basename('basename.ext'), 'basename.ext');
assert.strictEqual(path.win32.basename('basename.ext\\'), 'basename.ext');
assert.strictEqual(path.win32.basename('basename.ext\\\\'), 'basename.ext');
assert.strictEqual(path.win32.basename('foo'), 'foo');
assert.strictEqual(path.win32.basename('aaa\\bbb', '\\bbb'), 'bbb');
assert.strictEqual(path.win32.basename('aaa\\bbb', 'a\\bbb'), 'bbb');
assert.strictEqual(path.win32.basename('aaa\\bbb', 'bbb'), 'bbb');
assert.strictEqual(path.win32.basename('aaa\\bbb\\\\\\\\', 'bbb'), 'bbb');
assert.strictEqual(path.win32.basename('aaa\\bbb', 'bb'), 'b');
assert.strictEqual(path.win32.basename('aaa\\bbb', 'b'), 'bb');
assert.strictEqual(path.win32.basename('C:'), '');
assert.strictEqual(path.win32.basename('C:.'), '.');
assert.strictEqual(path.win32.basename('C:\\'), '');
assert.strictEqual(path.win32.basename('C:\\dir\\base.ext'), 'base.ext');
assert.strictEqual(path.win32.basename('C:\\basename.ext'), 'basename.ext');
assert.strictEqual(path.win32.basename('C:basename.ext'), 'basename.ext');
assert.strictEqual(path.win32.basename('C:basename.ext\\'), 'basename.ext');
assert.strictEqual(path.win32.basename('C:basename.ext\\\\'), 'basename.ext');
assert.strictEqual(path.win32.basename('C:foo'), 'foo');
assert.strictEqual(path.win32.basename('file:stream'), 'file:stream');
assert.strictEqual(path.win32.basename('a', 'a'), '');
// On unix a backslash is just treated as any other character.
assert.strictEqual(path.posix.basename('\\dir\\basename.ext'),
'\\dir\\basename.ext');
assert.strictEqual(path.posix.basename('\\basename.ext'), '\\basename.ext');
assert.strictEqual(path.posix.basename('basename.ext'), 'basename.ext');
assert.strictEqual(path.posix.basename('basename.ext\\'), 'basename.ext\\');
assert.strictEqual(path.posix.basename('basename.ext\\\\'), 'basename.ext\\\\');
assert.strictEqual(path.posix.basename('foo'), 'foo');
// POSIX filenames may include control characters
// c.f. http://www.dwheeler.com/essays/fixing-unix-linux-filenames.html
const controlCharFilename = `Icon${String.fromCharCode(13)}`;
assert.strictEqual(path.posix.basename(`/a/b/${controlCharFilename}`),
controlCharFilename);
// Tests from VSCode
assert.equal(path.basename('foo/bar'), 'bar');
assert.equal(path.posix.basename('foo\\bar'), 'foo\\bar');
assert.equal(path.win32.basename('foo\\bar'), 'bar');
assert.equal(path.basename('/foo/bar'), 'bar');
assert.equal(path.posix.basename('\\foo\\bar'), '\\foo\\bar');
assert.equal(path.win32.basename('\\foo\\bar'), 'bar');
assert.equal(path.basename('./bar'), 'bar');
assert.equal(path.posix.basename('.\\bar'), '.\\bar');
assert.equal(path.win32.basename('.\\bar'), 'bar');
assert.equal(path.basename('/bar'), 'bar');
assert.equal(path.posix.basename('\\bar'), '\\bar');
assert.equal(path.win32.basename('\\bar'), 'bar');
assert.equal(path.basename('bar/'), 'bar');
assert.equal(path.posix.basename('bar\\'), 'bar\\');
assert.equal(path.win32.basename('bar\\'), 'bar');
assert.equal(path.basename('bar'), 'bar');
assert.equal(path.basename('////////'), '');
assert.equal(path.posix.basename('\\\\\\\\'), '\\\\\\\\');
assert.equal(path.win32.basename('\\\\\\\\'), '');
});
test('relative', () => {
const failures = [] as string[];
const relativeTests = [
[path.win32.relative,
// arguments result
[['c:/blah\\blah', 'd:/games', 'd:\\games'],
['c:/aaaa/bbbb', 'c:/aaaa', '..'],
['c:/aaaa/bbbb', 'c:/cccc', '..\\..\\cccc'],
['c:/aaaa/bbbb', 'c:/aaaa/bbbb', ''],
['c:/aaaa/bbbb', 'c:/aaaa/cccc', '..\\cccc'],
['c:/aaaa/', 'c:/aaaa/cccc', 'cccc'],
['c:/', 'c:\\aaaa\\bbbb', 'aaaa\\bbbb'],
['c:/aaaa/bbbb', 'd:\\', 'd:\\'],
['c:/AaAa/bbbb', 'c:/aaaa/bbbb', ''],
['c:/aaaaa/', 'c:/aaaa/cccc', '..\\aaaa\\cccc'],
['C:\\foo\\bar\\baz\\quux', 'C:\\', '..\\..\\..\\..'],
['C:\\foo\\test', 'C:\\foo\\test\\bar\\package.json', 'bar\\package.json'],
['C:\\foo\\bar\\baz-quux', 'C:\\foo\\bar\\baz', '..\\baz'],
['C:\\foo\\bar\\baz', 'C:\\foo\\bar\\baz-quux', '..\\baz-quux'],
['\\\\foo\\bar', '\\\\foo\\bar\\baz', 'baz'],
['\\\\foo\\bar\\baz', '\\\\foo\\bar', '..'],
['\\\\foo\\bar\\baz-quux', '\\\\foo\\bar\\baz', '..\\baz'],
['\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz-quux', '..\\baz-quux'],
['C:\\baz-quux', 'C:\\baz', '..\\baz'],
['C:\\baz', 'C:\\baz-quux', '..\\baz-quux'],
['\\\\foo\\baz-quux', '\\\\foo\\baz', '..\\baz'],
['\\\\foo\\baz', '\\\\foo\\baz-quux', '..\\baz-quux'],
['C:\\baz', '\\\\foo\\bar\\baz', '\\\\foo\\bar\\baz'],
['\\\\foo\\bar\\baz', 'C:\\baz', 'C:\\baz']
]
],
[path.posix.relative,
// arguments result
[['/var/lib', '/var', '..'],
['/var/lib', '/bin', '../../bin'],
['/var/lib', '/var/lib', ''],
['/var/lib', '/var/apache', '../apache'],
['/var/', '/var/lib', 'lib'],
['/', '/var/lib', 'var/lib'],
['/foo/test', '/foo/test/bar/package.json', 'bar/package.json'],
['/Users/a/web/b/test/mails', '/Users/a/web/b', '../..'],
['/foo/bar/baz-quux', '/foo/bar/baz', '../baz'],
['/foo/bar/baz', '/foo/bar/baz-quux', '../baz-quux'],
['/baz-quux', '/baz', '../baz'],
['/baz', '/baz-quux', '../baz-quux']
]
]
];
relativeTests.forEach((test) => {
const relative = test[0];
//@ts-expect-error
test[1].forEach((test) => {
//@ts-expect-error
const actual = relative(test[0], test[1]);
const expected = test[2];
const os = relative === path.win32.relative ? 'win32' : 'posix';
const message = `path.${os}.relative(${test.slice(0, 2).map(JSON.stringify).join(',')})\n expect=${JSON.stringify(expected)}\n actual=${JSON.stringify(actual)}`;
if (actual !== expected) {
failures.push(`\n${message}`);
}
});
});
assert.strictEqual(failures.length, 0, failures.join(''));
});
test('normalize', () => {
assert.strictEqual(path.win32.normalize('./fixtures///b/../b/c.js'),
'fixtures\\b\\c.js');
assert.strictEqual(path.win32.normalize('/foo/../../../bar'), '\\bar');
assert.strictEqual(path.win32.normalize('a//b//../b'), 'a\\b');
assert.strictEqual(path.win32.normalize('a//b//./c'), 'a\\b\\c');
assert.strictEqual(path.win32.normalize('a//b//.'), 'a\\b');
assert.strictEqual(path.win32.normalize('//server/share/dir/file.ext'),
'\\\\server\\share\\dir\\file.ext');
assert.strictEqual(path.win32.normalize('/a/b/c/../../../x/y/z'), '\\x\\y\\z');
assert.strictEqual(path.win32.normalize('C:'), 'C:.');
assert.strictEqual(path.win32.normalize('C:..\\abc'), 'C:..\\abc');
assert.strictEqual(path.win32.normalize('C:..\\..\\abc\\..\\def'),
'C:..\\..\\def');
assert.strictEqual(path.win32.normalize('C:\\.'), 'C:\\');
assert.strictEqual(path.win32.normalize('file:stream'), 'file:stream');
assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\'), 'bar\\');
assert.strictEqual(path.win32.normalize('bar\\foo..\\..'), 'bar');
assert.strictEqual(path.win32.normalize('bar\\foo..\\..\\baz'), 'bar\\baz');
assert.strictEqual(path.win32.normalize('bar\\foo..\\'), 'bar\\foo..\\');
assert.strictEqual(path.win32.normalize('bar\\foo..'), 'bar\\foo..');
assert.strictEqual(path.win32.normalize('..\\foo..\\..\\..\\bar'),
'..\\..\\bar');
assert.strictEqual(path.win32.normalize('..\\...\\..\\.\\...\\..\\..\\bar'),
'..\\..\\bar');
assert.strictEqual(path.win32.normalize('../../../foo/../../../bar'),
'..\\..\\..\\..\\..\\bar');
assert.strictEqual(path.win32.normalize('../../../foo/../../../bar/../../'),
'..\\..\\..\\..\\..\\..\\');
assert.strictEqual(
path.win32.normalize('../foobar/barfoo/foo/../../../bar/../../'),
'..\\..\\'
);
assert.strictEqual(
path.win32.normalize('../.../../foobar/../../../bar/../../baz'),
'..\\..\\..\\..\\baz'
);
assert.strictEqual(path.win32.normalize('foo/bar\\baz'), 'foo\\bar\\baz');
assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'),
'fixtures/b/c.js');
assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar');
assert.strictEqual(path.posix.normalize('a//b//../b'), 'a/b');
assert.strictEqual(path.posix.normalize('a//b//./c'), 'a/b/c');
assert.strictEqual(path.posix.normalize('a//b//.'), 'a/b');
assert.strictEqual(path.posix.normalize('/a/b/c/../../../x/y/z'), '/x/y/z');
assert.strictEqual(path.posix.normalize('///..//./foo/.//bar'), '/foo/bar');
assert.strictEqual(path.posix.normalize('bar/foo../../'), 'bar/');
assert.strictEqual(path.posix.normalize('bar/foo../..'), 'bar');
assert.strictEqual(path.posix.normalize('bar/foo../../baz'), 'bar/baz');
assert.strictEqual(path.posix.normalize('bar/foo../'), 'bar/foo../');
assert.strictEqual(path.posix.normalize('bar/foo..'), 'bar/foo..');
assert.strictEqual(path.posix.normalize('../foo../../../bar'), '../../bar');
assert.strictEqual(path.posix.normalize('../.../.././.../../../bar'),
'../../bar');
assert.strictEqual(path.posix.normalize('../../../foo/../../../bar'),
'../../../../../bar');
assert.strictEqual(path.posix.normalize('../../../foo/../../../bar/../../'),
'../../../../../../');
assert.strictEqual(
path.posix.normalize('../foobar/barfoo/foo/../../../bar/../../'),
'../../'
);
assert.strictEqual(
path.posix.normalize('../.../../foobar/../../../bar/../../baz'),
'../../../../baz'
);
assert.strictEqual(path.posix.normalize('foo/bar\\baz'), 'foo/bar\\baz');
});
test('isAbsolute', () => {
assert.strictEqual(path.win32.isAbsolute('/'), true);
assert.strictEqual(path.win32.isAbsolute('//'), true);
assert.strictEqual(path.win32.isAbsolute('//server'), true);
assert.strictEqual(path.win32.isAbsolute('//server/file'), true);
assert.strictEqual(path.win32.isAbsolute('\\\\server\\file'), true);
assert.strictEqual(path.win32.isAbsolute('\\\\server'), true);
assert.strictEqual(path.win32.isAbsolute('\\\\'), true);
assert.strictEqual(path.win32.isAbsolute('c'), false);
assert.strictEqual(path.win32.isAbsolute('c:'), false);
assert.strictEqual(path.win32.isAbsolute('c:\\'), true);
assert.strictEqual(path.win32.isAbsolute('c:/'), true);
assert.strictEqual(path.win32.isAbsolute('c://'), true);
assert.strictEqual(path.win32.isAbsolute('C:/Users/'), true);
assert.strictEqual(path.win32.isAbsolute('C:\\Users\\'), true);
assert.strictEqual(path.win32.isAbsolute('C:cwd/another'), false);
assert.strictEqual(path.win32.isAbsolute('C:cwd\\another'), false);
assert.strictEqual(path.win32.isAbsolute('directory/directory'), false);
assert.strictEqual(path.win32.isAbsolute('directory\\directory'), false);
assert.strictEqual(path.posix.isAbsolute('/home/foo'), true);
assert.strictEqual(path.posix.isAbsolute('/home/foo/..'), true);
assert.strictEqual(path.posix.isAbsolute('bar/'), false);
assert.strictEqual(path.posix.isAbsolute('./baz'), false);
// Tests from VSCode:
// Absolute Paths
[
'C:/',
'C:\\',
'C:/foo',
'C:\\foo',
'z:/foo/bar.txt',
'z:\\foo\\bar.txt',
'\\\\localhost\\c$\\foo',
'/',
'/foo'
].forEach(absolutePath => {
assert.ok(path.win32.isAbsolute(absolutePath), absolutePath);
});
[
'/',
'/foo',
'/foo/bar.txt'
].forEach(absolutePath => {
assert.ok(path.posix.isAbsolute(absolutePath), absolutePath);
});
// Relative Paths
[
'',
'foo',
'foo/bar',
'./foo',
'http://foo.com/bar'
].forEach(nonAbsolutePath => {
assert.ok(!path.win32.isAbsolute(nonAbsolutePath), nonAbsolutePath);
});
[
'',
'foo',
'foo/bar',
'./foo',
'http://foo.com/bar',
'z:/foo/bar.txt',
].forEach(nonAbsolutePath => {
assert.ok(!path.posix.isAbsolute(nonAbsolutePath), nonAbsolutePath);
});
});
test('path', () => {
// path.sep tests
// windows
assert.strictEqual(path.win32.sep, '\\');
// posix
assert.strictEqual(path.posix.sep, '/');
// path.delimiter tests
// windows
assert.strictEqual(path.win32.delimiter, ';');
// posix
assert.strictEqual(path.posix.delimiter, ':');
// if (isWindows) {
// assert.strictEqual(path, path.win32);
// } else {
// assert.strictEqual(path, path.posix);
// }
});
// test('perf', () => {
// const folderNames = [
// 'abc',
// 'Users',
// 'reallylongfoldername',
// 's',
// 'reallyreallyreallylongfoldername',
// 'home'
// ];
// const basePaths = [
// 'C:',
// '',
// ];
// const separators = [
// '\\',
// '/'
// ];
// function randomInt(ciel: number): number {
// return Math.floor(Math.random() * ciel);
// }
// let pathsToNormalize = [];
// let pathsToJoin = [];
// let i;
// for (i = 0; i < 1000000; i++) {
// const basePath = basePaths[randomInt(basePaths.length)];
// let lengthOfPath = randomInt(10) + 2;
// let pathToNormalize = basePath + separators[randomInt(separators.length)];
// while (lengthOfPath-- > 0) {
// pathToNormalize = pathToNormalize + folderNames[randomInt(folderNames.length)] + separators[randomInt(separators.length)];
// }
// pathsToNormalize.push(pathToNormalize);
// let pathToJoin = '';
// lengthOfPath = randomInt(10) + 2;
// while (lengthOfPath-- > 0) {
// pathToJoin = pathToJoin + folderNames[randomInt(folderNames.length)] + separators[randomInt(separators.length)];
// }
// pathsToJoin.push(pathToJoin + '.ts');
// }
// let newTime = 0;
// let j;
// for(j = 0; j < pathsToJoin.length; j++) {
// const path1 = pathsToNormalize[j];
// const path2 = pathsToNormalize[j];
// const newStart = performance.now();
// path.join(path1, path2);
// newTime += performance.now() - newStart;
// }
// assert.ok(false, `Time: ${newTime}ms.`);
// });
});

View File

@@ -0,0 +1,23 @@
'use strict';
/// <reference path="employee.ts" />
var Workforce;
(function (Workforce_1) {
var Company = (function () {
function Company() {
}
return Company;
})();
(function (property, Workforce, IEmployee) {
if (property === undefined) { property = employees; }
if (IEmployee === undefined) { IEmployee = []; }
property;
calculateMonthlyExpenses();
{
var result = 0;
for (var i = 0; i < employees.length; i++) {
result += employees[i].calculatePay();
}
return result;
}
});
})(Workforce || (Workforce = {}));

View File

@@ -0,0 +1,117 @@
'use strict';
var Conway;
(function (Conway) {
var Cell = (function () {
function Cell() {
}
return Cell;
})();
(function (property, number, property, number, property, boolean) {
if (property === undefined) { property = row; }
if (property === undefined) { property = col; }
if (property === undefined) { property = live; }
});
var GameOfLife = (function () {
function GameOfLife() {
}
return GameOfLife;
})();
(function () {
property;
gridSize = 50;
property;
canvasSize = 600;
property;
lineColor = '#cdcdcd';
property;
liveColor = '#666';
property;
deadColor = '#eee';
property;
initialLifeProbability = 0.5;
property;
animationRate = 60;
property;
cellSize = 0;
property;
context: ICanvasRenderingContext2D;
property;
world = createWorld();
circleOfLife();
function createWorld() {
return travelWorld(function (cell) {
cell.live = Math.random() < initialLifeProbability;
return cell;
});
}
function circleOfLife() {
world = travelWorld(function (cell) {
cell = world[cell.row][cell.col];
draw(cell);
return resolveNextGeneration(cell);
});
setTimeout(function () { circleOfLife(); }, animationRate);
}
function resolveNextGeneration(cell) {
var count = countNeighbors(cell);
var newCell = new Cell(cell.row, cell.col, cell.live);
if (count < 2 || count > 3)
newCell.live = false;
else if (count == 3)
newCell.live = true;
return newCell;
}
function countNeighbors(cell) {
var neighbors = 0;
for (var row = -1; row <= 1; row++) {
for (var col = -1; col <= 1; col++) {
if (row == 0 && col == 0)
continue;
if (isAlive(cell.row + row, cell.col + col)) {
neighbors++;
}
}
}
return neighbors;
}
function isAlive(row, col) {
// todo - need to guard with world[row] exists?
if (row < 0 || col < 0 || row >= gridSize || col >= gridSize)
return false;
return world[row][col].live;
}
function travelWorld(callback) {
var result = [];
for (var row = 0; row < gridSize; row++) {
var rowData = [];
for (var col = 0; col < gridSize; col++) {
rowData.push(callback(new Cell(row, col, false)));
}
result.push(rowData);
}
return result;
}
function draw(cell) {
if (context == null)
context = createDrawingContext();
if (cellSize == 0)
cellSize = canvasSize / gridSize;
context.strokeStyle = lineColor;
context.strokeRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize);
context.fillStyle = cell.live ? liveColor : deadColor;
context.fillRect(cell.row * cellSize, cell.col * cellSize, cellSize, cellSize);
}
function createDrawingContext() {
var canvas = document.getElementById('conway-canvas');
if (canvas == null) {
canvas = document.createElement('canvas');
canvas.id = "conway-canvas";
canvas.width = canvasSize;
canvas.height = canvasSize;
document.body.appendChild(canvas);
}
return canvas.getContext('2d');
}
});
})(Conway || (Conway = {}));
var game = new Conway.GameOfLife();

View File

@@ -0,0 +1,38 @@
'use strict';
var Workforce;
(function (Workforce) {
var Employee = (function () {
function Employee() {
}
return Employee;
})();
(property);
name: string, property;
basepay: number;
implements;
IEmployee;
{
name;
basepay;
}
var SalesEmployee = (function () {
function SalesEmployee() {
}
return SalesEmployee;
})();
();
Employee(name, basepay);
{
function calculatePay() {
var multiplier = (document.getElementById("mult")), as = any, value;
return _super.calculatePay.call(this) * multiplier + bonus;
}
}
var employee = new Employee('Bob', 1000);
var salesEmployee = new SalesEmployee('Jim', 800, 400);
salesEmployee.calclatePay(); // error: No member 'calclatePay' on SalesEmployee
})(Workforce || (Workforce = {}));
extern;
var $;
var s = Workforce.salesEmployee.calculatePay();
$('#results').text(s);

View File

@@ -0,0 +1,24 @@
'use strict';
var M;
(function (M) {
var C = (function () {
function C() {
}
return C;
})();
(function (x, property, number) {
if (property === undefined) { property = w; }
var local = 1;
// unresolved symbol because x is local
//self.x++;
self.w--; // ok because w is a property
property;
f = function (y) {
return y + x + local + w + self.w;
};
function sum(z) {
return z + f(z) + w + self.w;
}
});
})(M || (M = {}));
var c = new M.C(12, 5);

View File

@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head id='headID'>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Strada </title>
<link href="site.css" rel="stylesheet" type="text/css" />
<script src="jquery-1.4.1.js"></script>
<script src="../compiler/dtree.js" type="text/javascript"></script>
<script src="../compiler/typescript.js" type="text/javascript"></script>
<script type="text/javascript">
// Compile strada source into resulting javascript
function compile(prog, libText) {
var outfile = {
source: "",
Write: function (s) { this.source += s; },
WriteLine: function (s) { this.source += s + "\r"; },
}
var parseErrors = []
var compiler=new Tools.TypeScriptCompiler(outfile,true);
compiler.setErrorCallback(function(start,len, message) { parseErrors.push({start:start, len:len, message:message}); });
compiler.addUnit(libText,"lib.ts");
compiler.addUnit(prog,"input.ts");
compiler.typeCheck();
compiler.emit();
if(parseErrors.length > 0 ) {
//throw new Error(parseErrors);
}
while(outfile.source[0] == '/' && outfile.source[1] == '/' && outfile.source[2] == ' ') {
outfile.source = outfile.source.slice(outfile.source.indexOf('\r')+1);
}
var errorPrefix = "";
for(var i = 0;i<parseErrors.length;i++) {
errorPrefix += "// Error: (" + parseErrors[i].start + "," + parseErrors[i].len + ") " + parseErrors[i].message + "\r";
}
return errorPrefix + outfile.source;
}
</script>
<script type="text/javascript">
var libText = "";
$.get("../compiler/lib.ts", function(newLibText) {
libText = newLibText;
});
// execute the javascript in the compiledOutput pane
function execute() {
$('#compilation').text("Running...");
var txt = $('#compiledOutput').val();
var res;
try {
var ret = eval(txt);
res = "Ran successfully!";
} catch(e) {
res = "Exception thrown: " + e;
}
$('#compilation').text(String(res));
}
// recompile the stradaSrc and populate the compiledOutput pane
function srcUpdated() {
var newText = $('#stradaSrc').val();
var compiledSource;
try {
compiledSource = compile(newText, libText);
} catch (e) {
compiledSource = "//Parse error"
for(var i in e)
compiledSource += "\r// " + e[i];
}
$('#compiledOutput').val(compiledSource);
}
// Populate the stradaSrc pane with one of the built in samples
function exampleSelectionChanged() {
var examples = document.getElementById('examples');
var selectedExample = examples.options[examples.selectedIndex].value;
if (selectedExample != "") {
$.get('examples/' + selectedExample, function (srcText) {
$('#stradaSrc').val(srcText);
setTimeout(srcUpdated,100);
}, function (err) {
console.log(err);
});
}
}
</script>
</head>
<body>
<h1>TypeScript</h1>
<br />
<select id="examples" onchange='exampleSelectionChanged()'>
<option value="">Select...</option>
<option value="small.ts">Small</option>
<option value="employee.ts">Employees</option>
<option value="conway.ts">Conway Game of Life</option>
<option value="typescript.ts">TypeScript Compiler</option>
</select>
<div>
<textarea id='stradaSrc' rows='40' cols='80' onchange='srcUpdated()' onkeyup='srcUpdated()' spellcheck="false">
//Type your TypeScript here...
</textarea>
<textarea id='compiledOutput' rows='40' cols='80' spellcheck="false">
//Compiled code will show up here...
</textarea>
<br />
<button onclick='execute()'/>Run</button>
<div id='compilation'>Press 'run' to execute code...</div>
<div id='results'>...write your results into #results...</div>
</div>
<div id='bod' style='display:none'></div>
</body>
</html>

View File

@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------
The base color for this template is #5c87b2. If you'd like
to use a different color start by replacing all instances of
#5c87b2 with your new color.
----------------------------------------------------------*/
body
{
background-color: #5c87b2;
font-size: .75em;
font-family: Segoe UI, Verdana, Helvetica, Sans-Serif;
margin: 8px;
padding: 0;
color: #696969;
}
h1, h2, h3, h4, h5, h6
{
color: #000;
font-size: 40px;
margin: 0px;
}
textarea
{
font-family: Consolas
}
#results
{
margin-top: 2em;
margin-left: 2em;
color: black;
font-size: medium;
}

View File

@@ -0,0 +1,480 @@
/*---------------------------------------------------------------------------------------------
* 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 * as os from 'os';
import * as path from 'vs/base/common/path';
import * as fs from 'fs';
import * as uuid from 'vs/base/common/uuid';
import * as pfs from 'vs/base/node/pfs';
import { timeout } from 'vs/base/common/async';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { isWindows } from 'vs/base/common/platform';
import { canNormalize } from 'vs/base/common/normalization';
import { VSBuffer } from 'vs/base/common/buffer';
suite('PFS', function () {
// Given issues such as https://github.com/microsoft/vscode/issues/84066
// we see random test failures when accessing the native file system. To
// diagnose further, we retry node.js file access tests up to 3 times to
// rule out any random disk issue.
this.retries(3);
test('writeFile', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'writefile.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
await pfs.writeFile(testFile, 'Hello World', (null!));
assert.equal(fs.readFileSync(testFile), 'Hello World');
await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
});
test('writeFile - parallel write on different files works', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile1 = path.join(newDir, 'writefile1.txt');
const testFile2 = path.join(newDir, 'writefile2.txt');
const testFile3 = path.join(newDir, 'writefile3.txt');
const testFile4 = path.join(newDir, 'writefile4.txt');
const testFile5 = path.join(newDir, 'writefile5.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
await Promise.all([
pfs.writeFile(testFile1, 'Hello World 1', (null!)),
pfs.writeFile(testFile2, 'Hello World 2', (null!)),
pfs.writeFile(testFile3, 'Hello World 3', (null!)),
pfs.writeFile(testFile4, 'Hello World 4', (null!)),
pfs.writeFile(testFile5, 'Hello World 5', (null!))
]);
assert.equal(fs.readFileSync(testFile1), 'Hello World 1');
assert.equal(fs.readFileSync(testFile2), 'Hello World 2');
assert.equal(fs.readFileSync(testFile3), 'Hello World 3');
assert.equal(fs.readFileSync(testFile4), 'Hello World 4');
assert.equal(fs.readFileSync(testFile5), 'Hello World 5');
await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
});
test('writeFile - parallel write on same files works and is sequentalized', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'writefile.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
await Promise.all([
pfs.writeFile(testFile, 'Hello World 1', undefined),
pfs.writeFile(testFile, 'Hello World 2', undefined),
timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 3', undefined)),
pfs.writeFile(testFile, 'Hello World 4', undefined),
timeout(10).then(() => pfs.writeFile(testFile, 'Hello World 5', undefined))
]);
assert.equal(fs.readFileSync(testFile), 'Hello World 5');
await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
});
test('rimraf - simple - unlink', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
await pfs.rimraf(newDir);
assert.ok(!fs.existsSync(newDir));
});
test('rimraf - simple - move', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
await pfs.rimraf(newDir, pfs.RimRafMode.MOVE);
assert.ok(!fs.existsSync(newDir));
});
test('rimraf - recursive folder structure - unlink', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
fs.mkdirSync(path.join(newDir, 'somefolder'));
fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents');
await pfs.rimraf(newDir);
assert.ok(!fs.existsSync(newDir));
});
test('rimraf - recursive folder structure - move', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
fs.mkdirSync(path.join(newDir, 'somefolder'));
fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents');
await pfs.rimraf(newDir, pfs.RimRafMode.MOVE);
assert.ok(!fs.existsSync(newDir));
});
test('rimraf - simple ends with dot - move', async () => {
const id = `${uuid.generateUuid()}.`;
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
await pfs.rimraf(newDir, pfs.RimRafMode.MOVE);
assert.ok(!fs.existsSync(newDir));
});
test('rimraf - simple ends with dot slash/backslash - move', async () => {
const id = `${uuid.generateUuid()}.`;
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
await pfs.rimraf(`${newDir}${path.sep}`, pfs.RimRafMode.MOVE);
assert.ok(!fs.existsSync(newDir));
});
test('rimrafSync - swallows file not found error', function () {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
pfs.rimrafSync(newDir);
assert.ok(!fs.existsSync(newDir));
});
test('rimrafSync - simple', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
pfs.rimrafSync(newDir);
assert.ok(!fs.existsSync(newDir));
});
test('rimrafSync - recursive folder structure', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
fs.mkdirSync(path.join(newDir, 'somefolder'));
fs.writeFileSync(path.join(newDir, 'somefolder', 'somefile.txt'), 'Contents');
pfs.rimrafSync(newDir);
assert.ok(!fs.existsSync(newDir));
});
test('moveIgnoreError', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
try {
await pfs.renameIgnoreError(path.join(newDir, 'foo'), path.join(newDir, 'bar'));
return pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
}
catch (error) {
assert.fail(error);
}
});
test('copy, move and delete', async () => {
const id = uuid.generateUuid();
const id2 = uuid.generateUuid();
const sourceDir = getPathFromAmdModule(require, './fixtures');
const parentDir = path.join(os.tmpdir(), 'vsctests', 'pfs');
const targetDir = path.join(parentDir, id);
const targetDir2 = path.join(parentDir, id2);
await pfs.copy(sourceDir, targetDir);
assert.ok(fs.existsSync(targetDir));
assert.ok(fs.existsSync(path.join(targetDir, 'index.html')));
assert.ok(fs.existsSync(path.join(targetDir, 'site.css')));
assert.ok(fs.existsSync(path.join(targetDir, 'examples')));
assert.ok(fs.statSync(path.join(targetDir, 'examples')).isDirectory());
assert.ok(fs.existsSync(path.join(targetDir, 'examples', 'small.jxs')));
await pfs.move(targetDir, targetDir2);
assert.ok(!fs.existsSync(targetDir));
assert.ok(fs.existsSync(targetDir2));
assert.ok(fs.existsSync(path.join(targetDir2, 'index.html')));
assert.ok(fs.existsSync(path.join(targetDir2, 'site.css')));
assert.ok(fs.existsSync(path.join(targetDir2, 'examples')));
assert.ok(fs.statSync(path.join(targetDir2, 'examples')).isDirectory());
assert.ok(fs.existsSync(path.join(targetDir2, 'examples', 'small.jxs')));
await pfs.move(path.join(targetDir2, 'index.html'), path.join(targetDir2, 'index_moved.html'));
assert.ok(!fs.existsSync(path.join(targetDir2, 'index.html')));
assert.ok(fs.existsSync(path.join(targetDir2, 'index_moved.html')));
await pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
assert.ok(!fs.existsSync(parentDir));
});
test('mkdirp', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
return pfs.rimraf(parentDir, pfs.RimRafMode.MOVE);
});
test('readDirsInDir', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
await pfs.mkdirp(newDir, 493);
fs.mkdirSync(path.join(newDir, 'somefolder1'));
fs.mkdirSync(path.join(newDir, 'somefolder2'));
fs.mkdirSync(path.join(newDir, 'somefolder3'));
fs.writeFileSync(path.join(newDir, 'somefile.txt'), 'Contents');
fs.writeFileSync(path.join(newDir, 'someOtherFile.txt'), 'Contents');
const result = await pfs.readDirsInDir(newDir);
assert.equal(result.length, 3);
assert.ok(result.indexOf('somefolder1') !== -1);
assert.ok(result.indexOf('somefolder2') !== -1);
assert.ok(result.indexOf('somefolder3') !== -1);
await pfs.rimraf(newDir);
});
test('stat link', async () => {
if (isWindows) {
return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges
}
const id1 = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id1);
const directory = path.join(parentDir, 'pfs', id1);
const id2 = uuid.generateUuid();
const symbolicLink = path.join(parentDir, 'pfs', id2);
await pfs.mkdirp(directory, 493);
fs.symlinkSync(directory, symbolicLink);
let statAndIsLink = await pfs.statLink(directory);
assert.ok(!statAndIsLink?.symbolicLink);
statAndIsLink = await pfs.statLink(symbolicLink);
assert.ok(statAndIsLink?.symbolicLink);
assert.ok(!statAndIsLink?.symbolicLink?.dangling);
pfs.rimrafSync(directory);
});
test('stat link (non existing target)', async () => {
if (isWindows) {
return; // Symlinks are not the same on win, and we can not create them programitically without admin privileges
}
const id1 = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id1);
const directory = path.join(parentDir, 'pfs', id1);
const id2 = uuid.generateUuid();
const symbolicLink = path.join(parentDir, 'pfs', id2);
await pfs.mkdirp(directory, 493);
fs.symlinkSync(directory, symbolicLink);
pfs.rimrafSync(directory);
const statAndIsLink = await pfs.statLink(symbolicLink);
assert.ok(statAndIsLink?.symbolicLink);
assert.ok(statAndIsLink?.symbolicLink?.dangling);
});
test('readdir', async () => {
if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id, 'öäü');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
const children = await pfs.readdir(path.join(parentDir, 'pfs', id));
assert.equal(children.some(n => n === 'öäü'), true); // Mac always converts to NFD, so
await pfs.rimraf(parentDir);
}
});
test('readdirWithFileTypes', async () => {
if (canNormalize && typeof process.versions['electron'] !== 'undefined' /* needs electron */) {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const testDir = path.join(parentDir, 'pfs', id);
const newDir = path.join(testDir, 'öäü');
await pfs.mkdirp(newDir, 493);
await pfs.writeFile(path.join(testDir, 'somefile.txt'), 'contents');
assert.ok(fs.existsSync(newDir));
const children = await pfs.readdirWithFileTypes(testDir);
assert.equal(children.some(n => n.name === 'öäü'), true); // Mac always converts to NFD, so
assert.equal(children.some(n => n.isDirectory()), true);
assert.equal(children.some(n => n.name === 'somefile.txt'), true);
assert.equal(children.some(n => n.isFile()), true);
await pfs.rimraf(parentDir);
}
});
test('writeFile (string)', async () => {
const smallData = 'Hello World';
const bigData = (new Array(100 * 1024)).join('Large String\n');
return testWriteFileAndFlush(smallData, smallData, bigData, bigData);
});
test('writeFile (Buffer)', async () => {
const smallData = 'Hello World';
const bigData = (new Array(100 * 1024)).join('Large String\n');
return testWriteFileAndFlush(Buffer.from(smallData), smallData, Buffer.from(bigData), bigData);
});
test('writeFile (UInt8Array)', async () => {
const smallData = 'Hello World';
const bigData = (new Array(100 * 1024)).join('Large String\n');
return testWriteFileAndFlush(VSBuffer.fromString(smallData).buffer, smallData, VSBuffer.fromString(bigData).buffer, bigData);
});
async function testWriteFileAndFlush(
smallData: string | Buffer | Uint8Array,
smallDataValue: string,
bigData: string | Buffer | Uint8Array,
bigDataValue: string
): Promise<void> {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
await pfs.writeFile(testFile, smallData);
assert.equal(fs.readFileSync(testFile), smallDataValue);
await pfs.writeFile(testFile, bigData);
assert.equal(fs.readFileSync(testFile), bigDataValue);
await pfs.rimraf(parentDir);
}
test('writeFile (string, error handling)', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
fs.mkdirSync(testFile); // this will trigger an error because testFile is now a directory!
let expectedError: Error | undefined;
try {
await pfs.writeFile(testFile, 'Hello World');
} catch (error) {
expectedError = error;
}
assert.ok(expectedError);
await pfs.rimraf(parentDir);
});
test('writeFileSync', async () => {
const id = uuid.generateUuid();
const parentDir = path.join(os.tmpdir(), 'vsctests', id);
const newDir = path.join(parentDir, 'pfs', id);
const testFile = path.join(newDir, 'flushed.txt');
await pfs.mkdirp(newDir, 493);
assert.ok(fs.existsSync(newDir));
pfs.writeFileSync(testFile, 'Hello World');
assert.equal(fs.readFileSync(testFile), 'Hello World');
const largeString = (new Array(100 * 1024)).join('Large String\n');
pfs.writeFileSync(testFile, largeString);
assert.equal(fs.readFileSync(testFile), largeString);
await pfs.rimraf(parentDir);
});
});

View File

@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* 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 * as net from 'net';
import * as ports from 'vs/base/node/ports';
suite('Ports', () => {
test('Finds a free port (no timeout)', function (done) {
this.timeout(1000 * 10); // higher timeout for this test
if (process.env['VSCODE_PID']) {
return done(); // this test fails when run from within VS Code
}
// get an initial freeport >= 7000
ports.findFreePort(7000, 100, 300000).then(initialPort => {
assert.ok(initialPort >= 7000);
// create a server to block this port
const server = net.createServer();
server.listen(initialPort, undefined, undefined, () => {
// once listening, find another free port and assert that the port is different from the opened one
ports.findFreePort(7000, 50, 300000).then(freePort => {
assert.ok(freePort >= 7000 && freePort !== initialPort);
server.close();
done();
}, err => done(err));
});
}, err => done(err));
});
});

View File

@@ -0,0 +1,14 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as processes from 'vs/base/node/processes';
const sender = processes.createQueuedSender(<any>process);
process.on('message', msg => {
sender.send(msg);
});
sender.send('ready');

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as processes from 'vs/base/node/processes';
const sender = processes.createQueuedSender(<any>process);
process.on('message', msg => {
sender.send(msg);
sender.send(msg);
sender.send(msg);
sender.send('done');
});
sender.send('ready');

View File

@@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* 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 * as cp from 'child_process';
import * as objects from 'vs/base/common/objects';
import * as platform from 'vs/base/common/platform';
import * as processes from 'vs/base/node/processes';
import { getPathFromAmdModule } from 'vs/base/common/amd';
function fork(id: string): cp.ChildProcess {
const opts: any = {
env: objects.mixin(objects.deepClone(process.env), {
AMD_ENTRYPOINT: id,
PIPE_LOGGING: 'true',
VERBOSE_LOGGING: true
})
};
return cp.fork(getPathFromAmdModule(require, 'bootstrap-fork'), ['--type=processTests'], opts);
}
suite('Processes', () => {
test('buffered sending - simple data', function (done: () => void) {
if (process.env['VSCODE_PID']) {
return done(); // this test fails when run from within VS Code
}
const child = fork('vs/base/test/node/processes/fixtures/fork');
const sender = processes.createQueuedSender(child);
let counter = 0;
const msg1 = 'Hello One';
const msg2 = 'Hello Two';
const msg3 = 'Hello Three';
child.on('message', msgFromChild => {
if (msgFromChild === 'ready') {
sender.send(msg1);
sender.send(msg2);
sender.send(msg3);
} else {
counter++;
if (counter === 1) {
assert.equal(msgFromChild, msg1);
} else if (counter === 2) {
assert.equal(msgFromChild, msg2);
} else if (counter === 3) {
assert.equal(msgFromChild, msg3);
child.kill();
done();
}
}
});
});
test('buffered sending - lots of data (potential deadlock on win32)', function (done: () => void) {
if (!platform.isWindows || process.env['VSCODE_PID']) {
return done(); // test is only relevant for Windows and seems to crash randomly on some Linux builds
}
const child = fork('vs/base/test/node/processes/fixtures/fork_large');
const sender = processes.createQueuedSender(child);
const largeObj = Object.create(null);
for (let i = 0; i < 10000; i++) {
largeObj[i] = 'some data';
}
const msg = JSON.stringify(largeObj);
child.on('message', msgFromChild => {
if (msgFromChild === 'ready') {
sender.send(msg);
sender.send(msg);
sender.send(msg);
} else if (msgFromChild === 'done') {
child.kill();
done();
}
});
});
});

View File

@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/path';
import { generateUuid } from 'vs/base/common/uuid';
export function getRandomTestPath(tmpdir: string, ...segments: string[]): string {
return join(tmpdir, ...segments, generateUuid());
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
/*---------------------------------------------------------------------------------------------
* 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 { readFileSync } from 'fs';
import { getPathFromAmdModule } from 'vs/base/common/amd';
suite('URI - perf', function () {
let manyFileUris: URI[];
setup(function () {
manyFileUris = [];
let data = readFileSync(getPathFromAmdModule(require, './uri.test.data.txt')).toString();
let lines = data.split('\n');
for (let line of lines) {
manyFileUris.push(URI.file(line));
}
});
function perfTest(name: string, callback: Function) {
test(name, _done => {
let t1 = Date.now();
callback();
let d = Date.now() - t1;
console.log(`${name} took ${d}ms (${(d / manyFileUris.length).toPrecision(3)} ms/uri)`);
_done();
});
}
perfTest('toString', function () {
for (const uri of manyFileUris) {
let data = uri.toString();
assert.ok(data);
}
});
perfTest('toString(skipEncoding)', function () {
for (const uri of manyFileUris) {
let data = uri.toString(true);
assert.ok(data);
}
});
perfTest('fsPath', function () {
for (const uri of manyFileUris) {
let data = uri.fsPath;
assert.ok(data);
}
});
perfTest('toJSON', function () {
for (const uri of manyFileUris) {
let data = uri.toJSON();
assert.ok(data);
}
});
});

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* 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 * as path from 'vs/base/common/path';
import * as os from 'os';
import { extract } from 'vs/base/node/zip';
import { generateUuid } from 'vs/base/common/uuid';
import { rimraf, exists } from 'vs/base/node/pfs';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { createCancelablePromise } from 'vs/base/common/async';
const fixtures = getPathFromAmdModule(require, './fixtures');
suite('Zip', () => {
test('extract should handle directories', () => {
const fixture = path.join(fixtures, 'extract.zip');
const target = path.join(os.tmpdir(), generateUuid());
return createCancelablePromise(token => extract(fixture, target, {}, token)
.then(() => exists(path.join(target, 'extension')))
.then(exists => assert(exists))
.then(() => rimraf(target)));
});
});