Squashed 'lib/vscode/' content from commit e5a624b788

git-subtree-dir: lib/vscode
git-subtree-split: e5a624b788d92b8d34d1392e4c4d9789406efe8f
This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
commit be3e823608
4649 changed files with 1311795 additions and 0 deletions

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));
}
});
});