mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
435 lines
11 KiB
TypeScript
435 lines
11 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2017 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { expect } from 'chai';
|
|
|
|
import { SortedMap, LLRBNode } from '../src/core/util/SortedMap';
|
|
|
|
import { shuffle } from './helpers/util';
|
|
|
|
// Many of these were adapted from the mugs source code.
|
|
// http://mads379.github.com/mugs/
|
|
describe('SortedMap Tests', () => {
|
|
const defaultCmp = function (a, b) {
|
|
if (a === b) {
|
|
return 0;
|
|
} else if (a < b) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
it('Create node', () => {
|
|
const map = new SortedMap(defaultCmp).insert('key', 'value');
|
|
expect((map as any).root_.left.isEmpty()).to.equal(true);
|
|
expect((map as any).root_.right.isEmpty()).to.equal(true);
|
|
});
|
|
|
|
it('You can search a map for a specific key', () => {
|
|
const map = new SortedMap(defaultCmp).insert(1, 1).insert(2, 2);
|
|
expect(map.get(1)).to.equal(1);
|
|
expect(map.get(2)).to.equal(2);
|
|
expect(map.get(3)).to.equal(null);
|
|
});
|
|
|
|
it('You can insert a new key/value pair into the tree', () => {
|
|
const map = new SortedMap(defaultCmp).insert(1, 1).insert(2, 2);
|
|
expect((map as any).root_.key).to.equal(2);
|
|
expect((map as any).root_.left.key).to.equal(1);
|
|
});
|
|
|
|
it('You can remove a key/value pair from the map', () => {
|
|
const map = new SortedMap(defaultCmp).insert(1, 1).insert(2, 2);
|
|
const newMap = map.remove(1);
|
|
expect(newMap.get(2)).to.equal(2);
|
|
expect(newMap.get(1)).to.equal(null);
|
|
});
|
|
|
|
it('More removals', () => {
|
|
const map = new SortedMap(defaultCmp)
|
|
.insert(1, 1)
|
|
.insert(50, 50)
|
|
.insert(3, 3)
|
|
.insert(4, 4)
|
|
.insert(7, 7)
|
|
.insert(9, 9)
|
|
.insert(20, 20)
|
|
.insert(18, 18)
|
|
.insert(2, 2)
|
|
.insert(71, 71)
|
|
.insert(42, 42)
|
|
.insert(88, 88);
|
|
|
|
const m1 = map.remove(7);
|
|
const m2 = m1.remove(3);
|
|
const m3 = m2.remove(1);
|
|
expect(m3.count()).to.equal(9);
|
|
expect(m3.get(1)).to.equal(null);
|
|
expect(m3.get(3)).to.equal(null);
|
|
expect(m3.get(7)).to.equal(null);
|
|
expect(m3.get(20)).to.equal(20);
|
|
});
|
|
|
|
it('Removal bug', () => {
|
|
const map = new SortedMap(defaultCmp)
|
|
.insert(1, 1)
|
|
.insert(2, 2)
|
|
.insert(3, 3);
|
|
|
|
const m1 = map.remove(2);
|
|
expect(m1.get(1)).to.equal(1);
|
|
expect(m1.get(3)).to.equal(3);
|
|
});
|
|
|
|
it('Test increasing', () => {
|
|
const total = 100;
|
|
let item;
|
|
let map = new SortedMap(defaultCmp).insert(1, 1);
|
|
for (item = 2; item < total; item++) {
|
|
map = map.insert(item, item);
|
|
}
|
|
expect((map as any).root_.checkMaxDepth_()).to.equal(true);
|
|
for (item = 2; item < total; item++) {
|
|
map = map.remove(item);
|
|
}
|
|
expect((map as any).root_.checkMaxDepth_()).to.equal(true);
|
|
});
|
|
|
|
it('The structure should be valid after insertion (1)', () => {
|
|
const map = new SortedMap(defaultCmp)
|
|
.insert(1, 1)
|
|
.insert(2, 2)
|
|
.insert(3, 3);
|
|
|
|
expect((map as any).root_.key).to.equal(2);
|
|
expect((map as any).root_.left.key).to.equal(1);
|
|
expect((map as any).root_.right.key).to.equal(3);
|
|
});
|
|
|
|
it('The structure should be valid after insertion (2)', () => {
|
|
const map = new SortedMap(defaultCmp)
|
|
.insert(1, 1)
|
|
.insert(2, 2)
|
|
.insert(3, 3)
|
|
.insert(4, 4)
|
|
.insert(5, 5)
|
|
.insert(6, 6)
|
|
.insert(7, 7)
|
|
.insert(8, 8)
|
|
.insert(9, 9)
|
|
.insert(10, 10)
|
|
.insert(11, 11)
|
|
.insert(12, 12);
|
|
|
|
expect(map.count()).to.equal(12);
|
|
expect((map as any).root_.checkMaxDepth_()).to.equal(true);
|
|
});
|
|
|
|
it('Rotate left leaves the tree in a valid state', () => {
|
|
const node = new LLRBNode(
|
|
4,
|
|
4,
|
|
false,
|
|
new LLRBNode(2, 2, false, null, null),
|
|
new LLRBNode(
|
|
7,
|
|
7,
|
|
true,
|
|
new LLRBNode(5, 5, false, null, null),
|
|
new LLRBNode(8, 8, false, null, null)
|
|
)
|
|
);
|
|
|
|
const node2 = (node as any).rotateLeft_();
|
|
expect(node2.count()).to.equal(5);
|
|
expect(node2.checkMaxDepth_()).to.equal(true);
|
|
});
|
|
|
|
it('Rotate right leaves the tree in a valid state', () => {
|
|
const node = new LLRBNode(
|
|
7,
|
|
7,
|
|
false,
|
|
new LLRBNode(
|
|
4,
|
|
4,
|
|
true,
|
|
new LLRBNode(2, 2, false, null, null),
|
|
new LLRBNode(5, 5, false, null, null)
|
|
),
|
|
new LLRBNode(8, 8, false, null, null)
|
|
);
|
|
|
|
const node2 = (node as any).rotateRight_();
|
|
expect(node2.count()).to.equal(5);
|
|
expect(node2.key).to.equal(4);
|
|
expect(node2.left.key).to.equal(2);
|
|
expect(node2.right.key).to.equal(7);
|
|
expect(node2.right.left.key).to.equal(5);
|
|
expect(node2.right.right.key).to.equal(8);
|
|
});
|
|
|
|
it('The structure should be valid after insertion (3)', () => {
|
|
const map = new SortedMap(defaultCmp)
|
|
.insert(1, 1)
|
|
.insert(50, 50)
|
|
.insert(3, 3)
|
|
.insert(4, 4)
|
|
.insert(7, 7)
|
|
.insert(9, 9);
|
|
|
|
expect(map.count()).to.equal(6);
|
|
expect((map as any).root_.checkMaxDepth_()).to.equal(true);
|
|
|
|
const m2 = map.insert(20, 20).insert(18, 18).insert(2, 2);
|
|
|
|
expect(m2.count()).to.equal(9);
|
|
expect((m2 as any).root_.checkMaxDepth_()).to.equal(true);
|
|
|
|
const m3 = m2.insert(71, 71).insert(42, 42).insert(88, 88);
|
|
|
|
expect(m3.count()).to.equal(12);
|
|
expect((m3 as any).root_.checkMaxDepth_()).to.equal(true);
|
|
});
|
|
|
|
it('you can overwrite a value', () => {
|
|
const map = new SortedMap(defaultCmp).insert(10, 10).insert(10, 8);
|
|
expect(map.get(10)).to.equal(8);
|
|
});
|
|
|
|
it('removing the last element returns an empty map', () => {
|
|
const map = new SortedMap(defaultCmp).insert(10, 10).remove(10);
|
|
expect(map.isEmpty()).to.equal(true);
|
|
});
|
|
|
|
it('empty .get()', () => {
|
|
const empty = new SortedMap(defaultCmp);
|
|
expect(empty.get('something')).to.equal(null);
|
|
});
|
|
|
|
it('empty .count()', () => {
|
|
const empty = new SortedMap(defaultCmp);
|
|
expect(empty.count()).to.equal(0);
|
|
});
|
|
|
|
it('empty .remove()', () => {
|
|
const empty = new SortedMap(defaultCmp);
|
|
expect(empty.remove('something').count()).to.equal(0);
|
|
});
|
|
|
|
it('.reverseTraversal() works.', () => {
|
|
const map = new SortedMap(defaultCmp)
|
|
.insert(1, 1)
|
|
.insert(5, 5)
|
|
.insert(3, 3)
|
|
.insert(2, 2)
|
|
.insert(4, 4);
|
|
let next = 5;
|
|
map.reverseTraversal((key, value) => {
|
|
expect(key).to.equal(next);
|
|
next--;
|
|
});
|
|
expect(next).to.equal(0);
|
|
});
|
|
|
|
it('insertion and removal of 100 items in random order.', () => {
|
|
const N = 100;
|
|
const toInsert = [],
|
|
toRemove = [];
|
|
for (let i = 0; i < N; i++) {
|
|
toInsert.push(i);
|
|
toRemove.push(i);
|
|
}
|
|
|
|
shuffle(toInsert);
|
|
shuffle(toRemove);
|
|
|
|
let map = new SortedMap(defaultCmp);
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
map = map.insert(toInsert[i], toInsert[i]);
|
|
expect((map as any).root_.checkMaxDepth_()).to.equal(true);
|
|
}
|
|
expect(map.count()).to.equal(N);
|
|
|
|
// Ensure order is correct.
|
|
let next = 0;
|
|
map.inorderTraversal((key, value) => {
|
|
expect(key).to.equal(next);
|
|
expect(value).to.equal(next);
|
|
next++;
|
|
});
|
|
expect(next).to.equal(N);
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
expect((map as any).root_.checkMaxDepth_()).to.equal(true);
|
|
map = map.remove(toRemove[i]);
|
|
}
|
|
expect(map.count()).to.equal(0);
|
|
});
|
|
|
|
// A little perf test for convenient benchmarking.
|
|
xit('Perf', () => {
|
|
for (let j = 0; j < 5; j++) {
|
|
let map = new SortedMap(defaultCmp);
|
|
const start = new Date().getTime();
|
|
for (let i = 0; i < 50000; i++) {
|
|
map = map.insert(i, i);
|
|
}
|
|
|
|
for (let i = 0; i < 50000; i++) {
|
|
map = map.remove(i);
|
|
}
|
|
const end = new Date().getTime();
|
|
// console.log(end-start);
|
|
}
|
|
});
|
|
|
|
xit('Perf: Insertion and removal with various # of items.', () => {
|
|
const verifyTraversal = function (map, max) {
|
|
let next = 0;
|
|
map.inorderTraversal((key, value) => {
|
|
expect(key).to.equal(next);
|
|
expect(value).to.equal(next);
|
|
next++;
|
|
});
|
|
expect(next).to.equal(max);
|
|
};
|
|
|
|
for (let N = 10; N <= 100000; N *= 10) {
|
|
const toInsert = [],
|
|
toRemove = [];
|
|
for (let i = 0; i < N; i++) {
|
|
toInsert.push(i);
|
|
toRemove.push(i);
|
|
}
|
|
|
|
shuffle(toInsert);
|
|
shuffle(toRemove);
|
|
|
|
let map = new SortedMap(defaultCmp);
|
|
|
|
const start = new Date().getTime();
|
|
for (let i = 0; i < N; i++) {
|
|
map = map.insert(toInsert[i], toInsert[i]);
|
|
}
|
|
|
|
// Ensure order is correct.
|
|
verifyTraversal(map, N);
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
map = map.remove(toRemove[i]);
|
|
}
|
|
|
|
const elapsed = new Date().getTime() - start;
|
|
// console.log(N + ": " +elapsed);
|
|
}
|
|
});
|
|
|
|
xit('Perf: Comparison with {}: Insertion and removal with various # of items.', () => {
|
|
const verifyTraversal = function (tree, max) {
|
|
const keys = [];
|
|
for (const k of Object.keys(tree)) {
|
|
keys.push(k);
|
|
}
|
|
|
|
keys.sort();
|
|
expect(keys.length).to.equal(max);
|
|
for (let i = 0; i < max; i++) {
|
|
expect(tree[i]).to.equal(i);
|
|
}
|
|
};
|
|
|
|
for (let N = 10; N <= 100000; N *= 10) {
|
|
const toInsert = [],
|
|
toRemove = [];
|
|
for (let i = 0; i < N; i++) {
|
|
toInsert.push(i);
|
|
toRemove.push(i);
|
|
}
|
|
|
|
shuffle(toInsert);
|
|
shuffle(toRemove);
|
|
|
|
const tree = {};
|
|
|
|
const start = new Date().getTime();
|
|
for (let i = 0; i < N; i++) {
|
|
tree[i] = i;
|
|
}
|
|
|
|
// Ensure order is correct.
|
|
//verifyTraversal(tree, N);
|
|
|
|
for (let i = 0; i < N; i++) {
|
|
delete tree[i];
|
|
}
|
|
|
|
const elapsed = new Date().getTime() - start;
|
|
// console.log(N + ": " +elapsed);
|
|
}
|
|
});
|
|
|
|
it('SortedMapIterator empty test.', () => {
|
|
const map = new SortedMap(defaultCmp);
|
|
const iterator = map.getIterator();
|
|
expect(iterator.getNext()).to.equal(null);
|
|
});
|
|
|
|
it('SortedMapIterator test with 10 items.', () => {
|
|
const items = [];
|
|
for (let i = 0; i < 10; i++) {
|
|
items.push(i);
|
|
}
|
|
shuffle(items);
|
|
|
|
let map = new SortedMap(defaultCmp);
|
|
for (let i = 0; i < 10; i++) {
|
|
map = map.insert(items[i], items[i]);
|
|
}
|
|
|
|
const iterator = map.getIterator();
|
|
let n = iterator.getNext() as any,
|
|
expected = 0;
|
|
while (n !== null) {
|
|
expect(n.key).to.equal(expected);
|
|
expect(n.value).to.equal(expected);
|
|
expected++;
|
|
n = iterator.getNext();
|
|
}
|
|
expect(expected).to.equal(10);
|
|
});
|
|
|
|
it('SortedMap.getPredecessorKey works.', () => {
|
|
const map = new SortedMap(defaultCmp)
|
|
.insert(1, 1)
|
|
.insert(50, 50)
|
|
.insert(3, 3)
|
|
.insert(4, 4)
|
|
.insert(7, 7)
|
|
.insert(9, 9);
|
|
|
|
expect(map.getPredecessorKey(1)).to.equal(null);
|
|
expect(map.getPredecessorKey(3)).to.equal(1);
|
|
expect(map.getPredecessorKey(4)).to.equal(3);
|
|
expect(map.getPredecessorKey(7)).to.equal(4);
|
|
expect(map.getPredecessorKey(9)).to.equal(7);
|
|
expect(map.getPredecessorKey(50)).to.equal(9);
|
|
});
|
|
});
|