// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * Copyright (C) 2012 Inktank, Inc. * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. */ #include "include/memory.h" #include #include #include #include #include "test/ObjectMap/KeyValueDBMemory.h" #include "kv/KeyValueDB.h" #include #include "global/global_init.h" #include "common/ceph_argparse.h" #include "gtest/gtest.h" using namespace std; string store_path; class IteratorTest : public ::testing::Test { public: boost::scoped_ptr db; boost::scoped_ptr mock; void SetUp() override { assert(!store_path.empty()); KeyValueDB *db_ptr = KeyValueDB::create(g_ceph_context, "leveldb", store_path); assert(!db_ptr->create_and_open(std::cerr)); db.reset(db_ptr); mock.reset(new KeyValueDBMemory()); } void TearDown() override { } ::testing::AssertionResult validate_db_clear(KeyValueDB *store) { KeyValueDB::WholeSpaceIterator it = store->get_iterator(); it->seek_to_first(); while (it->valid()) { pair k = it->raw_key(); if (mock->db.count(k)) { return ::testing::AssertionFailure() << __func__ << " mock store count " << mock->db.count(k) << " key(" << k.first << "," << k.second << ")"; } it->next(); } return ::testing::AssertionSuccess(); } ::testing::AssertionResult validate_db_match() { KeyValueDB::WholeSpaceIterator it = db->get_iterator(); it->seek_to_first(); while (it->valid()) { pair k = it->raw_key(); if (!mock->db.count(k)) { return ::testing::AssertionFailure() << __func__ << " mock db.count() " << mock->db.count(k) << " key(" << k.first << "," << k.second << ")"; } bufferlist it_bl = it->value(); bufferlist mock_bl = mock->db[k]; string it_val = _bl_to_str(it_bl); string mock_val = _bl_to_str(mock_bl); if (it_val != mock_val) { return ::testing::AssertionFailure() << __func__ << " key(" << k.first << "," << k.second << ")" << " mismatch db value(" << it_val << ")" << " mock value(" << mock_val << ")"; } it->next(); } return ::testing::AssertionSuccess(); } ::testing::AssertionResult validate_iterator( KeyValueDB::WholeSpaceIterator it, string expected_prefix, string expected_key, string expected_value) { if (!it->valid()) { return ::testing::AssertionFailure() << __func__ << " iterator not valid"; } if (!it->raw_key_is_prefixed(expected_prefix)) { return ::testing::AssertionFailure() << __func__ << " expected raw_key_is_prefixed() == TRUE" << " got FALSE"; } if (it->raw_key_is_prefixed("??__SomeUnexpectedValue__??")) { return ::testing::AssertionFailure() << __func__ << " expected raw_key_is_prefixed() == FALSE" << " got TRUE"; } pair key = it->raw_key(); if (expected_prefix != key.first) { return ::testing::AssertionFailure() << __func__ << " expected prefix '" << expected_prefix << "'" << " got prefix '" << key.first << "'"; } if (expected_key != it->key()) { return ::testing::AssertionFailure() << __func__ << " expected key '" << expected_key << "'" << " got key '" << it->key() << "'"; } if (it->key() != key.second) { return ::testing::AssertionFailure() << __func__ << " key '" << it->key() << "'" << " does not match" << " pair key '" << key.second << "'"; } if (_bl_to_str(it->value()) != expected_value) { return ::testing::AssertionFailure() << __func__ << " key '(" << key.first << "," << key.second << ")''" << " expected value '" << expected_value << "'" << " got value '" << _bl_to_str(it->value()) << "'"; } return ::testing::AssertionSuccess(); } /** * Checks if each key in the queue can be forward sequentially read from * the iterator iter. All keys must be present and be prefixed with prefix, * otherwise the validation will fail. * * Assumes that each key value must be based on the key name and generated * by _gen_val(). */ void validate_prefix(KeyValueDB::WholeSpaceIterator iter, string &prefix, deque &keys) { while (!keys.empty()) { ASSERT_TRUE(iter->valid()); string expected_key = keys.front(); keys.pop_front(); string expected_value = _gen_val_str(expected_key); ASSERT_TRUE(validate_iterator(iter, prefix, expected_key, expected_value)); iter->next(); } } /** * Checks if each key in the queue can be backward sequentially read from * the iterator iter. All keys must be present and be prefixed with prefix, * otherwise the validation will fail. * * Assumes that each key value must be based on the key name and generated * by _gen_val(). */ void validate_prefix_backwards(KeyValueDB::WholeSpaceIterator iter, string &prefix, deque &keys) { while (!keys.empty()) { ASSERT_TRUE(iter->valid()); string expected_key = keys.front(); keys.pop_front(); string expected_value = _gen_val_str(expected_key); ASSERT_TRUE(validate_iterator(iter, prefix, expected_key, expected_value)); iter->prev(); } } void clear(KeyValueDB *store) { KeyValueDB::WholeSpaceIterator it = store->get_iterator(); it->seek_to_first(); KeyValueDB::Transaction t = store->get_transaction(); while (it->valid()) { pair k = it->raw_key(); t->rmkey(k.first, k.second); it->next(); } store->submit_transaction_sync(t); } string _bl_to_str(bufferlist val) { string str(val.c_str(), val.length()); return str; } string _gen_val_str(string key) { ostringstream ss; ss << "##value##" << key << "##"; return ss.str(); } bufferlist _gen_val(string key) { bufferlist bl; bl.append(_gen_val_str(key)); return bl; } void print_iterator(KeyValueDB::WholeSpaceIterator iter) { if (!iter->valid()) { std::cerr << __func__ << " iterator is not valid; stop." << std::endl; return; } int i = 0; while (iter->valid()) { pair k = iter->raw_key(); std::cerr << __func__ << " pos " << (++i) << " key (" << k.first << "," << k.second << ")" << " value(" << _bl_to_str(iter->value()) << ")" << std::endl; iter->next(); } } void print_db(KeyValueDB *store) { KeyValueDB::WholeSpaceIterator it = store->get_iterator(); it->seek_to_first(); print_iterator(it); } }; // ------- Remove Keys / Remove Keys By Prefix ------- class RmKeysTest : public IteratorTest { public: string prefix1; string prefix2; string prefix3; void init(KeyValueDB *db) { KeyValueDB::Transaction tx = db->get_transaction(); tx->set(prefix1, "11", _gen_val("11")); tx->set(prefix1, "12", _gen_val("12")); tx->set(prefix1, "13", _gen_val("13")); tx->set(prefix2, "21", _gen_val("21")); tx->set(prefix2, "22", _gen_val("22")); tx->set(prefix2, "23", _gen_val("23")); tx->set(prefix3, "31", _gen_val("31")); tx->set(prefix3, "32", _gen_val("32")); tx->set(prefix3, "33", _gen_val("33")); db->submit_transaction_sync(tx); } void SetUp() override { IteratorTest::SetUp(); prefix1 = "_PREFIX_1_"; prefix2 = "_PREFIX_2_"; prefix3 = "_PREFIX_3_"; clear(db.get()); ASSERT_TRUE(validate_db_clear(db.get())); clear(mock.get()); ASSERT_TRUE(validate_db_match()); init(db.get()); init(mock.get()); ASSERT_TRUE(validate_db_match()); } void TearDown() override { IteratorTest::TearDown(); } /** * Test the transaction's rmkeys behavior when we remove a given prefix * from the beginning of the key space, or from the end of the key space, * or even simply in the middle. */ void RmKeysByPrefix(KeyValueDB *store) { // remove prefix2 ; check if prefix1 remains, and then prefix3 KeyValueDB::Transaction tx = store->get_transaction(); // remove the prefix in the middle of the key space tx->rmkeys_by_prefix(prefix2); store->submit_transaction_sync(tx); deque key_deque; KeyValueDB::WholeSpaceIterator iter = store->get_iterator(); iter->seek_to_first(); // check for prefix1 key_deque.push_back("11"); key_deque.push_back("12"); key_deque.push_back("13"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); // check for prefix3 ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("31"); key_deque.push_back("32"); key_deque.push_back("33"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); clear(store); ASSERT_TRUE(validate_db_clear(store)); init(store); // remove prefix1 ; check if prefix2 and then prefix3 remain tx = store->get_transaction(); // remove the prefix at the beginning of the key space tx->rmkeys_by_prefix(prefix1); store->submit_transaction_sync(tx); iter = store->get_iterator(); iter->seek_to_first(); // check for prefix2 key_deque.clear(); key_deque.push_back("21"); key_deque.push_back("22"); key_deque.push_back("23"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); // check for prefix3 ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("31"); key_deque.push_back("32"); key_deque.push_back("33"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); clear(store); ASSERT_TRUE(validate_db_clear(store)); init(store); // remove prefix3 ; check if prefix1 and then prefix2 remain tx = store->get_transaction(); // remove the prefix at the end of the key space tx->rmkeys_by_prefix(prefix3); store->submit_transaction_sync(tx); iter = store->get_iterator(); iter->seek_to_first(); // check for prefix1 key_deque.clear(); key_deque.push_back("11"); key_deque.push_back("12"); key_deque.push_back("13"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); // check for prefix2 ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("21"); key_deque.push_back("22"); key_deque.push_back("23"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); } /** * Test how the leveldb's whole-space iterator behaves when we remove * keys from the store while iterating over them. */ void RmKeysWhileIteratingSnapshot(KeyValueDB *store, KeyValueDB::WholeSpaceIterator iter) { SCOPED_TRACE("RmKeysWhileIteratingSnapshot"); iter->seek_to_first(); ASSERT_TRUE(iter->valid()); KeyValueDB::Transaction t = store->get_transaction(); t->rmkey(prefix1, "11"); t->rmkey(prefix1, "12"); t->rmkey(prefix2, "23"); t->rmkey(prefix3, "33"); store->submit_transaction_sync(t); deque key_deque; // check for prefix1 key_deque.push_back("11"); key_deque.push_back("12"); key_deque.push_back("13"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); // check for prefix2 key_deque.clear(); key_deque.push_back("21"); key_deque.push_back("22"); key_deque.push_back("23"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); // check for prefix3 key_deque.clear(); key_deque.push_back("31"); key_deque.push_back("32"); key_deque.push_back("33"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); iter->next(); ASSERT_FALSE(iter->valid()); // make sure those keys were removed from the store KeyValueDB::WholeSpaceIterator tmp_it = store->get_iterator(); tmp_it->seek_to_first(); ASSERT_TRUE(tmp_it->valid()); key_deque.clear(); key_deque.push_back("13"); validate_prefix(tmp_it, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(tmp_it->valid()); key_deque.clear(); key_deque.push_back("21"); key_deque.push_back("22"); validate_prefix(tmp_it, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(tmp_it->valid()); key_deque.clear(); key_deque.push_back("31"); key_deque.push_back("32"); validate_prefix(tmp_it, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(tmp_it->valid()); } }; TEST_F(RmKeysTest, RmKeysByPrefixLevelDB) { SCOPED_TRACE("LevelDB"); RmKeysByPrefix(db.get()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(RmKeysTest, RmKeysByPrefixMockDB) { SCOPED_TRACE("Mock DB"); RmKeysByPrefix(mock.get()); ASSERT_FALSE(HasFatalFailure()); } /** * If you refer to function RmKeysTest::RmKeysWhileIteratingSnapshot(), * you will notice that we seek the iterator to the first key, and then * we go on to remove several keys from the underlying store, including * the first couple keys. * * We would expect that during this test, as soon as we removed the keys * from the store, the iterator would get invalid, or cause some sort of * unexpected mess. * * Instead, the current version of leveldb handles it perfectly, by making * the iterator to use a snapshot instead of the store's real state. This * way, LevelDBStore's whole-space iterator will behave much like its own * whole-space snapshot iterator. * * However, this particular behavior of the iterator hasn't been documented * on leveldb, and we should assume that it can be changed at any point in * time. * * Therefore, we keep this test, being exactly the same as the one for the * whole-space snapshot iterator, as we currently assume they should behave * identically. If this test fails, at some point, and the whole-space * snapshot iterator passes, then it probably means that leveldb changed * how its iterator behaves. */ TEST_F(RmKeysTest, RmKeysWhileIteratingLevelDB) { SCOPED_TRACE("LevelDB -- WholeSpaceIterator"); RmKeysWhileIteratingSnapshot(db.get(), db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(RmKeysTest, RmKeysWhileIteratingMockDB) { std::cout << "There is no safe way to test key removal while iterating\n" << "over the mock store without using snapshots" << std::endl; } // ------- Set Keys / Update Values ------- class SetKeysTest : public IteratorTest { public: string prefix1; string prefix2; void init(KeyValueDB *db) { KeyValueDB::Transaction tx = db->get_transaction(); tx->set(prefix1, "aaa", _gen_val("aaa")); tx->set(prefix1, "ccc", _gen_val("ccc")); tx->set(prefix1, "eee", _gen_val("eee")); tx->set(prefix2, "vvv", _gen_val("vvv")); tx->set(prefix2, "xxx", _gen_val("xxx")); tx->set(prefix2, "zzz", _gen_val("zzz")); db->submit_transaction_sync(tx); } void SetUp() override { IteratorTest::SetUp(); prefix1 = "_PREFIX_1_"; prefix2 = "_PREFIX_2_"; clear(db.get()); ASSERT_TRUE(validate_db_clear(db.get())); clear(mock.get()); ASSERT_TRUE(validate_db_match()); init(db.get()); init(mock.get()); ASSERT_TRUE(validate_db_match()); } void TearDown() override { IteratorTest::TearDown(); } /** * Make sure that the iterator picks on new keys added if it hasn't yet * iterated away from that position. * * This should only happen for the whole-space iterator when not using * the snapshot version. * * We don't need to test the validity of all elements, but we do test * inserting while moving from the first element to the last, using next() * to move forward, and then we test the same behavior while iterating * from the last element to the first, using prev() to move backwards. */ void SetKeysWhileIterating(KeyValueDB *store, KeyValueDB::WholeSpaceIterator iter) { iter->seek_to_first(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa", _gen_val_str("aaa"))); iter->next(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "ccc", _bl_to_str(_gen_val("ccc")))); // insert new key 'ddd' after 'ccc' and before 'eee' KeyValueDB::Transaction tx = store->get_transaction(); tx->set(prefix1, "ddd", _gen_val("ddd")); store->submit_transaction_sync(tx); iter->next(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "ddd", _gen_val_str("ddd"))); iter->seek_to_last(); ASSERT_TRUE(iter->valid()); tx = store->get_transaction(); tx->set(prefix2, "yyy", _gen_val("yyy")); store->submit_transaction_sync(tx); iter->prev(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix2, "yyy", _gen_val_str("yyy"))); } /** * Make sure that the whole-space snapshot iterator does not pick on new keys * added to the store since we created the iterator, thus guaranteeing * read-consistency. * * We don't need to test the validity of all elements, but we do test * inserting while moving from the first element to the last, using next() * to move forward, and then we test the same behavior while iterating * from the last element to the first, using prev() to move backwards. */ void SetKeysWhileIteratingSnapshot(KeyValueDB *store, KeyValueDB::WholeSpaceIterator iter) { iter->seek_to_first(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa", _gen_val_str("aaa"))); iter->next(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "ccc", _bl_to_str(_gen_val("ccc")))); // insert new key 'ddd' after 'ccc' and before 'eee' KeyValueDB::Transaction tx = store->get_transaction(); tx->set(prefix1, "ddd", _gen_val("ddd")); store->submit_transaction_sync(tx); iter->next(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "eee", _gen_val_str("eee"))); iter->seek_to_last(); ASSERT_TRUE(iter->valid()); tx = store->get_transaction(); tx->set(prefix2, "yyy", _gen_val("yyy")); store->submit_transaction_sync(tx); iter->prev(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix2, "xxx", _gen_val_str("xxx"))); } /** * Make sure that the whole-space iterator is able to read values changed on * the store, even after we moved to the updated position. * * This should only be possible when not using the whole-space snapshot * version of the iterator. */ void UpdateValuesWhileIterating(KeyValueDB *store, KeyValueDB::WholeSpaceIterator iter) { iter->seek_to_first(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa", _gen_val_str("aaa"))); KeyValueDB::Transaction tx = store->get_transaction(); tx->set(prefix1, "aaa", _gen_val("aaa_1")); store->submit_transaction_sync(tx); ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa", _gen_val_str("aaa_1"))); iter->seek_to_last(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix2, "zzz", _gen_val_str("zzz"))); tx = store->get_transaction(); tx->set(prefix2, "zzz", _gen_val("zzz_1")); store->submit_transaction_sync(tx); ASSERT_TRUE(validate_iterator(iter, prefix2, "zzz", _gen_val_str("zzz_1"))); } /** * Make sure that the whole-space iterator is able to read values changed on * the store, even after we moved to the updated position. * * This should only be possible when not using the whole-space snapshot * version of the iterator. */ void UpdateValuesWhileIteratingSnapshot( KeyValueDB *store, KeyValueDB::WholeSpaceIterator iter) { iter->seek_to_first(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa", _gen_val_str("aaa"))); KeyValueDB::Transaction tx = store->get_transaction(); tx->set(prefix1, "aaa", _gen_val("aaa_1")); store->submit_transaction_sync(tx); ASSERT_TRUE(validate_iterator(iter, prefix1, "aaa", _gen_val_str("aaa"))); iter->seek_to_last(); ASSERT_TRUE(iter->valid()); ASSERT_TRUE(validate_iterator(iter, prefix2, "zzz", _gen_val_str("zzz"))); tx = store->get_transaction(); tx->set(prefix2, "zzz", _gen_val("zzz_1")); store->submit_transaction_sync(tx); ASSERT_TRUE(validate_iterator(iter, prefix2, "zzz", _gen_val_str("zzz"))); // check those values were really changed in the store KeyValueDB::WholeSpaceIterator tmp_iter = store->get_iterator(); tmp_iter->seek_to_first(); ASSERT_TRUE(tmp_iter->valid()); ASSERT_TRUE(validate_iterator(tmp_iter, prefix1, "aaa", _gen_val_str("aaa_1"))); tmp_iter->seek_to_last(); ASSERT_TRUE(tmp_iter->valid()); ASSERT_TRUE(validate_iterator(tmp_iter, prefix2, "zzz", _gen_val_str("zzz_1"))); } }; TEST_F(SetKeysTest, DISABLED_SetKeysWhileIteratingLevelDB) { SCOPED_TRACE("LevelDB: SetKeysWhileIteratingLevelDB"); SetKeysWhileIterating(db.get(), db->get_iterator()); ASSERT_TRUE(HasFatalFailure()); } TEST_F(SetKeysTest, SetKeysWhileIteratingMockDB) { SCOPED_TRACE("Mock DB: SetKeysWhileIteratingMockDB"); SetKeysWhileIterating(mock.get(), mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SetKeysTest, DISABLED_UpdateValuesWhileIteratingLevelDB) { SCOPED_TRACE("LevelDB: UpdateValuesWhileIteratingLevelDB"); UpdateValuesWhileIterating(db.get(), db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SetKeysTest, UpdateValuesWhileIteratingMockDB) { SCOPED_TRACE("MockDB: UpdateValuesWhileIteratingMockDB"); UpdateValuesWhileIterating(mock.get(), mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } class BoundsTest : public IteratorTest { public: string prefix1; string prefix2; string prefix3; void init(KeyValueDB *store) { KeyValueDB::Transaction tx = store->get_transaction(); tx->set(prefix1, "aaa", _gen_val("aaa")); tx->set(prefix1, "ccc", _gen_val("ccc")); tx->set(prefix1, "eee", _gen_val("eee")); tx->set(prefix2, "vvv", _gen_val("vvv")); tx->set(prefix2, "xxx", _gen_val("xxx")); tx->set(prefix2, "zzz", _gen_val("zzz")); tx->set(prefix3, "aaa", _gen_val("aaa")); tx->set(prefix3, "mmm", _gen_val("mmm")); tx->set(prefix3, "yyy", _gen_val("yyy")); store->submit_transaction_sync(tx); } void SetUp() override { IteratorTest::SetUp(); prefix1 = "_PREFIX_1_"; prefix2 = "_PREFIX_2_"; prefix3 = "_PREFIX_4_"; clear(db.get()); ASSERT_TRUE(validate_db_clear(db.get())); clear(mock.get()); ASSERT_TRUE(validate_db_match()); init(db.get()); init(mock.get()); ASSERT_TRUE(validate_db_match()); } void TearDown() override { IteratorTest::TearDown(); } void LowerBoundWithEmptyKeyOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // see what happens when we have an empty key and try to get to the // first available prefix iter->lower_bound(prefix1, ""); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // if we got here without problems, then it is safe to assume the // remaining prefixes are intact. // see what happens when we have an empty key and try to get to the // middle of the key-space iter->lower_bound(prefix2, ""); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("vvv"); key_deque.push_back("xxx"); key_deque.push_back("zzz"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // if we got here without problems, then it is safe to assume the // remaining prefixes are intact. // see what happens when we have an empty key and try to get to the // last prefix on the key-space iter->lower_bound(prefix3, ""); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); key_deque.push_back("mmm"); key_deque.push_back("yyy"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); // we reached the end of the key_space, so the iterator should no longer // be valid // see what happens when we look for an inexistent prefix, that will // compare higher than the existing prefixes, with an empty key // expected: reach the store's end; iterator becomes invalid iter->lower_bound("_PREFIX_9_", ""); ASSERT_FALSE(iter->valid()); // see what happens when we look for an inexistent prefix, that will // compare lower than the existing prefixes, with an empty key // expected: find the first prefix; iterator is valid iter->lower_bound("_PREFIX_0_", ""); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // see what happens when we look for an empty prefix (that should compare // lower than any existing prefixes) // expected: find the first prefix; iterator is valid iter->lower_bound("", ""); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); } void LowerBoundWithEmptyPrefixOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // check for an empty prefix, with key 'aaa'. Since this key is shared // among two different prefixes, it is relevant to check which will be // found first. // expected: find key (prefix1, aaa); iterator is valid iter->lower_bound("", "aaa"); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // since we found prefix1, it is safe to assume that the remaining // prefixes (prefix2 and prefix3) will follow // any lower_bound operation with an empty prefix should always put the // iterator in the first key in the key-space, despite what key is // specified. This means that looking for ("","AAAAAAAAAA") should // also position the iterator on (prefix1, aaa). // expected: find key (prefix1, aaa); iterator is valid iter->lower_bound("", "AAAAAAAAAA"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // note: this test is a duplicate of the one in the function above. Why? // Well, because it also fits here (being its prefix empty), and one could // very well run solely this test (instead of the whole battery) and would // certainly expect this case to be tested. // see what happens when we look for an empty prefix (that should compare // lower than any existing prefixes) // expected: find the first prefix; iterator is valid iter->lower_bound("", ""); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); } void LowerBoundOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // check that we find the first key in the store // expected: find (prefix1, aaa); iterator is valid iter->lower_bound(prefix1, "aaa"); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that we find the last key in the store // expected: find (prefix3, yyy); iterator is valid iter->lower_bound(prefix3, "yyy"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("yyy"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); // check that looking for non-existent prefix '_PREFIX_0_' will // always result in the first value of prefix1 (prefix1,"aaa") // expected: find (prefix1, aaa); iterator is valid iter->lower_bound("_PREFIX_0_", "AAAAA"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that looking for non-existent prefix '_PREFIX_3_' will // always result in the first value of prefix3 (prefix4,"aaa") // expected: find (prefix3, aaa); iterator is valid iter->lower_bound("_PREFIX_3_", "AAAAA"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that looking for non-existent prefix '_PREFIX_9_' will // always result in an invalid iterator. // expected: iterator is invalid iter->lower_bound("_PREFIX_9_", "AAAAA"); ASSERT_FALSE(iter->valid()); } void UpperBoundWithEmptyKeyOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // check that looking for (prefix1, "") will result in finding // the first key in prefix1 (prefix1, "aaa") // expected: find (prefix1, aaa); iterator is valid iter->upper_bound(prefix1, ""); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that looking for (prefix2, "") will result in finding // the first key in prefix2 (prefix2, vvv) // expected: find (prefix2, aaa); iterator is valid iter->upper_bound(prefix2, ""); key_deque.push_back("vvv"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that looking for (prefix3, "") will result in finding // the first key in prefix3 (prefix3, aaa) // expected: find (prefix3, aaa); iterator is valid iter->upper_bound(prefix3, ""); key_deque.push_back("aaa"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // see what happens when we look for an inexistent prefix, that will // compare higher than the existing prefixes, with an empty key // expected: reach the store's end; iterator becomes invalid iter->upper_bound("_PREFIX_9_", ""); ASSERT_FALSE(iter->valid()); // see what happens when we look for an inexistent prefix, that will // compare lower than the existing prefixes, with an empty key // expected: find the first prefix; iterator is valid iter->upper_bound("_PREFIX_0_", ""); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // see what happens when we look for an empty prefix (that should compare // lower than any existing prefixes) // expected: find the first prefix; iterator is valid iter->upper_bound("", ""); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); } void UpperBoundWithEmptyPrefixOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // check for an empty prefix, with key 'aaa'. Since this key is shared // among two different prefixes, it is relevant to check which will be // found first. // expected: find key (prefix1, aaa); iterator is valid iter->upper_bound("", "aaa"); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // any upper_bound operation with an empty prefix should always put the // iterator in the first key whose prefix compares greater, despite the // key that is specified. This means that looking for ("","AAAAAAAAAA") // should position the iterator on (prefix1, aaa). // expected: find key (prefix1, aaa); iterator is valid iter->upper_bound("", "AAAAAAAAAA"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // note: this test is a duplicate of the one in the function above. Why? // Well, because it also fits here (being its prefix empty), and one could // very well run solely this test (instead of the whole battery) and would // certainly expect this case to be tested. // see what happens when we look for an empty prefix (that should compare // lower than any existing prefixes) // expected: find the first prefix; iterator is valid iter->upper_bound("", ""); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); } void UpperBoundOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // check that we find the second key in the store // expected: find (prefix1, ccc); iterator is valid iter->upper_bound(prefix1, "bbb"); ASSERT_TRUE(iter->valid()); key_deque.push_back("ccc"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that we find the last key in the store // expected: find (prefix3, yyy); iterator is valid iter->upper_bound(prefix3, "xxx"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("yyy"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); // check that looking for non-existent prefix '_PREFIX_0_' will // always result in the first value of prefix1 (prefix1,"aaa") // expected: find (prefix1, aaa); iterator is valid iter->upper_bound("_PREFIX_0_", "AAAAA"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that looking for non-existent prefix '_PREFIX_3_' will // always result in the first value of prefix3 (prefix3,"aaa") // expected: find (prefix3, aaa); iterator is valid iter->upper_bound("_PREFIX_3_", "AAAAA"); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix3, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // check that looking for non-existent prefix '_PREFIX_9_' will // always result in an invalid iterator. // expected: iterator is invalid iter->upper_bound("_PREFIX_9_", "AAAAA"); ASSERT_FALSE(iter->valid()); } }; TEST_F(BoundsTest, LowerBoundWithEmptyKeyOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Lower Bound, Empty Key, Whole-Space Iterator"); LowerBoundWithEmptyKeyOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, LowerBoundWithEmptyKeyOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Lower Bound, Empty Key, Whole-Space Iterator"); LowerBoundWithEmptyKeyOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, LowerBoundWithEmptyPrefixOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Lower Bound, Empty Prefix, Whole-Space Iterator"); LowerBoundWithEmptyPrefixOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, LowerBoundWithEmptyPrefixOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Lower Bound, Empty Prefix, Whole-Space Iterator"); LowerBoundWithEmptyPrefixOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, LowerBoundOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Lower Bound, Whole-Space Iterator"); LowerBoundOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, LowerBoundOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Lower Bound, Whole-Space Iterator"); LowerBoundOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, UpperBoundWithEmptyKeyOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Upper Bound, Empty Key, Whole-Space Iterator"); UpperBoundWithEmptyKeyOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, UpperBoundWithEmptyKeyOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Upper Bound, Empty Key, Whole-Space Iterator"); UpperBoundWithEmptyKeyOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, UpperBoundWithEmptyPrefixOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Upper Bound, Empty Prefix, Whole-Space Iterator"); UpperBoundWithEmptyPrefixOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, UpperBoundWithEmptyPrefixOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Upper Bound, Empty Prefix, Whole-Space Iterator"); UpperBoundWithEmptyPrefixOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, UpperBoundOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Upper Bound, Whole-Space Iterator"); UpperBoundOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(BoundsTest, UpperBoundOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Upper Bound, Whole-Space Iterator"); UpperBoundOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } class SeeksTest : public IteratorTest { public: string prefix0; string prefix1; string prefix2; string prefix3; string prefix4; string prefix5; void init(KeyValueDB *store) { KeyValueDB::Transaction tx = store->get_transaction(); tx->set(prefix1, "aaa", _gen_val("aaa")); tx->set(prefix1, "ccc", _gen_val("ccc")); tx->set(prefix1, "eee", _gen_val("eee")); tx->set(prefix2, "vvv", _gen_val("vvv")); tx->set(prefix2, "xxx", _gen_val("xxx")); tx->set(prefix2, "zzz", _gen_val("zzz")); tx->set(prefix4, "aaa", _gen_val("aaa")); tx->set(prefix4, "mmm", _gen_val("mmm")); tx->set(prefix4, "yyy", _gen_val("yyy")); store->submit_transaction_sync(tx); } void SetUp() override { IteratorTest::SetUp(); prefix0 = "_PREFIX_0_"; prefix1 = "_PREFIX_1_"; prefix2 = "_PREFIX_2_"; prefix3 = "_PREFIX_3_"; prefix4 = "_PREFIX_4_"; prefix5 = "_PREFIX_5_"; clear(db.get()); ASSERT_TRUE(validate_db_clear(db.get())); clear(mock.get()); ASSERT_TRUE(validate_db_match()); init(db.get()); init(mock.get()); ASSERT_TRUE(validate_db_match()); } void TearDown() override { IteratorTest::TearDown(); } void SeekToFirstOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { iter->seek_to_first(); ASSERT_TRUE(iter->valid()); deque key_deque; key_deque.push_back("aaa"); key_deque.push_back("ccc"); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); } void SeekToFirstWithPrefixOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // if the prefix is empty, we must end up seeking to the first key. // expected: seek to (prefix1, aaa); iterator is valid iter->seek_to_first(""); ASSERT_TRUE(iter->valid()); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to non-existent prefix that compares lower than the // first available prefix // expected: seek to (prefix1, aaa); iterator is valid iter->seek_to_first(prefix0); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to non-existent prefix // expected: seek to (prefix4, aaa); iterator is valid iter->seek_to_first(prefix3); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix4, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to non-existent prefix that compares greater than the // last available prefix // expected: iterator is invalid iter->seek_to_first(prefix5); ASSERT_FALSE(iter->valid()); // try seeking to the first prefix and make sure we end up in its first // position // expected: seek to (prefix1,aaa); iterator is valid iter->seek_to_first(prefix1); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to the second prefix and make sure we end up in its // first position // expected: seek to (prefix2,vvv); iterator is valid iter->seek_to_first(prefix2); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("vvv"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to the last prefix and make sure we end up in its // first position // expected: seek to (prefix4,aaa); iterator is valid iter->seek_to_first(prefix4); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("aaa"); validate_prefix(iter, prefix4, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); } void SeekToLastOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; iter->seek_to_last(); key_deque.push_back("yyy"); validate_prefix(iter, prefix4, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); } void SeekToLastWithPrefixOnWholeSpaceIterator( KeyValueDB::WholeSpaceIterator iter) { deque key_deque; // if the prefix is empty, we must end up seeking to last position // that has an empty prefix, or to the previous position to the first // position whose prefix compares higher than empty. // expected: iterator is invalid (because (prefix1,aaa) is the first // position that compared higher than an empty prefix) iter->seek_to_last(""); ASSERT_FALSE(iter->valid()); // try seeking to non-existent prefix that compares lower than the // first available prefix // expected: iterator is invalid (because (prefix1,aaa) is the first // position that compared higher than prefix0) iter->seek_to_last(prefix0); ASSERT_FALSE(iter->valid()); // try seeking to non-existent prefix // expected: seek to (prefix2, zzz); iterator is valid iter->seek_to_last(prefix3); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("zzz"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to non-existent prefix that compares greater than the // last available prefix // expected: iterator is in the last position of the store; // i.e., (prefix4,yyy) iter->seek_to_last(prefix5); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("yyy"); validate_prefix(iter, prefix4, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); // try seeking to the first prefix and make sure we end up in its last // position // expected: seek to (prefix1,eee); iterator is valid iter->seek_to_last(prefix1); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("eee"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to the second prefix and make sure we end up in its // last position // expected: seek to (prefix2,vvv); iterator is valid iter->seek_to_last(prefix2); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("zzz"); validate_prefix(iter, prefix2, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_TRUE(iter->valid()); // try seeking to the last prefix and make sure we end up in its // last position // expected: seek to (prefix4,aaa); iterator is valid iter->seek_to_last(prefix4); ASSERT_TRUE(iter->valid()); key_deque.clear(); key_deque.push_back("yyy"); validate_prefix(iter, prefix4, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); } }; TEST_F(SeeksTest, SeekToFirstOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Seek To First, Whole Space Iterator"); SeekToFirstOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SeeksTest, SeekToFirstOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Seek To First, Whole Space Iterator"); SeekToFirstOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SeeksTest, SeekToFirstWithPrefixOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Seek To First, With Prefix, Whole Space Iterator"); SeekToFirstWithPrefixOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SeeksTest, SeekToFirstWithPrefixOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Seek To First, With Prefix, Whole Space Iterator"); SeekToFirstWithPrefixOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SeeksTest, SeekToLastOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Seek To Last, Whole Space Iterator"); SeekToLastOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SeeksTest, SeekToLastOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Seek To Last, Whole Space Iterator"); SeekToLastOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SeeksTest, SeekToLastWithPrefixOnWholeSpaceIteratorLevelDB) { SCOPED_TRACE("LevelDB: Seek To Last, With Prefix, Whole Space Iterator"); SeekToLastWithPrefixOnWholeSpaceIterator(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(SeeksTest, SeekToLastWithPrefixOnWholeSpaceIteratorMockDB) { SCOPED_TRACE("MockDB: Seek To Last, With Prefix, Whole Space Iterator"); SeekToLastWithPrefixOnWholeSpaceIterator(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } class KeySpaceIteration : public IteratorTest { public: string prefix1; void init(KeyValueDB *store) { KeyValueDB::Transaction tx = store->get_transaction(); tx->set(prefix1, "aaa", _gen_val("aaa")); tx->set(prefix1, "vvv", _gen_val("vvv")); tx->set(prefix1, "zzz", _gen_val("zzz")); store->submit_transaction_sync(tx); } void SetUp() override { IteratorTest::SetUp(); prefix1 = "_PREFIX_1_"; clear(db.get()); ASSERT_TRUE(validate_db_clear(db.get())); clear(mock.get()); ASSERT_TRUE(validate_db_match()); init(db.get()); init(mock.get()); ASSERT_TRUE(validate_db_match()); } void TearDown() override { IteratorTest::TearDown(); } void ForwardIteration(KeyValueDB::WholeSpaceIterator iter) { deque key_deque; iter->seek_to_first(); key_deque.push_back("aaa"); key_deque.push_back("vvv"); key_deque.push_back("zzz"); validate_prefix(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); } void BackwardIteration(KeyValueDB::WholeSpaceIterator iter) { deque key_deque; iter->seek_to_last(); key_deque.push_back("zzz"); key_deque.push_back("vvv"); key_deque.push_back("aaa"); validate_prefix_backwards(iter, prefix1, key_deque); ASSERT_FALSE(HasFatalFailure()); ASSERT_FALSE(iter->valid()); } }; TEST_F(KeySpaceIteration, ForwardIterationLevelDB) { SCOPED_TRACE("LevelDB: Forward Iteration, Whole Space Iterator"); ForwardIteration(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(KeySpaceIteration, ForwardIterationMockDB) { SCOPED_TRACE("MockDB: Forward Iteration, Whole Space Iterator"); ForwardIteration(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(KeySpaceIteration, BackwardIterationLevelDB) { SCOPED_TRACE("LevelDB: Backward Iteration, Whole Space Iterator"); BackwardIteration(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(KeySpaceIteration, BackwardIterationMockDB) { SCOPED_TRACE("MockDB: Backward Iteration, Whole Space Iterator"); BackwardIteration(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } class EmptyStore : public IteratorTest { public: void SetUp() override { IteratorTest::SetUp(); clear(db.get()); ASSERT_TRUE(validate_db_clear(db.get())); clear(mock.get()); ASSERT_TRUE(validate_db_match()); } void SeekToFirst(KeyValueDB::WholeSpaceIterator iter) { // expected: iterator is invalid iter->seek_to_first(); ASSERT_FALSE(iter->valid()); } void SeekToFirstWithPrefix(KeyValueDB::WholeSpaceIterator iter) { // expected: iterator is invalid iter->seek_to_first("prefix"); ASSERT_FALSE(iter->valid()); } void SeekToLast(KeyValueDB::WholeSpaceIterator iter) { // expected: iterator is invalid iter->seek_to_last(); ASSERT_FALSE(iter->valid()); } void SeekToLastWithPrefix(KeyValueDB::WholeSpaceIterator iter) { // expected: iterator is invalid iter->seek_to_last("prefix"); ASSERT_FALSE(iter->valid()); } void LowerBound(KeyValueDB::WholeSpaceIterator iter) { // expected: iterator is invalid iter->lower_bound("prefix", ""); ASSERT_FALSE(iter->valid()); // expected: iterator is invalid iter->lower_bound("", "key"); ASSERT_FALSE(iter->valid()); // expected: iterator is invalid iter->lower_bound("prefix", "key"); ASSERT_FALSE(iter->valid()); } void UpperBound(KeyValueDB::WholeSpaceIterator iter) { // expected: iterator is invalid iter->upper_bound("prefix", ""); ASSERT_FALSE(iter->valid()); // expected: iterator is invalid iter->upper_bound("", "key"); ASSERT_FALSE(iter->valid()); // expected: iterator is invalid iter->upper_bound("prefix", "key"); ASSERT_FALSE(iter->valid()); } }; TEST_F(EmptyStore, SeekToFirstLevelDB) { SCOPED_TRACE("LevelDB: Empty Store, Seek To First"); SeekToFirst(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, SeekToFirstMockDB) { SCOPED_TRACE("MockDB: Empty Store, Seek To First"); SeekToFirst(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, SeekToFirstWithPrefixLevelDB) { SCOPED_TRACE("LevelDB: Empty Store, Seek To First With Prefix"); SeekToFirstWithPrefix(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, SeekToFirstWithPrefixMockDB) { SCOPED_TRACE("MockDB: Empty Store, Seek To First With Prefix"); SeekToFirstWithPrefix(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, SeekToLastLevelDB) { SCOPED_TRACE("LevelDB: Empty Store, Seek To Last"); SeekToLast(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, SeekToLastMockDB) { SCOPED_TRACE("MockDB: Empty Store, Seek To Last"); SeekToLast(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, SeekToLastWithPrefixLevelDB) { SCOPED_TRACE("LevelDB: Empty Store, Seek To Last With Prefix"); SeekToLastWithPrefix(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, SeekToLastWithPrefixMockDB) { SCOPED_TRACE("MockDB: Empty Store, Seek To Last With Prefix"); SeekToLastWithPrefix(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, LowerBoundLevelDB) { SCOPED_TRACE("LevelDB: Empty Store, Lower Bound"); LowerBound(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, LowerBoundMockDB) { SCOPED_TRACE("MockDB: Empty Store, Lower Bound"); LowerBound(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, UpperBoundLevelDB) { SCOPED_TRACE("LevelDB: Empty Store, Upper Bound"); UpperBound(db->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } TEST_F(EmptyStore, UpperBoundMockDB) { SCOPED_TRACE("MockDB: Empty Store, Upper Bound"); UpperBound(mock->get_iterator()); ASSERT_FALSE(HasFatalFailure()); } int main(int argc, char *argv[]) { vector args; argv_to_vec(argc, (const char **) argv, args); auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0); common_init_finish(g_ceph_context); ::testing::InitGoogleTest(&argc, argv); if (argc < 2) { std::cerr << "Usage: " << argv[0] << "[ceph_options] [gtest_options] " << std::endl; return 1; } store_path = string(argv[1]); return RUN_ALL_TESTS(); }