UNPKG

chrome-devtools-frontend

Version:
500 lines (443 loc) • 17 kB
// 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])); }); }); });