summaryrefslogtreecommitdiffstats
path: root/comm/mail/base/test/unit/test_treeSelection.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/base/test/unit/test_treeSelection.js')
-rw-r--r--comm/mail/base/test/unit/test_treeSelection.js581
1 files changed, 581 insertions, 0 deletions
diff --git a/comm/mail/base/test/unit/test_treeSelection.js b/comm/mail/base/test/unit/test_treeSelection.js
new file mode 100644
index 0000000000..7e1b3193d1
--- /dev/null
+++ b/comm/mail/base/test/unit/test_treeSelection.js
@@ -0,0 +1,581 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { TreeSelection } = ChromeUtils.importESModule(
+ "chrome://messenger/content/tree-selection.mjs"
+);
+
+var fakeView = {
+ rowCount: 101,
+ selectionChanged() {},
+ QueryInterface: ChromeUtils.generateQI(["nsITreeView"]),
+};
+
+var sel = new TreeSelection(null);
+sel.view = fakeView;
+
+var tree = {
+ view: fakeView,
+
+ _invalidationCount: 0,
+ invalidate() {
+ this._invalidationCount++;
+ },
+ invalidateRange(startIndex, endIndex) {
+ for (let index = startIndex; index <= endIndex; index++) {
+ this.invalidateRow(index);
+ }
+ },
+ _invalidatedRows: [],
+ invalidateRow(index) {
+ this._invalidatedRows.push(index);
+ },
+
+ assertInvalidated() {
+ Assert.equal(this._invalidationCount, 1, "invalidated once");
+ this._invalidationCount = 0;
+ this.assertInvalidatedRows();
+ },
+ assertDidntInvalidate() {
+ Assert.equal(this._invalidationCount, 0, "didn't invalidate");
+ },
+ assertInvalidatedRows(expected) {
+ if (expected) {
+ this.assertDidntInvalidate();
+ } else {
+ expected = [];
+ }
+ let numericSort = (a, b) => a - b;
+ Assert.deepEqual(
+ this._invalidatedRows.sort(numericSort),
+ expected.sort(numericSort),
+ "invalidated rows"
+ );
+ this._invalidatedRows.length = 0;
+ },
+};
+sel.tree = tree;
+
+function createRangeArray(low, high) {
+ let array = [];
+ for (let i = low; i <= high; i++) {
+ array.push(i);
+ }
+ return array;
+}
+
+function assertSelectionRanges(expected) {
+ Assert.deepEqual(sel._ranges, expected, "selected ranges");
+}
+
+function assertCurrentIndex(index) {
+ Assert.equal(sel.currentIndex, index, `current index should be ${index}`);
+}
+
+function assertShiftPivot(index) {
+ Assert.equal(
+ sel.shiftSelectPivot,
+ index,
+ `shift select pivot should be ${index}`
+ );
+}
+
+function assertSelected(index) {
+ Assert.ok(sel.isSelected(index), `${index} should be selected`);
+}
+
+function assertNotSelected(index) {
+ Assert.ok(!sel.isSelected(index), `${index} should not be selected`);
+}
+
+function run_test() {
+ // -- select
+ sel.select(1);
+ tree.assertInvalidatedRows([1]);
+ assertSelected(1);
+ assertNotSelected(0);
+ assertNotSelected(2);
+ assertSelectionRanges([[1, 1]]);
+ assertCurrentIndex(1);
+
+ sel.select(2);
+ tree.assertInvalidatedRows([1, 2]);
+ assertSelected(2);
+ assertNotSelected(1);
+ assertNotSelected(3);
+ assertSelectionRanges([[2, 2]]);
+ assertCurrentIndex(2);
+
+ // -- clearSelection
+ sel.clearSelection();
+ tree.assertInvalidatedRows([2]);
+ assertSelectionRanges([]);
+ assertCurrentIndex(2); // should still be the same...
+
+ // -- toggleSelect
+ // start from nothing
+ sel.clearSelection();
+ tree.assertInvalidatedRows([]);
+ sel.toggleSelect(1);
+ tree.assertInvalidatedRows([1]);
+ assertSelectionRanges([[1, 1]]);
+ assertCurrentIndex(1);
+
+ // lower fusion
+ sel.select(2);
+ tree.assertInvalidatedRows([1, 2]);
+ sel.toggleSelect(1);
+ tree.assertInvalidatedRows([1]);
+ assertSelectionRanges([[1, 2]]);
+ assertCurrentIndex(1);
+
+ // upper fusion
+ sel.toggleSelect(3);
+ tree.assertInvalidatedRows([3]);
+ assertSelectionRanges([[1, 3]]);
+ assertCurrentIndex(3);
+
+ // splitting
+ sel.toggleSelect(2);
+ tree.assertInvalidatedRows([2]);
+ assertSelectionRanges([
+ [1, 1],
+ [3, 3],
+ ]);
+ assertSelected(1);
+ assertSelected(3);
+ assertNotSelected(0);
+ assertNotSelected(2);
+ assertNotSelected(4);
+ assertCurrentIndex(2);
+
+ // merge
+ sel.toggleSelect(2);
+ tree.assertInvalidatedRows([2]);
+ assertSelectionRanges([[1, 3]]);
+ assertCurrentIndex(2);
+
+ // lower shrinkage
+ sel.toggleSelect(1);
+ tree.assertInvalidatedRows([1]);
+ assertSelectionRanges([[2, 3]]);
+ assertCurrentIndex(1);
+
+ // upper shrinkage
+ sel.toggleSelect(3);
+ tree.assertInvalidatedRows([3]);
+ assertSelectionRanges([[2, 2]]);
+ assertCurrentIndex(3);
+
+ // nukage
+ sel.toggleSelect(2);
+ tree.assertInvalidatedRows([2]);
+ assertSelectionRanges([]);
+ assertCurrentIndex(2);
+
+ // -- rangedSelect
+ // simple non-augment
+ sel.rangedSelect(0, 0, false);
+ tree.assertInvalidatedRows([0]);
+ assertSelectionRanges([[0, 0]]);
+ assertShiftPivot(0);
+ assertCurrentIndex(0);
+
+ // slightly less simple non-augment
+ sel.rangedSelect(2, 4, false);
+ tree.assertInvalidatedRows([0, 2, 3, 4]);
+ assertSelectionRanges([[2, 4]]);
+ assertShiftPivot(2);
+ assertCurrentIndex(4);
+
+ // higher distinct range
+ sel.rangedSelect(7, 9, true);
+ tree.assertInvalidatedRows([7, 8, 9]);
+ assertSelectionRanges([
+ [2, 4],
+ [7, 9],
+ ]);
+ assertShiftPivot(7);
+ assertCurrentIndex(9);
+
+ // lower distinct range
+ sel.rangedSelect(0, 0, true);
+ tree.assertInvalidatedRows([0]);
+ assertSelectionRanges([
+ [0, 0],
+ [2, 4],
+ [7, 9],
+ ]);
+ assertShiftPivot(0);
+ assertCurrentIndex(0);
+
+ // lower fusion
+ sel.rangedSelect(6, 6, true);
+ tree.assertInvalidatedRows([6, 7, 8, 9]); // Ideally this would just be 6.
+ assertSelectionRanges([
+ [0, 0],
+ [2, 4],
+ [6, 9],
+ ]);
+ assertShiftPivot(6);
+ assertCurrentIndex(6);
+
+ // upper fusion
+ sel.rangedSelect(10, 11, true);
+ tree.assertInvalidatedRows([6, 7, 8, 9, 10, 11]); // 10, 11
+ assertSelectionRanges([
+ [0, 0],
+ [2, 4],
+ [6, 11],
+ ]);
+ assertShiftPivot(10);
+ assertCurrentIndex(11);
+
+ // notch merge
+ sel.rangedSelect(5, 5, true);
+ tree.assertInvalidatedRows([2, 3, 4, 5, 6, 7, 8, 9, 10, 11]); // 5
+ assertSelectionRanges([
+ [0, 0],
+ [2, 11],
+ ]);
+ assertShiftPivot(5);
+ assertCurrentIndex(5);
+
+ // ambiguous consume with merge
+ sel.rangedSelect(0, 5, true);
+ tree.assertInvalidatedRows(createRangeArray(0, 11)); // 1
+ assertSelectionRanges([[0, 11]]);
+ assertShiftPivot(0);
+ assertCurrentIndex(5);
+
+ // aligned consumption
+ sel.rangedSelect(0, 15, true);
+ tree.assertInvalidatedRows(createRangeArray(0, 15)); // 12, 13, 14, 15
+ assertSelectionRanges([[0, 15]]);
+ assertShiftPivot(0);
+ assertCurrentIndex(15);
+
+ // excessive consumption
+ sel.rangedSelect(5, 7, false);
+ tree.assertInvalidatedRows(createRangeArray(0, 15)); // 0 to 4, 8 to 15
+ sel.rangedSelect(3, 10, true);
+ tree.assertInvalidatedRows([3, 4, 5, 6, 7, 8, 9, 10]); // 3, 4, 8, 9, 10
+ assertSelectionRanges([[3, 10]]);
+ assertShiftPivot(3);
+ assertCurrentIndex(10);
+
+ // overlap merge
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows([3, 4, 5, 6, 7, 8, 9, 10]); // 3, 4
+ sel.rangedSelect(15, 20, true);
+ tree.assertInvalidatedRows([15, 16, 17, 18, 19, 20]);
+ sel.rangedSelect(7, 17, true);
+ tree.assertInvalidatedRows(createRangeArray(5, 20)); // 11, 12, 13, 14
+ assertSelectionRanges([[5, 20]]);
+ assertShiftPivot(7);
+ assertCurrentIndex(17);
+
+ // big merge and consume
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows(createRangeArray(5, 20)); // 11 to 20
+ sel.rangedSelect(15, 20, true);
+ tree.assertInvalidatedRows([15, 16, 17, 18, 19, 20]);
+ sel.rangedSelect(25, 30, true);
+ tree.assertInvalidatedRows([25, 26, 27, 28, 29, 30]);
+ sel.rangedSelect(35, 40, true);
+ tree.assertInvalidatedRows([35, 36, 37, 38, 39, 40]);
+ sel.rangedSelect(7, 37, true);
+ tree.assertInvalidatedRows(createRangeArray(5, 40)); // 11 to 14, 21 to 24, 31 to 34
+ assertSelectionRanges([[5, 40]]);
+ assertShiftPivot(7);
+ assertCurrentIndex(37);
+
+ // broad lower fusion
+ sel.rangedSelect(10, 20, false);
+ tree.assertInvalidatedRows(createRangeArray(5, 40)); // 5 to 9, 21 to 40
+ sel.rangedSelect(3, 15, true);
+ tree.assertInvalidatedRows(createRangeArray(3, 20)); // 3 to 9
+ assertSelectionRanges([[3, 20]]);
+ assertShiftPivot(3);
+ assertCurrentIndex(15);
+
+ // -- clearRange
+ sel.rangedSelect(10, 30, false);
+ tree.assertInvalidatedRows(createRangeArray(3, 30)); // 3 to 9, 21 to 30
+
+ // irrelevant low
+ sel.clearRange(0, 5);
+ tree.assertInvalidatedRows([]);
+ assertSelectionRanges([[10, 30]]);
+
+ // irrelevant high
+ sel.clearRange(40, 45);
+ tree.assertInvalidatedRows([]);
+ assertSelectionRanges([[10, 30]]);
+
+ // lower shrinkage tight
+ sel.clearRange(10, 10);
+ tree.assertInvalidatedRows([10]);
+ assertSelectionRanges([[11, 30]]);
+
+ // lower shrinkage broad
+ sel.clearRange(0, 13);
+ tree.assertInvalidatedRows(createRangeArray(0, 13)); // 11, 12, 13
+ assertSelectionRanges([[14, 30]]);
+
+ // upper shrinkage tight
+ sel.clearRange(30, 30);
+ tree.assertInvalidatedRows([30]);
+ assertSelectionRanges([[14, 29]]);
+
+ // upper shrinkage broad
+ sel.clearRange(27, 50);
+ tree.assertInvalidatedRows(createRangeArray(27, 50)); // 27, 28, 29
+ assertSelectionRanges([[14, 26]]);
+
+ // split tight
+ sel.clearRange(20, 20);
+ tree.assertInvalidatedRows([20]);
+ assertSelectionRanges([
+ [14, 19],
+ [21, 26],
+ ]);
+
+ // split broad
+ sel.toggleSelect(20);
+ tree.assertInvalidatedRows([20]);
+ sel.clearRange(19, 21);
+ tree.assertInvalidatedRows([19, 20, 21]);
+ assertSelectionRanges([
+ [14, 18],
+ [22, 26],
+ ]);
+
+ // hit two with tight shrinkage
+ sel.clearRange(18, 22);
+ tree.assertInvalidatedRows([18, 19, 20, 21, 22]); // 18, 22
+ assertSelectionRanges([
+ [14, 17],
+ [23, 26],
+ ]);
+
+ // hit two with broad shrinkage
+ sel.clearRange(15, 25);
+ tree.assertInvalidatedRows(createRangeArray(15, 25)); // 15, 16, 17, 23, 24, 25
+ assertSelectionRanges([
+ [14, 14],
+ [26, 26],
+ ]);
+
+ // obliterate
+ sel.clearRange(0, 100);
+ tree.assertInvalidatedRows(createRangeArray(0, 100)); // 14, 26
+ assertSelectionRanges([]);
+
+ // multi-obliterate
+ sel.rangedSelect(10, 20, true);
+ tree.assertInvalidatedRows(createRangeArray(10, 20));
+ sel.rangedSelect(30, 40, true);
+ tree.assertInvalidatedRows(createRangeArray(30, 40));
+ sel.clearRange(0, 100);
+ tree.assertInvalidatedRows(createRangeArray(0, 100)); // 10 to 20, 30 to 40
+ assertSelectionRanges([]);
+
+ // obliterate with shrinkage
+ sel.rangedSelect(5, 10, true);
+ tree.assertInvalidatedRows([5, 6, 7, 8, 9, 10]);
+ sel.rangedSelect(15, 20, true);
+ tree.assertInvalidatedRows([15, 16, 17, 18, 19, 20]);
+ sel.rangedSelect(25, 30, true);
+ tree.assertInvalidatedRows([25, 26, 27, 28, 29, 30]);
+ sel.rangedSelect(35, 40, true);
+ tree.assertInvalidatedRows([35, 36, 37, 38, 39, 40]);
+ sel.clearRange(7, 37);
+ tree.assertInvalidatedRows(createRangeArray(7, 37)); // 7 to 10, 15 to 20, 25 to 30, 35 to 37
+ assertSelectionRanges([
+ [5, 6],
+ [38, 40],
+ ]);
+
+ // -- selectAll
+ sel.selectAll();
+ tree.assertInvalidated();
+ assertSelectionRanges([[0, 100]]);
+
+ // -- adjustSelection
+ // bump due to addition on simple select
+ sel.select(5);
+ tree.assertInvalidatedRows(createRangeArray(0, 100));
+ sel.adjustSelection(5, 1);
+ tree.assertInvalidatedRows(createRangeArray(5, 100));
+ assertSelectionRanges([[6, 6]]);
+ assertCurrentIndex(6);
+
+ sel.select(5);
+ tree.assertInvalidatedRows([5, 6]);
+ sel.adjustSelection(0, 1);
+ tree.assertInvalidatedRows(createRangeArray(0, 100));
+ assertSelectionRanges([[6, 6]]);
+ assertCurrentIndex(6);
+
+ // bump due to addition on ranged simple select
+ sel.rangedSelect(5, 5, false);
+ tree.assertInvalidatedRows([5, 6]);
+ sel.adjustSelection(5, 1);
+ tree.assertInvalidatedRows(createRangeArray(5, 100));
+ assertSelectionRanges([[6, 6]]);
+ assertShiftPivot(6);
+ assertCurrentIndex(6);
+
+ sel.rangedSelect(5, 5, false);
+ tree.assertInvalidatedRows([5, 6]);
+ sel.adjustSelection(0, 1);
+ tree.assertInvalidatedRows(createRangeArray(0, 100));
+ assertSelectionRanges([[6, 6]]);
+ assertShiftPivot(6);
+ assertCurrentIndex(6);
+
+ // bump due to addition on ranged select
+ sel.rangedSelect(5, 7, false);
+ tree.assertInvalidatedRows([5, 6, 7]);
+ sel.adjustSelection(5, 1);
+ tree.assertInvalidatedRows(createRangeArray(5, 100));
+ assertSelectionRanges([[6, 8]]);
+ assertShiftPivot(6);
+ assertCurrentIndex(8);
+
+ // no-op with addition
+ sel.rangedSelect(0, 3, false);
+ tree.assertInvalidatedRows([0, 1, 2, 3, 6, 7, 8]);
+ sel.adjustSelection(10, 1);
+ tree.assertInvalidatedRows(createRangeArray(10, 100));
+ assertSelectionRanges([[0, 3]]);
+ assertShiftPivot(0);
+ assertCurrentIndex(3);
+
+ // split due to addition
+ sel.rangedSelect(5, 6, false);
+ tree.assertInvalidatedRows([0, 1, 2, 3, 5, 6]);
+ sel.adjustSelection(6, 1);
+ tree.assertInvalidatedRows(createRangeArray(6, 100));
+ assertSelectionRanges([
+ [5, 5],
+ [7, 7],
+ ]);
+ assertShiftPivot(5);
+ assertCurrentIndex(7);
+
+ // shift due to removal on simple select
+ sel.select(5);
+ tree.assertInvalidatedRows([5, 7]);
+ sel.adjustSelection(0, -1);
+ tree.assertInvalidatedRows(createRangeArray(0, 100));
+ assertSelectionRanges([[4, 4]]);
+ assertCurrentIndex(4);
+
+ // shift due to removal on ranged simple select
+ sel.rangedSelect(5, 5, false);
+ tree.assertInvalidatedRows([4, 5]);
+ sel.adjustSelection(0, -1);
+ tree.assertInvalidatedRows(createRangeArray(0, 100));
+ assertSelectionRanges([[4, 4]]);
+ assertShiftPivot(4);
+ assertCurrentIndex(4);
+
+ // nuked due to removal on simple select
+ sel.select(5);
+ tree.assertInvalidatedRows([4, 5]);
+ sel.adjustSelection(5, -1);
+ tree.assertInvalidatedRows(createRangeArray(5, 100));
+ assertSelectionRanges([]);
+ assertCurrentIndex(-1);
+
+ // upper tight shrinkage due to removal
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows([5, 6, 7, 8, 9, 10]);
+ sel.adjustSelection(10, -1);
+ tree.assertInvalidatedRows(createRangeArray(10, 100));
+ assertSelectionRanges([[5, 9]]);
+ assertShiftPivot(5);
+ assertCurrentIndex(-1);
+
+ // upper broad shrinkage due to removal
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows([5, 6, 7, 8, 9, 10]);
+ sel.adjustSelection(6, -10);
+ tree.assertInvalidatedRows(createRangeArray(6, 100));
+ assertSelectionRanges([[5, 5]]);
+ assertShiftPivot(5);
+ assertCurrentIndex(-1);
+
+ // lower tight shrinkage due to removal
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows([5, 6, 7, 8, 9, 10]);
+ sel.adjustSelection(5, -1);
+ tree.assertInvalidatedRows(createRangeArray(5, 100));
+ assertSelectionRanges([[5, 9]]);
+ assertShiftPivot(-1);
+ assertCurrentIndex(9);
+
+ // lower broad shrinkage due to removal
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows([5, 6, 7, 8, 9, 10]);
+ sel.adjustSelection(0, -10);
+ tree.assertInvalidatedRows(createRangeArray(0, 100));
+ assertSelectionRanges([[0, 0]]);
+ assertShiftPivot(-1);
+ assertCurrentIndex(0);
+
+ // tight nuke due to removal
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows([0, 5, 6, 7, 8, 9, 10]);
+ sel.adjustSelection(5, -6);
+ tree.assertInvalidatedRows(createRangeArray(5, 100));
+ assertSelectionRanges([]);
+ assertShiftPivot(-1);
+ assertCurrentIndex(-1);
+
+ // broad nuke due to removal
+ sel.rangedSelect(5, 10, false);
+ tree.assertInvalidatedRows([5, 6, 7, 8, 9, 10]);
+ sel.adjustSelection(0, -20);
+ tree.assertInvalidatedRows(createRangeArray(0, 100));
+ assertSelectionRanges([]);
+ assertShiftPivot(-1);
+ assertCurrentIndex(-1);
+
+ // duplicateSelection (please keep this right at the end, as this modifies
+ // sel)
+ // no guarantees for the shift pivot yet, so don't test that
+ let oldSel = sel;
+ let newSel = new TreeSelection(null);
+ newSel.view = fakeView;
+ // multiple selections
+ oldSel.rangedSelect(1, 3, false);
+ oldSel.rangedSelect(5, 5, true);
+ oldSel.rangedSelect(10, 10, true);
+ oldSel.rangedSelect(6, 7, true);
+
+ oldSel.duplicateSelection(newSel);
+ // from now on we're only going to be checking newSel
+ sel = newSel;
+ assertSelectionRanges([
+ [1, 3],
+ [5, 7],
+ [10, 10],
+ ]);
+ assertCurrentIndex(7);
+
+ // single selection
+ oldSel.select(4);
+ oldSel.duplicateSelection(newSel);
+ assertSelectionRanges([[4, 4]]);
+ assertCurrentIndex(4);
+
+ // nothing selected
+ oldSel.clearSelection();
+ oldSel.duplicateSelection(newSel);
+ assertSelectionRanges([]);
+ assertCurrentIndex(4);
+}