Skip to content

Commit 8ac8fb0

Browse files
authored
Fix SortedMap's reverse iterator (without start key). (#6020)
* Fix SortedMap's reverse iterator (without start key). * Fix sorted map. * address comments.
1 parent 13c0895 commit 8ac8fb0

File tree

2 files changed

+79
-21
lines changed

2 files changed

+79
-21
lines changed

packages/firestore/src/util/sorted_map.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ export class SortedMapIterator<K, V> {
197197
while (!node.isEmpty()) {
198198
cmp = startKey ? comparator(node.key, startKey) : 1;
199199
// flip the comparison if we're going in reverse
200-
if (isReverse) {
200+
if (startKey && isReverse) {
201201
cmp *= -1;
202202
}
203203

packages/firestore/test/unit/util/sorted_map.test.ts

Lines changed: 78 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ import { expect } from 'chai';
1919

2020
import { primitiveComparator } from '../../../src/util/misc';
2121
import { forEach } from '../../../src/util/obj';
22-
import { LLRBNode, SortedMap } from '../../../src/util/sorted_map';
22+
import {
23+
LLRBNode,
24+
SortedMap,
25+
SortedMapIterator
26+
} from '../../../src/util/sorted_map';
2327

2428
function shuffle(arr: number[]): void {
2529
for (let i = arr.length - 1; i > 0; i--) {
@@ -32,6 +36,41 @@ function shuffle(arr: number[]): void {
3236
// Many of these were adapted from the mugs source code.
3337
// http://mads379.github.com/mugs/
3438
describe('SortedMap Tests', () => {
39+
/** Returns a map with (i, i) entries for i in [0, 9] inclusive */
40+
function getMapOfNumbersZeroToNine(): SortedMap<number, number> {
41+
const items: number[] = [];
42+
for (let i = 0; i < 10; i++) {
43+
items.push(i);
44+
}
45+
shuffle(items);
46+
47+
let map = new SortedMap<number, number>(primitiveComparator);
48+
for (let i = 0; i < 10; i++) {
49+
map = map.insert(items[i], items[i]);
50+
}
51+
return map;
52+
}
53+
54+
/**
55+
* Validates that the given iterator covers the given range by counting from
56+
* the given `from` argument to the given `to` argument (inclusive).
57+
*/
58+
function validateIteratorCoversRange(
59+
iterator: SortedMapIterator<number, number>,
60+
from: number,
61+
to: number
62+
): void {
63+
const countUp = from <= to;
64+
let expected = from;
65+
while (iterator.hasNext()) {
66+
const n = iterator.getNext();
67+
expect(n.key).to.equal(expected);
68+
expect(n.value).to.equal(expected);
69+
expected = countUp ? expected + 1 : expected - 1;
70+
}
71+
expect(expected).to.equal(countUp ? to + 1 : to - 1);
72+
}
73+
3574
it('Create node', () => {
3675
const map = new SortedMap(primitiveComparator).insert('key', 'value');
3776
expect(map.root.left.isEmpty()).to.equal(true);
@@ -405,27 +444,46 @@ describe('SortedMap Tests', () => {
405444
expect(() => iterator.getNext()).to.throw();
406445
});
407446

408-
it('SortedMapIterator test with 10 items.', () => {
409-
const items: number[] = [];
410-
for (let i = 0; i < 10; i++) {
411-
items.push(i);
412-
}
413-
shuffle(items);
447+
it('forward iterator without start key', () => {
448+
const iterator = getMapOfNumbersZeroToNine().getIterator();
449+
validateIteratorCoversRange(iterator, 0, 9);
450+
});
414451

415-
let map = new SortedMap(primitiveComparator);
416-
for (let i = 0; i < 10; i++) {
417-
map = map.insert(items[i], items[i]);
418-
}
452+
it('forward iterator with start key.', () => {
453+
const iterator = getMapOfNumbersZeroToNine().getIteratorFrom(5);
454+
validateIteratorCoversRange(iterator, 5, 9);
455+
});
419456

420-
const iterator = map.getIterator();
421-
let expected = 0;
422-
while (iterator.hasNext()) {
423-
const n = iterator.getNext();
424-
expect(n.key).to.equal(expected);
425-
expect(n.value).to.equal(expected);
426-
expected++;
427-
}
428-
expect(expected).to.equal(10);
457+
it('forward iterator with start key larger than max key', () => {
458+
const iterator = getMapOfNumbersZeroToNine().getIteratorFrom(50);
459+
expect(iterator.hasNext()).to.equal(false);
460+
expect(() => iterator.getNext()).to.throw();
461+
});
462+
463+
it('forward iterator with start key smaller than min key', () => {
464+
const iterator = getMapOfNumbersZeroToNine().getIteratorFrom(-50);
465+
validateIteratorCoversRange(iterator, 0, 9);
466+
});
467+
468+
it('reverse iterator without start key', () => {
469+
const iterator = getMapOfNumbersZeroToNine().getReverseIterator();
470+
validateIteratorCoversRange(iterator, 9, 0);
471+
});
472+
473+
it('reverse iterator with start key.', () => {
474+
const iterator = getMapOfNumbersZeroToNine().getReverseIteratorFrom(7);
475+
validateIteratorCoversRange(iterator, 7, 0);
476+
});
477+
478+
it('reverse iterator with start key smaller than min key', () => {
479+
const iterator = getMapOfNumbersZeroToNine().getReverseIteratorFrom(-50);
480+
expect(iterator.hasNext()).to.equal(false);
481+
expect(() => iterator.getNext()).to.throw();
482+
});
483+
484+
it('reverse iterator with start key larger than max key', () => {
485+
const iterator = getMapOfNumbersZeroToNine().getReverseIteratorFrom(50);
486+
validateIteratorCoversRange(iterator, 9, 0);
429487
});
430488

431489
it('SortedMap.indexOf returns index.', () => {

0 commit comments

Comments
 (0)