diff --git a/unit-tests/btree_counter_t.cc b/unit-tests/btree_counter_t.cc index 39412c7..0aa01c0 100644 --- a/unit-tests/btree_counter_t.cc +++ b/unit-tests/btree_counter_t.cc @@ -28,11 +28,21 @@ namespace { tm_(bm_, sm_) { } - void check_nr_metadata_blocks_is_ge(unsigned n) { - block_counter bc; + size_t get_nr_metadata_blocks(bool ignore_non_fatal = false, + bool stop_on_error = false) { + block_counter bc(stop_on_error); noop_value_counter vc; - count_btree_blocks(*tree_, bc, vc); - ASSERT_THAT(bc.get_counts().size(), Ge(n)); + count_btree_blocks(*tree_, bc, vc, ignore_non_fatal); + return bc.get_counts().size(); + } + + void damage_first_leaf_underfull() { + bcache::validator::ptr v = create_btree_node_validator(); + + block_address b = get_first_leaf(); + block_manager::write_ref blk = bm_->write_lock(b, v); + btree<1, uint64_traits>::leaf_node n = to_node(blk); + n.set_nr_entries(1); } with_temp_directory dir_; @@ -53,6 +63,19 @@ namespace { void commit() { block_manager::write_ref superblock(bm_->superblock(SUPERBLOCK)); } + + block_address get_first_leaf() { + bcache::validator::ptr v = create_btree_node_validator(); + + block_manager::read_ref root = bm_->read_lock(tree_->get_root(), v); + btree<1, uint64_traits>::internal_node n = to_node(root); + while (n.get_type() == INTERNAL) { + block_manager::read_ref internal = bm_->read_lock(n.value_at(0), v); + n = to_node(internal); + } + + return n.get_block_nr(); + } }; } @@ -62,7 +85,7 @@ TEST_F(BTreeCounterTests, count_empty_tree) { tree_.reset(new btree<1, uint64_traits>(tm_, rc_)); tm_.get_bm()->flush(); - check_nr_metadata_blocks_is_ge(1); + ASSERT_GE(get_nr_metadata_blocks(), 1u); } TEST_F(BTreeCounterTests, count_populated_tree) @@ -75,7 +98,44 @@ TEST_F(BTreeCounterTests, count_populated_tree) } tm_.get_bm()->flush(); - check_nr_metadata_blocks_is_ge(40); + ASSERT_GE(get_nr_metadata_blocks(), 40u); +} + +TEST_F(BTreeCounterTests, count_underfull_nodes) +{ + tree_.reset(new btree<1, uint64_traits>(tm_, rc_)); + + for (unsigned i = 0; i < 10000; i++) { + uint64_t key[1] = {i}; + tree_->insert(key, 0ull); + } + + tm_.get_bm()->flush(); + size_t nr_blocks = get_nr_metadata_blocks(); + + damage_first_leaf_underfull(); + tm_.get_bm()->flush(); + + // underfull nodes are not counted + bool ignore_non_fatal = false; + bool stop_on_error = false; + ASSERT_EQ(get_nr_metadata_blocks(ignore_non_fatal, stop_on_error), nr_blocks - 1); + + // underfull nodes are counted + ignore_non_fatal = true; + stop_on_error = false; + ASSERT_EQ(get_nr_metadata_blocks(ignore_non_fatal, stop_on_error), nr_blocks); + + // logical errors like underfull nodes don't result in exceptions, + // therefore the stop_on_error flag has no effect. + // FIXME: is it necessary to stop the counting on logical errors? + ignore_non_fatal = false; + stop_on_error = true; + ASSERT_EQ(get_nr_metadata_blocks(ignore_non_fatal, stop_on_error), nr_blocks - 1); + + ignore_non_fatal = true; + stop_on_error = true; + ASSERT_EQ(get_nr_metadata_blocks(ignore_non_fatal, stop_on_error), nr_blocks); } //----------------------------------------------------------------