chrome-devtools-frontend
Version:
Chrome DevTools UI
500 lines (443 loc) • 17 kB
text/typescript
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as Platform from './platform.js';
function comparator(a: number, b: number): number {
return a < b ? -1 : (a > b ? 1 : 0);
}
describe('ArrayUtilities', () => {
describe('removeElement', () => {
it('removes elements', () => {
const testCases = [
{input: [], expectedFirstOnlyTrue: [], expectedFirstOnlyFalse: []},
{input: [1], expectedFirstOnlyTrue: [1], expectedFirstOnlyFalse: [1]},
{
input: [1, 2, 3, 4, 5, 4, 3, 2, 1],
expectedFirstOnlyTrue: [1, 3, 4, 5, 4, 3, 2, 1],
expectedFirstOnlyFalse: [1, 3, 4, 5, 4, 3, 1],
},
{input: [2, 2, 2, 2, 2], expectedFirstOnlyTrue: [2, 2, 2, 2], expectedFirstOnlyFalse: []},
{input: [2, 2, 2, 1, 2, 2, 3, 2], expectedFirstOnlyTrue: [2, 2, 1, 2, 2, 3, 2], expectedFirstOnlyFalse: [1, 3]},
];
for (const testCase of testCases) {
const actualFirstOnlyTrue = [...testCase.input];
Platform.ArrayUtilities.removeElement(actualFirstOnlyTrue, 2, true);
assert.deepEqual(actualFirstOnlyTrue, testCase.expectedFirstOnlyTrue, 'Removing firstOnly (true) failed');
const actualFirstOnlyFalse = [...testCase.input];
Platform.ArrayUtilities.removeElement(actualFirstOnlyFalse, 2, false);
assert.deepEqual(actualFirstOnlyFalse, testCase.expectedFirstOnlyFalse, 'Removing firstOnly (false) failed');
}
});
});
const fixtures = [
[],
[1],
[2, 1],
[6, 4, 2, 7, 10, 15, 1],
[10, 44, 3, 6, 56, 66, 10, 55, 32, 56, 2, 5],
];
for (let i = 0; i < fixtures.length; i++) {
const fixture = fixtures[i];
it(`sorts ranges, fixture ${i}`, () => {
for (let left = 0, l = fixture.length - 1; left < l; ++left) {
for (let right = left, r = fixture.length; right < r; ++right) {
for (let first = left; first <= right; ++first) {
for (let count = 1, k = right - first + 1; count <= k; ++count) {
const actual = fixture.slice(0);
Platform.ArrayUtilities.sortRange(actual, comparator, left, right, first, first + count - 1);
assert.deepEqual(
fixture.slice(0, left), actual.slice(0, left), 'left ' + left + ' ' + right + ' ' + count);
assert.deepEqual(
fixture.slice(right + 1), actual.slice(right + 1), 'right ' + left + ' ' + right + ' ' + count);
const middle = fixture.slice(left, right + 1);
middle.sort(comparator);
assert.deepEqual(
middle.slice(first - left, first - left + count), actual.slice(first, first + count),
'sorted ' + left + ' ' + right + ' ' + first + ' ' + count);
const actualRest = actual.slice(first + count, right + 1);
actualRest.sort(comparator);
assert.deepEqual(
middle.slice(first - left + count), actualRest,
'unsorted ' + left + ' ' + right + ' ' + first + ' ' + count);
}
}
}
}
});
}
describe('binaryIndexOf', () => {
it('calculates the correct binary index', () => {
const fixtures = [
[],
[1],
[1, 10],
[1, 10, 11, 12, 13, 14, 100],
[-100, -50, 0, 50, 100],
[-100, -14, -13, -12, -11, -10, -1],
];
function testArray(array: number[]) {
function comparator(a: number, b: number) {
return a < b ? -1 : (a > b ? 1 : 0);
}
for (let i = -100; i <= 100; ++i) {
const reference = array.indexOf(i);
const actual = Platform.ArrayUtilities.binaryIndexOf(array, i, comparator);
assert.strictEqual(reference, actual);
}
}
for (const fixture of fixtures) {
testArray(fixture);
}
});
});
describe('merge and intersect', () => {
it('orders merge intersect', () => {
function comparator(a: number, b: number) {
return a - b;
}
function count(a: number[], x: number) {
return Platform.ArrayUtilities.upperBound(a, x, Platform.ArrayUtilities.DEFAULT_COMPARATOR) -
Platform.ArrayUtilities.lowerBound(a, x, Platform.ArrayUtilities.DEFAULT_COMPARATOR);
}
function testAll(a: number[], b: number[]) {
testOperation(a, b, Platform.ArrayUtilities.mergeOrdered(a, b, comparator), Math.max, 'U');
testOperation(a, b, Platform.ArrayUtilities.intersectOrdered(a, b, comparator), Math.min, 'x');
}
function testOperation(
a: number[], b: number[], actual: number[], checkOperation: (...values: number[]) => number, opName: string) {
const allValues = a.concat(b).concat(actual);
let expectedCount: number;
let actualCount: number;
for (let i = 0; i < allValues.length; ++i) {
const value = allValues[i];
expectedCount = checkOperation(count(a, value), count(b, value));
actualCount = count(actual, value);
assert.strictEqual(
expectedCount, actualCount,
'Incorrect result for value: ' + value + ' at [' + a + '] ' + opName + ' [' + b + '] -> [' + actual +
']');
}
const shallowCopy = [...actual];
assert.deepEqual(actual.sort(), shallowCopy, 'Result array is ordered');
}
const fixtures = new Map([
[[], []],
[[1], []],
[[1, 2, 2, 2, 3], []],
[[4, 5, 5, 8, 8], [1, 1, 1, 2, 6]],
[[1, 2, 2, 2, 2, 3, 3, 4], [2, 2, 2, 3, 3, 3, 3]],
[[1, 2, 3, 4, 5], [1, 2, 3]],
]);
for (const [a, b] of fixtures) {
testAll(a, b);
testAll(b, a);
}
});
});
describe('upperBound', () => {
it('finds the first object after the needle whose value is greater than the needle', async () => {
const input = [0, 1, 2, 3, 4, 5];
const index = Platform.ArrayUtilities.upperBound(input, 2, Platform.ArrayUtilities.DEFAULT_COMPARATOR);
assert.strictEqual(index, 3);
});
it('can take left and right params to alter the range', async () => {
const input = [0, 1, 2, 3, 4, 5];
const index = Platform.ArrayUtilities.upperBound(input, 2, Platform.ArrayUtilities.DEFAULT_COMPARATOR, 4, 6);
assert.strictEqual(index, 4);
});
it('can take a custom comparator to determine how to compare elements', async () => {
const input = [{time: 0, name: 'test1'}, {time: 6, name: 'test2'}];
const index = Platform.ArrayUtilities.upperBound(input, 2, (needle, element) => {
if (needle > element.time) {
return 1;
}
if (element.time > needle) {
return -1;
}
return 0;
});
assert.strictEqual(index, 1);
});
});
describe('lowerBound', () => {
it('finds the first object after the needle whose value is equal to or greater than the needle', async () => {
const input = [0, 1, 2, 3, 4, 5];
const index = Platform.ArrayUtilities.lowerBound(input, 2, Platform.ArrayUtilities.DEFAULT_COMPARATOR);
assert.strictEqual(index, 2);
});
it('can take left and right params to alter the range', async () => {
const input = [0, 1, 2, 3, 4, 5];
const index = Platform.ArrayUtilities.lowerBound(input, 2, Platform.ArrayUtilities.DEFAULT_COMPARATOR, 5, 6);
assert.strictEqual(index, 5);
});
it('can take a custom comparator to determine how to compare elements', async () => {
const input = [{time: 0, name: 'test1'}, {time: 2, name: 'test2'}, {time: 3, name: 'test3'}];
const index = Platform.ArrayUtilities.lowerBound(input, 2, (needle, element) => {
if (needle > element.time) {
return 1;
}
if (element.time > needle) {
return -1;
}
return 0;
});
assert.strictEqual(index, 1);
});
});
describe('Nearest', () => {
describe('Finding the last item where predicate is true', () => {
it('works with an even number of entries', () => {
const ascEntries = [{a: 1}, {a: 3}, {a: 3}, {a: 12}, {a: 13}, {a: 18}, {a: 23}, {a: 24}];
let nearest = Platform.ArrayUtilities.nearestIndexFromEnd(ascEntries, value => value.a < 7);
assert.strictEqual(nearest, 2);
const descEntries = [{a: 23}, {a: 18}, {a: 13}, {a: 12}, {a: 12}, {a: 3}, {a: 1}, {a: 0}];
nearest = Platform.ArrayUtilities.nearestIndexFromEnd(descEntries, value => value.a > 7);
assert.strictEqual(nearest, 4);
});
it('works with an odd number of entries', () => {
const ascEntries = [
{a: 1},
{a: 3},
{a: 12},
{a: 13},
{a: 18},
{a: 23},
{a: 23},
{a: 32},
{a: 33},
];
let nearest = Platform.ArrayUtilities.nearestIndexFromEnd(ascEntries, value => value.a < 31);
assert.strictEqual(nearest, 6);
const descEntries = [
{a: 32},
{a: 23},
{a: 18},
{a: 13},
{a: 12},
{a: 3},
{a: 3},
{a: 1},
];
nearest = Platform.ArrayUtilities.nearestIndexFromEnd(descEntries, value => value.a > 2);
assert.strictEqual(nearest, 6);
});
it('returns null if there are no matches at all', () => {
const ascEntries = [
{a: 1},
{a: 3},
{a: 12},
{a: 13},
{a: 18},
{a: 23},
{a: 32},
];
let zeroth = Platform.ArrayUtilities.nearestIndexFromEnd(ascEntries, value => value.a < 0);
assert.isNull(zeroth);
const descEntries = [
{a: 32},
{a: 23},
{a: 18},
{a: 13},
{a: 12},
{a: 3},
{a: 1},
];
zeroth = Platform.ArrayUtilities.nearestIndexFromEnd(descEntries, value => value.a > 40);
assert.isNull(zeroth);
});
it('works when the result is the last item', () => {
const ascEntries = [
{a: 1},
{a: 3},
{a: 12},
{a: 13},
{a: 18},
{a: 23},
{a: 32},
{a: 32},
];
let last = Platform.ArrayUtilities.nearestIndexFromEnd(ascEntries, value => value.a < 40);
assert.strictEqual(last, ascEntries.length - 1);
const descEntries = [
{a: 32},
{a: 23},
{a: 18},
{a: 13},
{a: 12},
{a: 3},
{a: 1},
{a: 1},
];
last = Platform.ArrayUtilities.nearestIndexFromEnd(descEntries, value => value.a > 0);
assert.strictEqual(last, descEntries.length - 1);
});
it('works on exact values', () => {
const ascEntries = [
{a: 1},
{a: 2},
{a: 3},
{a: 3},
{a: 4},
{a: 5},
{a: 6},
];
const predicateFunc = (value: {a: number}) => value.a <= 3;
// Odd number of entries.
// Note that the predicate is allowing an the exact match.
let nearest = Platform.ArrayUtilities.nearestIndexFromEnd(ascEntries, predicateFunc);
assert.strictEqual(nearest, 3);
// Even number of entries.
ascEntries.push({a: 7});
nearest = Platform.ArrayUtilities.nearestIndexFromEnd(ascEntries, predicateFunc);
assert.strictEqual(nearest, 3);
const descEntries = [
{a: 6},
{a: 5},
{a: 4},
{a: 3},
{a: 3},
{a: 2},
{a: 1},
];
// Note that the predicate is allowing an the exact match.
const gePredicate = (value: {a: number}) => value.a >= 3;
// Odd number of entries.
nearest = Platform.ArrayUtilities.nearestIndexFromEnd(descEntries, gePredicate);
assert.strictEqual(nearest, 4);
// Even number of entries.
descEntries.push({a: 7});
nearest = Platform.ArrayUtilities.nearestIndexFromEnd(descEntries, gePredicate);
assert.strictEqual(nearest, 4);
});
});
describe('Finding the first item in the array where predicate is true', () => {
it('works with an even number of entries', () => {
const ascEntries = [{a: 1}, {a: 3}, {a: 12}, {a: 12}, {a: 13}, {a: 18}, {a: 23}, {a: 24}];
let nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(ascEntries, value => value.a > 7);
assert.strictEqual(nearest, 2);
const descEntries = [{a: 23}, {a: 18}, {a: 13}, {a: 12}, {a: 12}, {a: 3}, {a: 1}, {a: 0}];
nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(descEntries, value => value.a < 13);
assert.strictEqual(nearest, 3);
});
it('works with an odd number of entries', () => {
const ascEntries = [
{a: 1},
{a: 3},
{a: 12},
{a: 13},
{a: 18},
{a: 23},
{a: 32},
{a: 32},
{a: 33},
];
let nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(ascEntries, value => value.a > 31);
assert.strictEqual(nearest, 6);
const descEntries = [
{a: 33},
{a: 32},
{a: 23},
{a: 23},
{a: 18},
{a: 23},
{a: 32},
{a: 3},
{a: 1},
];
nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(descEntries, value => value.a < 32);
assert.strictEqual(nearest, 2);
});
it('returns null if there are no matches at all', () => {
const entries = [
{a: 1},
{a: 3},
{a: 12},
{a: 13},
{a: 18},
{a: 23},
{a: 32},
];
const predicate = (value: {a: number}) => value.a > 33;
const nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(entries, predicate);
assert.isNull(nearest);
});
it('works when the result is the first item', () => {
const ascEntries = [
{a: 1},
{a: 1},
{a: 3},
{a: 12},
{a: 13},
{a: 18},
{a: 23},
{a: 32},
];
const greaterThanPredicate = (value: {a: number}) => value.a > 0;
let first = Platform.ArrayUtilities.nearestIndexFromBeginning(ascEntries, greaterThanPredicate);
assert.strictEqual(first, 0);
const descEntries = [
{a: 32},
{a: 32},
{a: 23},
{a: 18},
{a: 13},
{a: 12},
{a: 5},
{a: 5},
];
const predicate = (value: {a: number}) => value.a < 64;
first = Platform.ArrayUtilities.nearestIndexFromBeginning(descEntries, predicate);
assert.strictEqual(first, 0);
});
it('works on exact values', () => {
const ascEntries = [
{a: 1},
{a: 2},
{a: 3},
{a: 3},
{a: 4},
{a: 5},
{a: 6},
];
// Note that the predicate is allowing an the exact match.
const gePredicate = (value: {a: number}) => value.a >= 3;
// Even number of entries.
let nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(ascEntries, gePredicate);
assert.strictEqual(nearest, 2);
// Odd number of entries.
ascEntries.push({a: 7});
nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(ascEntries, gePredicate);
assert.strictEqual(nearest, 2);
const descEntries = [
{a: 6},
{a: 5},
{a: 4},
{a: 3},
{a: 3},
{a: 2},
{a: 1},
];
// Note that the predicate is allowing an the exact match.
const predicateFunc = (value: {a: number}) => value.a <= 3;
// Even number of entries.
nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(descEntries, predicateFunc);
assert.strictEqual(nearest, 3);
// Odd number of entries.
descEntries.push({a: 7});
nearest = Platform.ArrayUtilities.nearestIndexFromBeginning(descEntries, predicateFunc);
assert.strictEqual(nearest, 3);
});
});
});
describe('arrayDoesNotContainNullOrUndefined', () => {
it('should return false when array contains null', () => {
assert.isFalse(Platform.ArrayUtilities.arrayDoesNotContainNullOrUndefined([1, null, 2]));
});
it('should return false when array contains undefined', () => {
assert.isFalse(Platform.ArrayUtilities.arrayDoesNotContainNullOrUndefined([1, undefined, 2]));
});
it('should return true when array does not contain undefined and null', () => {
assert.isTrue(Platform.ArrayUtilities.arrayDoesNotContainNullOrUndefined([1, 2]));
});
});
});