/* 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/. */
// The "standard" AVL interface, `class AvlTree` at the end of // js/src/ds/AvlTree.h, is too restrictive to allow proper testing of the AVL // tree internals. In particular it disallows insertion of duplicate values // and removal of non-present values, and lacks various secondary functions // such as for counting the number of nodes. // // So, for testing, we wrap an alternative interface `AvlTreeTestIF` around // the core implementation.
template <class T, class C> class AvlTreeTestIF : public AvlTreeImpl<T, C> { // Shorthands for names in the implementation (parent) class. using Impl = AvlTreeImpl<T, C>; using ImplTag = typename AvlTreeImpl<T, C>::Tag; using ImplNode = typename AvlTreeImpl<T, C>::Node; using ImplResult = typename AvlTreeImpl<T, C>::Result; using ImplNodeAndResult = typename AvlTreeImpl<T, C>::NodeAndResult;
bool TreeIsPlausible(const AvlTreeTestIF<CmpInt, CmpInt>& tree, const std::set<int>& should_be_in_tree, int UNIV_MIN, int UNIV_MAX) { // Same cardinality
size_t n_in_set = should_be_in_tree.size();
size_t n_in_tree = tree.testSize(); if (n_in_set != n_in_tree) { returnfalse;
}
// Tree is not wildly out of balance. Depth should not exceed 1.44 * // log2(size).
size_t tree_depth = tree.testDepth();
size_t log2_size = 0;
{
size_t n = n_in_tree; while (n > 0) {
n = n >> 1;
log2_size += 1;
}
} // Actually a tighter limit than stated above. For these test cases, the // tree is either perfectly balanced or within one level of being so (hence // the +1). if (tree_depth > log2_size + 1) { returnfalse;
}
// Check that everything that should be in the tree is in it, and vice // versa. for (int i = UNIV_MIN; i < UNIV_MAX; i++) { bool should_be_in = should_be_in_tree.find(i) != should_be_in_tree.end();
// Look it up with a null comparator (so `contains` compares // directly) bool is_in = tree.testContains(CmpInt(i)); if (is_in != should_be_in) { returnfalse;
}
}
// Add numbers to the tree, checking as we go. for (int i = UNIV_MIN; i < UNIV_MAX; i++) { // Idiotic but simple if (i % 3 != 0) { continue;
} bool was_added = tree.testInsert(CmpInt(i));
should_be_in_tree.insert(i);
CHECK(was_added);
CHECK(TreeIsPlausible(tree, should_be_in_tree, UNIV_MIN, UNIV_MAX));
}
// Then remove the middle half of the tree, also checking. for (int i = UNIV_MIN + UNIV_SIZE / 4; i < UNIV_MIN + 3 * (UNIV_SIZE / 4);
i++) { // Note that here, we're asking to delete a bunch of numbers that aren't // in the tree. It should remain valid throughout. bool was_removed = tree.testRemove(CmpInt(i)); bool should_have_been_removed = SetContains(should_be_in_tree, i);
CHECK(was_removed == should_have_been_removed);
should_be_in_tree.erase(i);
CHECK(TreeIsPlausible(tree, should_be_in_tree, UNIV_MIN, UNIV_MAX));
}
// Now add some numbers which are already in the tree. for (int i = UNIV_MIN; i < UNIV_MIN + UNIV_SIZE / 4; i++) { if (i % 3 != 0) { continue;
} bool was_added = tree.testInsert(CmpInt(i)); bool should_have_been_added = !SetContains(should_be_in_tree, i);
CHECK(was_added == should_have_been_added);
should_be_in_tree.insert(i);
CHECK(TreeIsPlausible(tree, should_be_in_tree, UNIV_MIN, UNIV_MAX));
}
// Then remove all numbers from the tree, in reverse order. for (int ir = UNIV_MIN; ir < UNIV_MAX; ir++) { int i = UNIV_MIN + (UNIV_MAX - ir); bool was_removed = tree.testRemove(CmpInt(i)); bool should_have_been_removed = SetContains(should_be_in_tree, i);
CHECK(was_removed == should_have_been_removed);
should_be_in_tree.erase(i);
CHECK(TreeIsPlausible(tree, should_be_in_tree, UNIV_MIN, UNIV_MAX));
}
// Now the tree should be empty.
CHECK(should_be_in_tree.empty());
CHECK(tree.testSize() == 0);
// Now delete some more stuff. Tree should still be empty :-) for (int i = UNIV_MIN + 10; i < UNIV_MIN + 100; i++) {
CHECK(should_be_in_tree.empty());
CHECK(tree.testSize() == 0); bool was_removed = tree.testRemove(CmpInt(i));
CHECK(!was_removed);
CHECK(TreeIsPlausible(tree, should_be_in_tree, UNIV_MIN, UNIV_MAX));
}
// The tree root should be NULL.
CHECK(tree.testGetRoot() == nullptr);
CHECK(tree.testGetFreeList() != nullptr);
// Check the freelist to the extent we can: it's non-circular, and the // elements look plausible. If it's not shorter than the specified length // then it must have a cycle, since the tests above won't have resulted in // more than 400 free nodes at the end.
CHECK(tree.testFreeListLooksValid(400 /*arbitrary*/));
// Test iteration, first in a tree with 9 nodes. This tests the general // case.
{
CHECK(tree.testSize() == 0); for (int i = 10; i < 100; i += 10) { bool was_inserted = tree.testInsert(CmpInt(i));
CHECK(was_inserted);
}
// Test iteration across the whole tree.
AvlTreeTestIF<CmpInt, CmpInt>::Iter iter(&tree); // `expect` produces (independently) the next expected number. `remaining` // counts the number items of items remaining. int expect = 10; int remaining = 9; while (iter.hasMore()) {
CmpInt ci = iter.next();
CHECK(ci.get() == expect);
expect += 10;
remaining--;
}
CHECK(remaining == 0);
// Test iteration from a specified start point. for (int i = 10; i < 100; i += 10) { for (int j = i - 1; j <= i + 1; j++) { // Set up `expect` and `remaining`.
remaining = (100 - i) / 10; switch (j % 10) { case 0:
expect = j; break; case 1:
expect = j + 9;
remaining--; break; case 9:
expect = j + 1; break; default:
MOZ_CRASH();
}
AvlTreeTestIF<CmpInt, CmpInt>::Iter iter(&tree, CmpInt(j)); while (iter.hasMore()) {
CmpInt ci = iter.next();
CHECK(ci.get() == expect);
expect += 10;
remaining--;
}
CHECK(remaining == 0);
}
}
}
// Now with a completely empty tree.
{
AvlTreeTestIF<CmpInt, CmpInt> emptyTree(&alloc);
CHECK(emptyTree.testSize() == 0); // Full tree iteration gets us nothing.
AvlTreeTestIF<CmpInt, CmpInt>::Iter iter1(&emptyTree);
CHECK(!iter1.hasMore()); // Starting iteration with any number gets us nothing.
AvlTreeTestIF<CmpInt, CmpInt>::Iter iter2(&emptyTree, CmpInt(42));
CHECK(!iter2.hasMore());
}
// Finally with a one-element tree.
{
AvlTreeTestIF<CmpInt, CmpInt> unitTree(&alloc); bool was_inserted = unitTree.testInsert(CmpInt(1337));
CHECK(was_inserted);
CHECK(unitTree.testSize() == 1); // Try full tree iteration.
AvlTreeTestIF<CmpInt, CmpInt>::Iter iter3(&unitTree);
CHECK(iter3.hasMore());
CmpInt ci = iter3.next();
CHECK(ci.get() == 1337);
CHECK(!iter3.hasMore()); for (int i = 1336; i <= 1338; i++) { int remaining = i < 1338 ? 1 : 0; int expect = i < 1338 ? 1337 : 99999 /*we'll never use this*/;
AvlTreeTestIF<CmpInt, CmpInt>::Iter iter4(&unitTree, CmpInt(i)); while (iter4.hasMore()) {
CmpInt ci = iter4.next();
CHECK(ci.get() == expect);
remaining--; // expect doesn't change, we only expect it (or nothing)
}
CHECK(remaining == 0);
}
}
returntrue;
}
END_TEST(testAvlTree_main)
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.