X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Ftest%2FObjectMap%2Ftest_object_map.cc;fp=src%2Fceph%2Fsrc%2Ftest%2FObjectMap%2Ftest_object_map.cc;h=8d3f6882c93cff222816e5da37977cff43715fe3;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/test/ObjectMap/test_object_map.cc b/src/ceph/src/test/ObjectMap/test_object_map.cc new file mode 100644 index 0000000..8d3f688 --- /dev/null +++ b/src/ceph/src/test/ObjectMap/test_object_map.cc @@ -0,0 +1,1131 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +#include "include/memory.h" +#include +#include +#include + +#include "include/buffer.h" +#include "test/ObjectMap/KeyValueDBMemory.h" +#include "kv/KeyValueDB.h" +#include "os/filestore/DBObjectMap.h" +#include "os/filestore/HashIndex.h" +#include +#include "global/global_init.h" +#include "common/ceph_argparse.h" +#include + +#include "gtest/gtest.h" +#include "stdlib.h" + +using namespace std; + +template +typename T::iterator rand_choose(T &cont) { + if (cont.size() == 0) { + return cont.end(); + } + int index = rand() % cont.size(); + typename T::iterator retval = cont.begin(); + + for (; index > 0; --index) ++retval; + return retval; +} + +string num_str(unsigned i) { + char buf[100]; + snprintf(buf, sizeof(buf), "%.10u", i); + return string(buf); +} + +class ObjectMapTester { +public: + ObjectMap *db; + set key_space; + set object_name_space; + map > omap; + map hmap; + map > xattrs; + unsigned seq; + + ObjectMapTester() : db(0), seq(0) {} + + string val_from_key(const string &object, const string &key) { + return object + "_" + key + "_" + num_str(seq++); + } + + void set_key(const string &objname, const string &key, const string &value) { + set_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + key, value); + } + + void set_xattr(const string &objname, const string &key, const string &value) { + set_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + key, value); + } + + void set_key(ghobject_t hoid, + string key, string value) { + map to_write; + bufferptr bp(value.c_str(), value.size()); + bufferlist bl; + bl.append(bp); + to_write.insert(make_pair(key, bl)); + db->set_keys(hoid, to_write); + } + + void set_keys(ghobject_t hoid, const map &to_set) { + map to_write; + for (auto &&i: to_set) { + bufferptr bp(i.second.data(), i.second.size()); + bufferlist bl; + bl.append(bp); + to_write.insert(make_pair(i.first, bl)); + } + db->set_keys(hoid, to_write); + } + + void set_xattr(ghobject_t hoid, + string key, string value) { + map to_write; + bufferptr bp(value.c_str(), value.size()); + bufferlist bl; + bl.append(bp); + to_write.insert(make_pair(key, bl)); + db->set_xattrs(hoid, to_write); + } + + void set_header(const string &objname, const string &value) { + set_header(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + value); + } + + void set_header(ghobject_t hoid, + const string &value) { + bufferlist header; + header.append(bufferptr(value.c_str(), value.size() + 1)); + db->set_header(hoid, header); + } + + int get_header(const string &objname, string *value) { + return get_header(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + value); + } + + int get_header(ghobject_t hoid, + string *value) { + bufferlist header; + int r = db->get_header(hoid, &header); + if (r < 0) + return r; + if (header.length()) + *value = string(header.c_str()); + else + *value = string(""); + return 0; + } + + int get_xattr(const string &objname, const string &key, string *value) { + return get_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + key, value); + } + + int get_xattr(ghobject_t hoid, + string key, string *value) { + set to_get; + to_get.insert(key); + map got; + db->get_xattrs(hoid, to_get, &got); + if (!got.empty()) { + *value = string(got.begin()->second.c_str(), + got.begin()->second.length()); + return 1; + } else { + return 0; + } + } + + int get_key(const string &objname, const string &key, string *value) { + return get_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + key, value); + } + + int get_key(ghobject_t hoid, + string key, string *value) { + set to_get; + to_get.insert(key); + map got; + db->get_values(hoid, to_get, &got); + if (!got.empty()) { + if (value) { + *value = string(got.begin()->second.c_str(), + got.begin()->second.length()); + } + return 1; + } else { + return 0; + } + } + + void remove_key(const string &objname, const string &key) { + remove_key(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + key); + } + + void remove_keys(const string &objname, const set &to_remove) { + remove_keys(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + to_remove); + } + + void remove_key(ghobject_t hoid, + string key) { + set to_remove; + to_remove.insert(key); + db->rm_keys(hoid, to_remove); + } + + void remove_keys(ghobject_t hoid, + const set &to_remove) { + db->rm_keys(hoid, to_remove); + } + + void remove_xattr(const string &objname, const string &key) { + remove_xattr(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + key); + } + + void remove_xattr(ghobject_t hoid, + string key) { + set to_remove; + to_remove.insert(key); + db->remove_xattrs(hoid, to_remove); + } + + void clone(const string &objname, const string &target) { + clone(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP)))); + } + + void clone(ghobject_t hoid, + ghobject_t hoid2) { + db->clone(hoid, hoid2); + } + + void rename(const string &objname, const string &target) { + rename(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP)))); + } + + void rename(ghobject_t hoid, + ghobject_t hoid2) { + db->rename(hoid, hoid2); + } + + void clear(const string &objname) { + clear(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP)))); + } + + void legacy_clone(const string &objname, const string &target) { + legacy_clone(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP))), + ghobject_t(hobject_t(sobject_t(target, CEPH_NOSNAP)))); + } + + void legacy_clone(ghobject_t hoid, + ghobject_t hoid2) { + db->legacy_clone(hoid, hoid2); + } + + void clear(ghobject_t hoid) { + db->clear(hoid); + } + + void clear_omap(const string &objname) { + clear_omap(ghobject_t(hobject_t(sobject_t(objname, CEPH_NOSNAP)))); + } + + void clear_omap(const ghobject_t &objname) { + db->clear_keys_header(objname); + } + + void def_init() { + for (unsigned i = 0; i < 10000; ++i) { + key_space.insert("key_" + num_str(i)); + } + for (unsigned i = 0; i < 100; ++i) { + object_name_space.insert("name_" + num_str(i)); + } + } + + void init_key_set(const set &keys) { + key_space = keys; + } + + void init_object_name_space(const set &onamespace) { + object_name_space = onamespace; + } + + void auto_set_xattr(ostream &out) { + set::iterator key = rand_choose(key_space); + set::iterator object = rand_choose(object_name_space); + + string value = val_from_key(*object, *key); + + xattrs[*object][*key] = value; + set_xattr(*object, *key, value); + + out << "auto_set_xattr " << *object << ": " << *key << " -> " + << value << std::endl; + } + + void test_set_key(const string &obj, const string &key, const string &val) { + omap[obj][key] = val; + set_key(obj, key, val); + } + + void test_set_keys(const string &obj, const map &to_set) { + for (auto &&i: to_set) { + omap[obj][i.first] = i.second; + } + set_keys( + ghobject_t(hobject_t(sobject_t(obj, CEPH_NOSNAP))), + to_set); + } + + void auto_set_keys(ostream &out) { + set::iterator object = rand_choose(object_name_space); + + map to_set; + unsigned amount = (rand() % 10) + 1; + for (unsigned i = 0; i < amount; ++i) { + set::iterator key = rand_choose(key_space); + string value = val_from_key(*object, *key); + out << "auto_set_key " << *object << ": " << *key << " -> " + << value << std::endl; + to_set.insert(make_pair(*key, value)); + } + + + test_set_keys(*object, to_set); + } + + void xattrs_on_object(const string &object, set *out) { + if (!xattrs.count(object)) + return; + const map &xmap = xattrs.find(object)->second; + for (map::const_iterator i = xmap.begin(); + i != xmap.end(); + ++i) { + out->insert(i->first); + } + } + + void keys_on_object(const string &object, set *out) { + if (!omap.count(object)) + return; + const map &kmap = omap.find(object)->second; + for (map::const_iterator i = kmap.begin(); + i != kmap.end(); + ++i) { + out->insert(i->first); + } + } + + void xattrs_off_object(const string &object, set *out) { + *out = key_space; + set xspace; + xattrs_on_object(object, &xspace); + for (set::iterator i = xspace.begin(); + i != xspace.end(); + ++i) { + out->erase(*i); + } + } + + void keys_off_object(const string &object, set *out) { + *out = key_space; + set kspace; + keys_on_object(object, &kspace); + for (set::iterator i = kspace.begin(); + i != kspace.end(); + ++i) { + out->erase(*i); + } + } + + int auto_check_present_xattr(ostream &out) { + set::iterator object = rand_choose(object_name_space); + set xspace; + xattrs_on_object(*object, &xspace); + set::iterator key = rand_choose(xspace); + if (key == xspace.end()) { + return 1; + } + + string result; + int r = get_xattr(*object, *key, &result); + if (!r) { + out << "auto_check_present_key: failed to find key " + << *key << " on object " << *object << std::endl; + return 0; + } + + if (result != xattrs[*object][*key]) { + out << "auto_check_present_key: for key " + << *key << " on object " << *object + << " found value " << result << " where we should have found " + << xattrs[*object][*key] << std::endl; + return 0; + } + + out << "auto_check_present_key: for key " + << *key << " on object " << *object + << " found value " << result << " where we should have found " + << xattrs[*object][*key] << std::endl; + return 1; + } + + + int auto_check_present_key(ostream &out) { + set::iterator object = rand_choose(object_name_space); + set kspace; + keys_on_object(*object, &kspace); + set::iterator key = rand_choose(kspace); + if (key == kspace.end()) { + return 1; + } + + string result; + int r = get_key(*object, *key, &result); + if (!r) { + out << "auto_check_present_key: failed to find key " + << *key << " on object " << *object << std::endl; + return 0; + } + + if (result != omap[*object][*key]) { + out << "auto_check_present_key: for key " + << *key << " on object " << *object + << " found value " << result << " where we should have found " + << omap[*object][*key] << std::endl; + return 0; + } + + out << "auto_check_present_key: for key " + << *key << " on object " << *object + << " found value " << result << " where we should have found " + << omap[*object][*key] << std::endl; + return 1; + } + + int auto_check_absent_xattr(ostream &out) { + set::iterator object = rand_choose(object_name_space); + set xspace; + xattrs_off_object(*object, &xspace); + set::iterator key = rand_choose(xspace); + if (key == xspace.end()) { + return 1; + } + + string result; + int r = get_xattr(*object, *key, &result); + if (!r) { + out << "auto_check_absent_key: did not find key " + << *key << " on object " << *object << std::endl; + return 1; + } + + out << "auto_check_basent_key: for key " + << *key << " on object " << *object + << " found value " << result << " where we should have found nothing" + << std::endl; + return 0; + } + + int auto_check_absent_key(ostream &out) { + set::iterator object = rand_choose(object_name_space); + set kspace; + keys_off_object(*object, &kspace); + set::iterator key = rand_choose(kspace); + if (key == kspace.end()) { + return 1; + } + + string result; + int r = get_key(*object, *key, &result); + if (!r) { + out << "auto_check_absent_key: did not find key " + << *key << " on object " << *object << std::endl; + return 1; + } + + out << "auto_check_basent_key: for key " + << *key << " on object " << *object + << " found value " << result << " where we should have found nothing" + << std::endl; + return 0; + } + + void test_clone(const string &object, const string &target, ostream &out) { + clone(object, target); + if (!omap.count(object)) { + out << " source missing."; + omap.erase(target); + } else { + out << " source present."; + omap[target] = omap[object]; + } + if (!hmap.count(object)) { + out << " hmap source missing." << std::endl; + hmap.erase(target); + } else { + out << " hmap source present." << std::endl; + hmap[target] = hmap[object]; + } + if (!xattrs.count(object)) { + out << " hmap source missing." << std::endl; + xattrs.erase(target); + } else { + out << " hmap source present." << std::endl; + xattrs[target] = xattrs[object]; + } + } + + void auto_clone_key(ostream &out) { + set::iterator object = rand_choose(object_name_space); + set::iterator target = rand_choose(object_name_space); + while (target == object) { + target = rand_choose(object_name_space); + } + out << "clone " << *object << " to " << *target; + test_clone(*object, *target, out); + } + + void test_remove_keys(const string &obj, const set &to_remove) { + for (auto &&k: to_remove) + omap[obj].erase(k); + remove_keys(obj, to_remove); + } + + void test_remove_key(const string &obj, const string &key) { + omap[obj].erase(key); + remove_key(obj, key); + } + + void auto_remove_keys(ostream &out) { + set::iterator object = rand_choose(object_name_space); + set kspace; + keys_on_object(*object, &kspace); + set to_remove; + for (unsigned i = 0; i < 3; ++i) { + set::iterator key = rand_choose(kspace); + if (key == kspace.end()) + continue; + out << "removing " << *key << " from " << *object << std::endl; + to_remove.insert(*key); + } + test_remove_keys(*object, to_remove); + } + + void auto_remove_xattr(ostream &out) { + set::iterator object = rand_choose(object_name_space); + set kspace; + xattrs_on_object(*object, &kspace); + set::iterator key = rand_choose(kspace); + if (key == kspace.end()) { + return; + } + out << "removing xattr " << *key << " from " << *object << std::endl; + xattrs[*object].erase(*key); + remove_xattr(*object, *key); + } + + void auto_delete_object(ostream &out) { + set::iterator object = rand_choose(object_name_space); + out << "auto_delete_object " << *object << std::endl; + clear(*object); + omap.erase(*object); + hmap.erase(*object); + xattrs.erase(*object); + } + + void test_clear(const string &obj) { + clear_omap(obj); + omap.erase(obj); + hmap.erase(obj); + } + + void auto_clear_omap(ostream &out) { + set::iterator object = rand_choose(object_name_space); + out << "auto_clear_object " << *object << std::endl; + test_clear(*object); + } + + void auto_write_header(ostream &out) { + set::iterator object = rand_choose(object_name_space); + string header = val_from_key(*object, "HEADER"); + out << "auto_write_header: " << *object << " -> " << header << std::endl; + set_header(*object, header); + hmap[*object] = header; + } + + int auto_verify_header(ostream &out) { + set::iterator object = rand_choose(object_name_space); + out << "verify_header: " << *object << " "; + string header; + int r = get_header(*object, &header); + if (r < 0) { + ceph_abort(); + } + if (header.size() == 0) { + if (hmap.count(*object)) { + out << " failed to find header " << hmap[*object] << std::endl; + return 0; + } else { + out << " found no header" << std::endl; + return 1; + } + } + + if (!hmap.count(*object)) { + out << " found header " << header << " should have been empty" + << std::endl; + return 0; + } else if (header == hmap[*object]) { + out << " found correct header " << header << std::endl; + return 1; + } else { + out << " found incorrect header " << header + << " where we should have found " << hmap[*object] << std::endl; + return 0; + } + } + + void verify_keys(const std::string &obj, ostream &out) { + set in_db; + ObjectMap::ObjectMapIterator iter = db->get_iterator( + ghobject_t(hobject_t(sobject_t(obj, CEPH_NOSNAP)))); + for (iter->seek_to_first(); iter->valid(); iter->next()) { + in_db.insert(iter->key()); + } + bool err = false; + for (auto &&i: omap[obj]) { + if (!in_db.count(i.first)) { + out << __func__ << ": obj " << obj << " missing key " + << i.first << std::endl; + err = true; + } else { + in_db.erase(i.first); + } + } + if (!in_db.empty()) { + out << __func__ << ": obj " << obj << " found extra keys " + << in_db << std::endl; + err = true; + } + ASSERT_FALSE(err); + } + + void auto_verify_objects(ostream &out) { + for (auto &&i: omap) { + verify_keys(i.first, out); + } + } +}; + +class ObjectMapTest : public ::testing::Test { +public: + boost::scoped_ptr< ObjectMap > db; + ObjectMapTester tester; + void SetUp() override { + char *path = getenv("OBJECT_MAP_PATH"); + if (!path) { + db.reset(new DBObjectMap(g_ceph_context, new KeyValueDBMemory())); + tester.db = db.get(); + return; + } + + string strpath(path); + + cerr << "using path " << strpath << std::endl; + KeyValueDB *store = KeyValueDB::create(g_ceph_context, "leveldb", strpath); + assert(!store->create_and_open(cerr)); + + db.reset(new DBObjectMap(g_ceph_context, store)); + tester.db = db.get(); + } + + void TearDown() override { + std::cerr << "Checking..." << std::endl; + ASSERT_EQ(0, db->check(std::cerr)); + } +}; + + +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); + return RUN_ALL_TESTS(); +} + +TEST_F(ObjectMapTest, CreateOneObject) { + ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)), 100, shard_id_t(0)); + map to_set; + string key("test"); + string val("test_val"); + bufferptr bp(val.c_str(), val.size()); + bufferlist bl; + bl.append(bp); + to_set.insert(make_pair(key, bl)); + ASSERT_EQ(db->set_keys(hoid, to_set), 0); + + map got; + set to_get; + to_get.insert(key); + to_get.insert("not there"); + db->get_values(hoid, to_get, &got); + ASSERT_EQ(got.size(), (unsigned)1); + ASSERT_EQ(string(got[key].c_str(), got[key].length()), val); + + bufferlist header; + got.clear(); + db->get(hoid, &header, &got); + ASSERT_EQ(got.size(), (unsigned)1); + ASSERT_EQ(string(got[key].c_str(), got[key].length()), val); + ASSERT_EQ(header.length(), (unsigned)0); + + db->rm_keys(hoid, to_get); + got.clear(); + db->get(hoid, &header, &got); + ASSERT_EQ(got.size(), (unsigned)0); + + map attrs; + attrs["attr1"] = bl; + db->set_xattrs(hoid, attrs); + + db->set_header(hoid, bl); + + db->clear_keys_header(hoid); + set attrs_got; + db->get_all_xattrs(hoid, &attrs_got); + ASSERT_EQ(attrs_got.size(), 1U); + ASSERT_EQ(*(attrs_got.begin()), "attr1"); + db->get(hoid, &header, &got); + ASSERT_EQ(got.size(), (unsigned)0); + ASSERT_EQ(header.length(), 0U); + got.clear(); + + db->clear(hoid); + db->get(hoid, &header, &got); + ASSERT_EQ(got.size(), (unsigned)0); + attrs_got.clear(); + db->get_all_xattrs(hoid, &attrs_got); + ASSERT_EQ(attrs_got.size(), 0U); +} + +TEST_F(ObjectMapTest, CloneOneObject) { + ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP)), 200, shard_id_t(0)); + ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP)), 201, shard_id_t(1)); + + tester.set_key(hoid, "foo", "bar"); + tester.set_key(hoid, "foo2", "bar2"); + string result; + int r = tester.get_key(hoid, "foo", &result); + ASSERT_EQ(r, 1); + ASSERT_EQ(result, "bar"); + + db->clone(hoid, hoid2); + r = tester.get_key(hoid, "foo", &result); + ASSERT_EQ(r, 1); + ASSERT_EQ(result, "bar"); + r = tester.get_key(hoid2, "foo", &result); + ASSERT_EQ(r, 1); + ASSERT_EQ(result, "bar"); + + tester.remove_key(hoid, "foo"); + r = tester.get_key(hoid2, "foo", &result); + ASSERT_EQ(r, 1); + ASSERT_EQ(result, "bar"); + r = tester.get_key(hoid, "foo", &result); + ASSERT_EQ(r, 0); + r = tester.get_key(hoid, "foo2", &result); + ASSERT_EQ(r, 1); + ASSERT_EQ(result, "bar2"); + + tester.set_key(hoid, "foo", "baz"); + tester.remove_key(hoid, "foo"); + r = tester.get_key(hoid, "foo", &result); + ASSERT_EQ(r, 0); + + tester.set_key(hoid, "foo2", "baz"); + tester.remove_key(hoid, "foo2"); + r = tester.get_key(hoid, "foo2", &result); + ASSERT_EQ(r, 0); + + map got; + bufferlist header; + + got.clear(); + db->clear(hoid); + db->get(hoid, &header, &got); + ASSERT_EQ(got.size(), (unsigned)0); + + got.clear(); + r = db->clear(hoid2); + ASSERT_EQ(0, r); + db->get(hoid2, &header, &got); + ASSERT_EQ(got.size(), (unsigned)0); + + tester.set_key(hoid, "baz", "bar"); + got.clear(); + db->get(hoid, &header, &got); + ASSERT_EQ(got.size(), (unsigned)1); + db->clear(hoid); + db->clear(hoid2); +} + +TEST_F(ObjectMapTest, OddEvenClone) { + ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP))); + ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP))); + + for (unsigned i = 0; i < 1000; ++i) { + tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i)); + } + + db->clone(hoid, hoid2); + + int r = 0; + for (unsigned i = 0; i < 1000; ++i) { + string result; + r = tester.get_key(hoid, "foo" + num_str(i), &result); + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + r = tester.get_key(hoid2, "foo" + num_str(i), &result); + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + + if (i % 2) { + tester.remove_key(hoid, "foo" + num_str(i)); + } else { + tester.remove_key(hoid2, "foo" + num_str(i)); + } + } + + for (unsigned i = 0; i < 1000; ++i) { + string result; + string result2; + r = tester.get_key(hoid, "foo" + num_str(i), &result); + int r2 = tester.get_key(hoid2, "foo" + num_str(i), &result2); + if (i % 2) { + ASSERT_EQ(0, r); + ASSERT_EQ(1, r2); + ASSERT_EQ("bar" + num_str(i), result2); + } else { + ASSERT_EQ(0, r2); + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + } + } + + { + ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid); + iter->seek_to_first(); + for (unsigned i = 0; i < 1000; ++i) { + if (!(i % 2)) { + ASSERT_TRUE(iter->valid()); + ASSERT_EQ("foo" + num_str(i), iter->key()); + iter->next(); + } + } + } + + { + ObjectMap::ObjectMapIterator iter2 = db->get_iterator(hoid2); + iter2->seek_to_first(); + for (unsigned i = 0; i < 1000; ++i) { + if (i % 2) { + ASSERT_TRUE(iter2->valid()); + ASSERT_EQ("foo" + num_str(i), iter2->key()); + iter2->next(); + } + } + } + + db->clear(hoid); + db->clear(hoid2); +} + +TEST_F(ObjectMapTest, Rename) { + ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP))); + ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP))); + + for (unsigned i = 0; i < 1000; ++i) { + tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i)); + } + + db->rename(hoid, hoid2); + // Verify rename where target exists + db->clone(hoid2, hoid); + db->rename(hoid, hoid2); + + int r = 0; + for (unsigned i = 0; i < 1000; ++i) { + string result; + r = tester.get_key(hoid2, "foo" + num_str(i), &result); + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + + if (i % 2) { + tester.remove_key(hoid2, "foo" + num_str(i)); + } + } + + for (unsigned i = 0; i < 1000; ++i) { + string result; + r = tester.get_key(hoid2, "foo" + num_str(i), &result); + if (i % 2) { + ASSERT_EQ(0, r); + } else { + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + } + } + + { + ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid2); + iter->seek_to_first(); + for (unsigned i = 0; i < 1000; ++i) { + if (!(i % 2)) { + ASSERT_TRUE(iter->valid()); + ASSERT_EQ("foo" + num_str(i), iter->key()); + iter->next(); + } + } + } + + db->clear(hoid2); +} + +TEST_F(ObjectMapTest, OddEvenOldClone) { + ghobject_t hoid(hobject_t(sobject_t("foo", CEPH_NOSNAP))); + ghobject_t hoid2(hobject_t(sobject_t("foo2", CEPH_NOSNAP))); + + for (unsigned i = 0; i < 1000; ++i) { + tester.set_key(hoid, "foo" + num_str(i), "bar" + num_str(i)); + } + + db->legacy_clone(hoid, hoid2); + + int r = 0; + for (unsigned i = 0; i < 1000; ++i) { + string result; + r = tester.get_key(hoid, "foo" + num_str(i), &result); + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + r = tester.get_key(hoid2, "foo" + num_str(i), &result); + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + + if (i % 2) { + tester.remove_key(hoid, "foo" + num_str(i)); + } else { + tester.remove_key(hoid2, "foo" + num_str(i)); + } + } + + for (unsigned i = 0; i < 1000; ++i) { + string result; + string result2; + r = tester.get_key(hoid, "foo" + num_str(i), &result); + int r2 = tester.get_key(hoid2, "foo" + num_str(i), &result2); + if (i % 2) { + ASSERT_EQ(0, r); + ASSERT_EQ(1, r2); + ASSERT_EQ("bar" + num_str(i), result2); + } else { + ASSERT_EQ(0, r2); + ASSERT_EQ(1, r); + ASSERT_EQ("bar" + num_str(i), result); + } + } + + { + ObjectMap::ObjectMapIterator iter = db->get_iterator(hoid); + iter->seek_to_first(); + for (unsigned i = 0; i < 1000; ++i) { + if (!(i % 2)) { + ASSERT_TRUE(iter->valid()); + ASSERT_EQ("foo" + num_str(i), iter->key()); + iter->next(); + } + } + } + + { + ObjectMap::ObjectMapIterator iter2 = db->get_iterator(hoid2); + iter2->seek_to_first(); + for (unsigned i = 0; i < 1000; ++i) { + if (i % 2) { + ASSERT_TRUE(iter2->valid()); + ASSERT_EQ("foo" + num_str(i), iter2->key()); + iter2->next(); + } + } + } + + db->clear(hoid); + db->clear(hoid2); +} + +TEST_F(ObjectMapTest, RandomTest) { + tester.def_init(); + for (unsigned i = 0; i < 5000; ++i) { + unsigned val = rand(); + val <<= 8; + val %= 100; + if (!(i%100)) + std::cout << "on op " << i + << " val is " << val << std::endl; + + if (val < 7) { + tester.auto_write_header(std::cerr); + } else if (val < 14) { + ASSERT_TRUE(tester.auto_verify_header(std::cerr)); + } else if (val < 30) { + tester.auto_set_keys(std::cerr); + } else if (val < 42) { + tester.auto_set_xattr(std::cerr); + } else if (val < 55) { + ASSERT_TRUE(tester.auto_check_present_key(std::cerr)); + } else if (val < 62) { + ASSERT_TRUE(tester.auto_check_present_xattr(std::cerr)); + } else if (val < 70) { + ASSERT_TRUE(tester.auto_check_absent_key(std::cerr)); + } else if (val < 72) { + ASSERT_TRUE(tester.auto_check_absent_xattr(std::cerr)); + } else if (val < 73) { + tester.auto_clear_omap(std::cerr); + } else if (val < 76) { + tester.auto_delete_object(std::cerr); + } else if (val < 85) { + tester.auto_clone_key(std::cerr); + } else if (val < 92) { + tester.auto_remove_xattr(std::cerr); + } else { + tester.auto_remove_keys(std::cerr); + } + + if (i % 500) { + tester.auto_verify_objects(std::cerr); + } + } +} + +TEST_F(ObjectMapTest, RandomTestNoDeletesXattrs) { + tester.def_init(); + for (unsigned i = 0; i < 5000; ++i) { + unsigned val = rand(); + val <<= 8; + val %= 100; + if (!(i%100)) + std::cout << "on op " << i + << " val is " << val << std::endl; + + if (val < 45) { + tester.auto_set_keys(std::cerr); + } else if (val < 90) { + tester.auto_remove_keys(std::cerr); + } else { + tester.auto_clone_key(std::cerr); + } + + if (i % 500) { + tester.auto_verify_objects(std::cerr); + } + } +} + +string num_to_key(unsigned i) { + char buf[100]; + int ret = snprintf(buf, sizeof(buf), "%010u", i); + assert(ret > 0); + return string(buf, ret); +} + +TEST_F(ObjectMapTest, TestMergeNewCompleteContainBug) { + /* This test exploits a bug in kraken and earlier where merge_new_complete + * could miss complete entries fully contained by a new entry. To get this + * to actually result in an incorrect return value, you need to remove at + * least two values, one before a complete region, and one which occurs in + * the parent after the complete region (but within 20 not yet completed + * parent points of the first value). + */ + for (unsigned i = 10; i < 160; i+=2) { + tester.test_set_key("foo", num_to_key(i), "asdf"); + } + tester.test_clone("foo", "foo2", std::cout); + tester.test_clear("foo"); + + tester.test_set_key("foo2", num_to_key(15), "asdf"); + tester.test_set_key("foo2", num_to_key(13), "asdf"); + tester.test_set_key("foo2", num_to_key(57), "asdf"); + + tester.test_remove_key("foo2", num_to_key(15)); + + set to_remove; + to_remove.insert(num_to_key(13)); + to_remove.insert(num_to_key(58)); + to_remove.insert(num_to_key(60)); + to_remove.insert(num_to_key(62)); + tester.test_remove_keys("foo2", to_remove); + + tester.verify_keys("foo2", std::cout); + ASSERT_EQ(tester.get_key("foo2", num_to_key(10), nullptr), 1); + ASSERT_EQ(tester.get_key("foo2", num_to_key(1), nullptr), 0); + ASSERT_EQ(tester.get_key("foo2", num_to_key(56), nullptr), 1); + // this one triggers the bug + ASSERT_EQ(tester.get_key("foo2", num_to_key(58), nullptr), 0); +} + +TEST_F(ObjectMapTest, TestIterateBug18533) { + /* This test starts with the one immediately above to create a pair of + * complete regions where one contains the other. Then, it deletes the + * key at the start of the contained region. The logic in next_parent() + * skips ahead to the end of the contained region, and we start copying + * values down again from the parent into the child -- including some + * that had actually been deleted. I think this works for any removal + * within the outer complete region after the start of the contained + * region. + */ + for (unsigned i = 10; i < 160; i+=2) { + tester.test_set_key("foo", num_to_key(i), "asdf"); + } + tester.test_clone("foo", "foo2", std::cout); + tester.test_clear("foo"); + + tester.test_set_key("foo2", num_to_key(15), "asdf"); + tester.test_set_key("foo2", num_to_key(13), "asdf"); + tester.test_set_key("foo2", num_to_key(57), "asdf"); + tester.test_set_key("foo2", num_to_key(91), "asdf"); + + tester.test_remove_key("foo2", num_to_key(15)); + + set to_remove; + to_remove.insert(num_to_key(13)); + to_remove.insert(num_to_key(58)); + to_remove.insert(num_to_key(60)); + to_remove.insert(num_to_key(62)); + to_remove.insert(num_to_key(82)); + to_remove.insert(num_to_key(84)); + tester.test_remove_keys("foo2", to_remove); + + //tester.test_remove_key("foo2", num_to_key(15)); also does the trick + tester.test_remove_key("foo2", num_to_key(80)); + + // the iterator in verify_keys will return an extra value + tester.verify_keys("foo2", std::cout); +} +