/************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ //------------------------------------------------------------------------------ // author: Lukasz Janyst // desc: Other tests //------------------------------------------------------------------------------ #include #include "namespace/ns_quarkdb/ConfigurationParser.hh" #include "namespace/ns_quarkdb/QdbContactDetails.hh" #include "namespace/ns_quarkdb/LRU.hh" #include "namespace/utils/PathProcessor.hh" #include #include #include "namespace/ns_quarkdb/tests/MockContainerMD.hh" #include "namespace/utils/BulkNsObjectLocker.hh" //------------------------------------------------------------------------------ // Check the path //------------------------------------------------------------------------------ bool checkPath(const std::vector& elements, size_t depth) { if (elements.size() < depth) { return false; } for (size_t i = 1; i <= depth; ++i) { std::ostringstream o; o << "test" << i; if (elements[i - 1] != o.str()) { return false; } } return true; } TEST(PathSplitter, BasicSanity) { std::string path1 = "/test1/test2/test3/test4/"; std::string path2 = "/test1/test2/test3/test4"; std::string path3 = "test1/test2/test3/test4/"; std::string path4 = "test1/test2/test3/test4"; std::vector elements; elements = eos::common::SplitPath(path1); ASSERT_TRUE(checkPath(elements, 4)); elements.clear(); elements = eos::common::SplitPath(path2); ASSERT_TRUE(checkPath(elements, 4)); elements.clear(); elements = eos::common::SplitPath(path3); ASSERT_TRUE(checkPath(elements, 4)); elements.clear(); elements = eos::common::SplitPath(path4); ASSERT_TRUE(checkPath(elements, 4)); elements.clear(); elements = eos::common::SplitPath("/"); ASSERT_TRUE(elements.empty()); elements.clear(); elements = eos::common::SplitPath(""); ASSERT_TRUE(elements.empty()); } TEST(PathSplitter, DequeTests) { std::string path1 = "/test1/test2/test3/test4/"; std::string path2 = "/test1/test2/test3/test4"; std::string path3 = "test1/test2/test3/test4/"; std::string path4 = "test1/test2/test3/test4"; std::deque dq1,dq2,dq3,dq4; std::deque expected { "test1","test2","test3","test4" }; eos::PathProcessor::insertChunksIntoDeque(dq1, path1); eos::PathProcessor::insertChunksIntoDeque(dq2, path2); eos::PathProcessor::insertChunksIntoDeque(dq3, path3); eos::PathProcessor::insertChunksIntoDeque(dq4, path4); ASSERT_EQ(dq1, expected); ASSERT_EQ(dq2, expected); ASSERT_EQ(dq3, expected); ASSERT_EQ(dq4, expected); } TEST(PathSplitter, DequeTestsNonEmpty) { std::string path1 = "/test1/test2/test3/test4/"; std::string path2 = "/test1/test2/test3/test4"; std::string path3 = "test1/test2/test3/test4/"; std::string path4 = "test1/test2/test3/test4"; std::deque dq1,dq2,dq3,dq4; dq1 = dq2 = dq3 = dq4 = {"foo","bar"}; std::deque expected { "test1","test2","test3","test4","foo","bar" }; eos::PathProcessor::insertChunksIntoDeque(dq1, path1); eos::PathProcessor::insertChunksIntoDeque(dq2, path2); eos::PathProcessor::insertChunksIntoDeque(dq3, path3); eos::PathProcessor::insertChunksIntoDeque(dq4, path4); ASSERT_EQ(dq1, expected); ASSERT_EQ(dq2, expected); ASSERT_EQ(dq3, expected); ASSERT_EQ(dq4, expected); } TEST(LRU, BasicSanity) { struct Entry { explicit Entry(std::uint64_t id) : id_(id) {} ~Entry() = default; std::uint64_t getId() const { return id_; } std::uint64_t id_; }; std::uint64_t max_size = 1000; std::uint64_t delta = 55; eos::LRU cache{max_size}; // Fill completely the cache for (std::uint64_t id = 0; id < max_size; ++id) { ASSERT_TRUE(cache.put(id, std::make_shared(id))); } ASSERT_EQ(max_size, cache.size()); for (std::uint64_t id = 0; id < max_size; ++id) { ASSERT_TRUE(cache.get(id)->getId() == id); } // This triggers a purge of the first 100 elements for (auto extra_id = max_size; extra_id < max_size + delta; ++extra_id) { ASSERT_TRUE(cache.put(extra_id, std::make_shared(extra_id))); } ASSERT_EQ((std::uint64_t)955, cache.size()); std::shared_ptr elem = cache.get(101); ASSERT_TRUE(elem); // Add another max_size elements for (std::uint64_t id = 2 * max_size; id < 3 * max_size; ++id) { ASSERT_TRUE(cache.put(id, std::make_shared(id))); } // Object 101 should still be in cache as we hold a reference to it ASSERT_TRUE(cache.get(101)); // Obect 102 should have been evicted from the cache ASSERT_TRUE(!cache.get(100)); } TEST(PathProcessor, AbsPathTest) { std::string path = "/a/b/c/d/"; eos::PathProcessor::absPath(path); EXPECT_EQ("/a/b/c/d", path); path = "/a/./b/./c/././d"; eos::PathProcessor::absPath(path); EXPECT_EQ("/a/b/c/d", path); path = "/a/./b/./c/././d/../d/../d/e/../"; eos::PathProcessor::absPath(path); EXPECT_EQ("/a/b/c/d", path); path = "/"; eos::PathProcessor::absPath(path); EXPECT_EQ("/", path); path = ".././../../."; eos::PathProcessor::absPath(path); EXPECT_EQ("/", path); path = "/a/./b//./c/////./././d"; eos::PathProcessor::absPath(path); EXPECT_EQ("/a/b/c/d", path); path = "/a/b/././././/../../c/d/.././../e/./f/"; eos::PathProcessor::absPath(path); EXPECT_EQ("/e/f", path); } TEST(QdbContactDetails, BasicSanity) { std::map configuration; ASSERT_THROW(eos::ConfigurationParser::parse(configuration), eos::MDException); configuration["qdb_cluster"] = "example1.cern.ch:1234 example2.cern.ch:2345 example3.cern.ch:3456"; eos::QdbContactDetails cd = eos::ConfigurationParser::parse(configuration); ASSERT_EQ(cd.members.toString(), "example1.cern.ch:1234,example2.cern.ch:2345,example3.cern.ch:3456"); ASSERT_TRUE(cd.password.empty()); configuration["qdb_password"] = "turtles_turtles_etc"; cd = eos::ConfigurationParser::parse(configuration); ASSERT_EQ(cd.members.toString(), "example1.cern.ch:1234,example2.cern.ch:2345,example3.cern.ch:3456"); ASSERT_EQ(cd.password, "turtles_turtles_etc"); } TEST(BulkNSObjectLocker, testBulkNSObjectLocker) { auto mockContainerMD1 = std::make_shared(1); auto mockContainerMD2 = std::make_shared(2); auto mockContainerMD3 = std::make_shared(3); eos::BulkNsObjectLocker bulkObjectLocker; bulkObjectLocker.add(mockContainerMD1); bulkObjectLocker.add(mockContainerMD2); bulkObjectLocker.add(mockContainerMD3); { auto locks = bulkObjectLocker.lockAll(); ASSERT_EQ(3,locks.size()); } auto lockedContainers = eos::MockContainerMD::getReadLockedContainers(); auto unlockedContainers = eos::MockContainerMD::getReadUnlockedContainers(); ASSERT_EQ(3,lockedContainers.size()); ASSERT_EQ(3,unlockedContainers.size()); for(uint64_t i = 1; i <= 3; i++) { ASSERT_EQ(i,lockedContainers[i-1]->getIdentifier().getUnderlyingUInt64()); } for(uint64_t i = 3; i > 0; i--) { ASSERT_EQ(i,lockedContainers[i-1]->getIdentifier().getUnderlyingUInt64()); } eos::MockContainerMD::clearVectors(); } TEST(NSObjectLocker, testNoDeadlockNSObjectLocker) { auto mockContainerMD1 = std::make_shared(1); { eos::IContainerMD::IContainerMDWriteLocker writeLock(mockContainerMD1); eos::IContainerMD::IContainerMDReadLocker readLock(mockContainerMD1); eos::IContainerMD::IContainerMDReadLocker readLock2(mockContainerMD1); eos::IContainerMD::IContainerMDWriteLocker writeLock2(mockContainerMD1); ASSERT_EQ(2,eos::MockContainerMD::getReadLockedContainers().size()); ASSERT_EQ(2,eos::MockContainerMD::getWriteLockedContainers().size()); } ASSERT_EQ(2,eos::MockContainerMD::getReadUnlockedContainers().size()); ASSERT_EQ(2,eos::MockContainerMD::getWriteUnlockedContainers().size()); eos::MockContainerMD::clearVectors(); }