Imported Upstream version 1.5.1
This commit is contained in:
249
cpp/test/libxtreemfs/async_write_handler_test.cpp
Normal file
249
cpp/test/libxtreemfs/async_write_handler_test.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (c) 2012 by Matthias Noack, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "common/test_environment.h"
|
||||
#include "common/test_rpc_server_dir.h"
|
||||
#include "common/test_rpc_server_mrc.h"
|
||||
#include "common/test_rpc_server_osd.h"
|
||||
#include "libxtreemfs/client.h"
|
||||
#include "libxtreemfs/file_handle.h"
|
||||
#include "libxtreemfs/options.h"
|
||||
#include "libxtreemfs/volume.h"
|
||||
#include "libxtreemfs/xtreemfs_exception.h"
|
||||
#include "rpc/client.h"
|
||||
#include "xtreemfs/OSDServiceConstants.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs::pbrpc;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
namespace xtreemfs {
|
||||
namespace rpc {
|
||||
|
||||
class AsyncWriteHandlerTest : public ::testing::Test {
|
||||
protected:
|
||||
static const int kBlockSize = 1024 * 128;
|
||||
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
test_env.options.connect_timeout_s = 3;
|
||||
test_env.options.request_timeout_s = 3;
|
||||
test_env.options.retry_delay_s = 3;
|
||||
test_env.options.enable_async_writes = true;
|
||||
test_env.options.async_writes_max_request_size_kb = 128;
|
||||
test_env.options.async_writes_max_requests = 8;
|
||||
|
||||
test_env.options.periodic_xcap_renewal_interval_s = 2;
|
||||
ASSERT_TRUE(test_env.Start());
|
||||
|
||||
// Open a volume
|
||||
volume = test_env.client->OpenVolume(
|
||||
test_env.volume_name_,
|
||||
NULL, // No SSL options.
|
||||
test_env.options);
|
||||
|
||||
// Open a file.
|
||||
file = volume->OpenFile(
|
||||
test_env.user_credentials,
|
||||
"/test_file",
|
||||
static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_TRUNC |
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDWR));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
//volume->Close();
|
||||
test_env.Stop();
|
||||
}
|
||||
|
||||
TestEnvironment test_env;
|
||||
Volume* volume;
|
||||
FileHandle* file;
|
||||
};
|
||||
|
||||
|
||||
/** A normal async write with nothing special */
|
||||
TEST_F(AsyncWriteHandlerTest, NormalWrite) {
|
||||
size_t blocks = 5;
|
||||
size_t buffer_size = kBlockSize * blocks;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]());
|
||||
|
||||
vector<WriteEntry> expected(blocks);
|
||||
for (size_t i = 0; i < blocks; ++i) {
|
||||
expected[i] = WriteEntry(i, 0, kBlockSize);
|
||||
}
|
||||
|
||||
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
|
||||
ASSERT_NO_THROW(file->Flush());
|
||||
|
||||
EXPECT_TRUE(equal(expected.begin(),
|
||||
expected.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().end() - blocks));
|
||||
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
}
|
||||
|
||||
/** Let the first write request fail. The write should be retried and finally
|
||||
* succeed. */
|
||||
TEST_F(AsyncWriteHandlerTest, FirstWriteFail) {
|
||||
size_t blocks = 5;
|
||||
size_t buffer_size = kBlockSize * blocks;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]());
|
||||
|
||||
vector<WriteEntry> expected_tail(blocks);
|
||||
for (size_t i = 0; i < blocks; ++i) {
|
||||
expected_tail[i] = WriteEntry(i, 0, kBlockSize);
|
||||
}
|
||||
|
||||
test_env.osds[0]->AddDropRule(
|
||||
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));
|
||||
|
||||
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
|
||||
ASSERT_NO_THROW(file->Flush());
|
||||
|
||||
EXPECT_TRUE(equal(expected_tail.begin(), expected_tail.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().end() - expected_tail.size()));
|
||||
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
}
|
||||
|
||||
/** Let the last write request fail. The write should be retried and finally
|
||||
* succeed. */
|
||||
TEST_F(AsyncWriteHandlerTest, LastWriteFail) {
|
||||
size_t blocks = 5;
|
||||
size_t buffer_size = kBlockSize * blocks;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]());
|
||||
|
||||
vector<WriteEntry> expected_front(blocks - 1);
|
||||
vector<WriteEntry> expected_tail(1);
|
||||
for (size_t i = 0; i < blocks - 1; ++i) {
|
||||
expected_front[i] = WriteEntry(i, 0, kBlockSize);
|
||||
}
|
||||
|
||||
expected_tail[0] = WriteEntry(blocks - 1, 0, kBlockSize);
|
||||
|
||||
test_env.osds[0]->AddDropRule(
|
||||
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
|
||||
new SkipMDropNRule(blocks - 1, 1)));
|
||||
|
||||
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
|
||||
ASSERT_NO_THROW(file->Flush());
|
||||
|
||||
EXPECT_TRUE(equal(expected_front.begin(),
|
||||
expected_front.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().begin()));
|
||||
EXPECT_TRUE(equal(expected_tail.begin(),
|
||||
expected_tail.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().end() -
|
||||
expected_tail.size()));
|
||||
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
}
|
||||
|
||||
|
||||
/** Let the intermediate write request fail. The write should be retried and
|
||||
* finally succeed. */
|
||||
TEST_F(AsyncWriteHandlerTest, IntermediateWriteFail) {
|
||||
size_t blocks = 5;
|
||||
size_t buffer_size = kBlockSize * blocks;
|
||||
size_t middle = blocks / 2;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]());
|
||||
|
||||
vector<WriteEntry> expected_front(middle);
|
||||
vector<WriteEntry> expected_tail(blocks - middle);
|
||||
for (size_t i = 0; i < middle; ++i) {
|
||||
expected_front[i] = WriteEntry(i, 0, kBlockSize);
|
||||
}
|
||||
|
||||
for (size_t i = middle; i < blocks; ++i) {
|
||||
expected_tail[i - middle] = WriteEntry(i, 0, kBlockSize);
|
||||
}
|
||||
|
||||
test_env.osds[0]->AddDropRule(
|
||||
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
|
||||
new SkipMDropNRule(middle, 1)));
|
||||
|
||||
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
|
||||
ASSERT_NO_THROW(file->Flush());
|
||||
|
||||
EXPECT_TRUE(equal(expected_front.begin(),
|
||||
expected_front.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().begin()));
|
||||
EXPECT_TRUE(equal(expected_tail.begin(),
|
||||
expected_tail.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().end() -
|
||||
expected_tail.size()));
|
||||
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
}
|
||||
|
||||
|
||||
/** Let the all writes request fail. The write should be retried and finally
|
||||
* succeed. */
|
||||
TEST_F(AsyncWriteHandlerTest, AllWritesFail) {
|
||||
size_t blocks = 5;
|
||||
size_t buffer_size = kBlockSize * blocks;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]());
|
||||
|
||||
vector<WriteEntry> expected_tail(blocks);
|
||||
for (size_t i = 0; i < blocks; ++i) {
|
||||
expected_tail[i] = WriteEntry(i, 0, kBlockSize);
|
||||
}
|
||||
|
||||
test_env.osds[0]->AddDropRule(
|
||||
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
|
||||
new DropNRule(blocks)));
|
||||
|
||||
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
|
||||
ASSERT_NO_THROW(file->Flush());
|
||||
|
||||
EXPECT_TRUE(equal(expected_tail.begin(),
|
||||
expected_tail.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().end() -
|
||||
expected_tail.size()));
|
||||
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Let the first write request fail when there are more writes than
|
||||
* writeahead allows. The write should be retried and finally succeed. */
|
||||
TEST_F(AsyncWriteHandlerTest, FirstWriteFailLong) {
|
||||
size_t blocks = 2 * test_env.options.async_writes_max_requests;
|
||||
size_t buffer_size = kBlockSize * blocks;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]());
|
||||
|
||||
vector<WriteEntry> expected_tail(blocks);
|
||||
for (size_t i = 0; i < blocks; ++i) {
|
||||
expected_tail[i] = WriteEntry(i, 0, kBlockSize);
|
||||
}
|
||||
|
||||
test_env.osds[0]->AddDropRule(
|
||||
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));
|
||||
|
||||
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
|
||||
ASSERT_NO_THROW(file->Flush());
|
||||
|
||||
EXPECT_TRUE(equal(expected_tail.begin(),
|
||||
expected_tail.end(),
|
||||
test_env.osds[0]->GetReceivedWrites().end() -
|
||||
expected_tail.size()));
|
||||
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
}
|
||||
|
||||
/** TODO(mno): Maybe let all future requests fail to test the retry count. */
|
||||
|
||||
} // namespace rpc
|
||||
} // namespace xtreemfs
|
||||
180
cpp/test/libxtreemfs/client_implementation_test.cpp
Normal file
180
cpp/test/libxtreemfs/client_implementation_test.cpp
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright (c) 2014 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <map>
|
||||
|
||||
#include "common/test_rpc_server_dir.h"
|
||||
#include "libxtreemfs/client.h"
|
||||
#include "libxtreemfs/helper.h"
|
||||
#include "libxtreemfs/options.h"
|
||||
#include "libxtreemfs/pbrpc_url.h"
|
||||
#include "libxtreemfs/xtreemfs_exception.h"
|
||||
#include "xtreemfs/DIR.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs::pbrpc;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
namespace xtreemfs {
|
||||
|
||||
class ClientImplementationTest : public ::testing::Test {
|
||||
protected:
|
||||
/** Mock-up DIR server where custom address mappings can be registered. */
|
||||
class TestRPCServerDIRCustomMapping : public xtreemfs::rpc::TestRPCServerDIR {
|
||||
public:
|
||||
void AddMapping(const string& uuid, const AddressMapping& mapping) {
|
||||
mappings_.insert(pair<string, AddressMapping>(uuid, mapping));
|
||||
}
|
||||
|
||||
private:
|
||||
/** Map from UUID to AddressMapping(s). */
|
||||
std::multimap<string, AddressMapping> mappings_;
|
||||
|
||||
virtual google::protobuf::Message* GetAddressMappingOperation(
|
||||
const pbrpc::Auth& auth,
|
||||
const pbrpc::UserCredentials& user_credentials,
|
||||
const google::protobuf::Message& request,
|
||||
const char* data,
|
||||
uint32_t data_len,
|
||||
boost::scoped_array<char>* response_data,
|
||||
uint32_t* response_data_len) {
|
||||
const addressMappingGetRequest* rq
|
||||
= reinterpret_cast<const addressMappingGetRequest*>(&request);
|
||||
|
||||
AddressMappingSet* response = new AddressMappingSet();
|
||||
|
||||
pair<multimap<string,AddressMapping>::iterator,
|
||||
multimap<string,AddressMapping>::iterator> ret;
|
||||
ret = mappings_.equal_range(rq->uuid());
|
||||
for (multimap<string,AddressMapping>::iterator it = ret.first;
|
||||
it != ret.second;
|
||||
++it) {
|
||||
AddressMapping* mapping = response->add_mappings();
|
||||
mapping->CopyFrom(it->second);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
|
||||
kTestUUID_ = "uuid";
|
||||
kTestPort_ = 12345;
|
||||
|
||||
user_credentials_.set_username("ClientImplementationTest");
|
||||
user_credentials_.add_groups("ClientImplementationTest");
|
||||
|
||||
|
||||
dir_.reset(new TestRPCServerDIRCustomMapping());
|
||||
ASSERT_TRUE(dir_->Start());
|
||||
|
||||
client_.reset(Client::CreateClient(
|
||||
dir_->GetAddress(),
|
||||
user_credentials_,
|
||||
NULL, // No SSL options.
|
||||
options_));
|
||||
|
||||
// Start the client (a connection to the DIR service will be setup).
|
||||
client_->Start();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
if (dir_.get()) {
|
||||
client_->Shutdown();
|
||||
}
|
||||
if (dir_.get()) {
|
||||
dir_->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
void AddAddressMapping(const std::string& match_network,
|
||||
const std::string& hostname) {
|
||||
AddressMapping mapping;
|
||||
mapping.set_uuid(kTestUUID_);
|
||||
mapping.set_version(0);
|
||||
mapping.set_protocol(PBRPCURL::GetSchemePBRPC());
|
||||
mapping.set_address(hostname);
|
||||
mapping.set_port(kTestPort_);
|
||||
mapping.set_match_network(match_network);
|
||||
mapping.set_ttl_s(3600);
|
||||
mapping.set_uri("");
|
||||
|
||||
dir_->AddMapping(kTestUUID_, mapping);
|
||||
}
|
||||
|
||||
std::string ExpectedAddress(const std::string& hostname) {
|
||||
return hostname + ":" + boost::lexical_cast<string>(kTestPort_);
|
||||
}
|
||||
|
||||
boost::scoped_ptr<TestRPCServerDIRCustomMapping> dir_;
|
||||
|
||||
boost::scoped_ptr<Client> client_;
|
||||
|
||||
Options options_;
|
||||
UserCredentials user_credentials_;
|
||||
|
||||
/** Test constant used for all operations. */
|
||||
std::string kTestUUID_;
|
||||
int kTestPort_;
|
||||
};
|
||||
|
||||
|
||||
/** For the requested UUID is exactly one default entry available. */
|
||||
TEST_F(ClientImplementationTest, UUIDToAddressDefaultOnly) {
|
||||
AddAddressMapping("*", "default");
|
||||
|
||||
EXPECT_EQ(ExpectedAddress("default"), client_->UUIDToAddress(kTestUUID_));
|
||||
}
|
||||
|
||||
/** For the requested UUID is no address known. */
|
||||
TEST_F(ClientImplementationTest, UUIDToAddressNoService) {
|
||||
AddAddressMapping("*", "default");
|
||||
|
||||
ASSERT_THROW(client_->UUIDToAddress("unknown-UUID"),
|
||||
AddressToUUIDNotFoundException);
|
||||
}
|
||||
|
||||
/** For the requested UUID is a local network and a default one available. */
|
||||
#ifdef __linux__
|
||||
TEST_F(ClientImplementationTest, UUIDToAddressLocalNetworkAndDefault) {
|
||||
boost::unordered_set<string> local_networks = GetNetworks();
|
||||
// TODO(mberlin): This effectively requires to run the unit test with at
|
||||
// least one network interface. Put an if around this if this
|
||||
// results into flaky tests.
|
||||
ASSERT_GE(local_networks.size(), 1);
|
||||
|
||||
AddAddressMapping(*local_networks.begin(), "local-network");
|
||||
AddAddressMapping("*", "default");
|
||||
|
||||
EXPECT_EQ(ExpectedAddress("local-network"),
|
||||
client_->UUIDToAddress(kTestUUID_));
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
/** Same as "UUIDToAddressLocalNetworkAndDefault", but reverse list order. */
|
||||
#ifdef __linux__
|
||||
TEST_F(ClientImplementationTest, UUIDToAddressDefaultAndLocalNetwork) {
|
||||
boost::unordered_set<string> local_networks = GetNetworks();
|
||||
// TODO(mberlin): This effectively requires to run the unit test with at
|
||||
// least one network interface. Put an if around this if this
|
||||
// results into flaky tests.
|
||||
ASSERT_GE(local_networks.size(), 1);
|
||||
|
||||
AddAddressMapping("*", "default");
|
||||
AddAddressMapping(*local_networks.begin(), "local-network");
|
||||
|
||||
EXPECT_EQ(ExpectedAddress("local-network"),
|
||||
client_->UUIDToAddress(kTestUUID_));
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
} // namespace xtreemfs
|
||||
92
cpp/test/libxtreemfs/helper_test.cpp
Normal file
92
cpp/test/libxtreemfs/helper_test.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2014 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <google/protobuf/stubs/common.h>
|
||||
|
||||
#include "libxtreemfs/helper.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
namespace xtreemfs {
|
||||
|
||||
/** Tests for various helper methods. */
|
||||
class HelperTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
shutdown_logger();
|
||||
atexit(google::protobuf::ShutdownProtobufLibrary);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
TEST_F(HelperTest, GetNetworkStringUnixIPv4) {
|
||||
// Linux's "getifaddrs" returns for each network interface a "struct ifaddrs".
|
||||
// Check that the network is correctly determined based on such a struct.
|
||||
struct ifaddrs ifaddr = {};
|
||||
struct addrinfo hints = {};
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
struct addrinfo* ai_addr;
|
||||
ASSERT_EQ(0, getaddrinfo("127.0.0.1", NULL, &hints, &ai_addr));
|
||||
|
||||
struct addrinfo* ai_netmask;
|
||||
ASSERT_EQ(0, getaddrinfo("255.0.0.0", NULL, &hints, &ai_netmask));
|
||||
|
||||
ifaddr.ifa_next = NULL;
|
||||
ifaddr.ifa_name = (char*) "eth0";
|
||||
ifaddr.ifa_addr = ai_addr->ai_addr;
|
||||
ifaddr.ifa_netmask = ai_netmask->ai_addr;
|
||||
|
||||
EXPECT_EQ("127.0.0.0/8", GetNetworkStringUnix(&ifaddr));
|
||||
|
||||
freeaddrinfo(ai_addr);
|
||||
freeaddrinfo(ai_netmask);
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
#ifdef __linux__
|
||||
TEST_F(HelperTest, GetNetworkStringUnixIPv6) {
|
||||
// Linux's "getifaddrs" returns for each network interface a "struct ifaddrs".
|
||||
// Check that the network is correctly determined based on such a struct.
|
||||
struct ifaddrs ifaddr = {};
|
||||
struct addrinfo hints = {};
|
||||
hints.ai_family = AF_INET6;
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
|
||||
struct addrinfo* ai_addr;
|
||||
ASSERT_EQ(0, getaddrinfo("fe80::b4ff:fe58:a410", NULL, &hints, &ai_addr));
|
||||
|
||||
struct addrinfo* ai_netmask;
|
||||
ASSERT_EQ(0, getaddrinfo("ffff:ffff:ffff:ffff::", NULL, &hints, &ai_netmask));
|
||||
|
||||
ifaddr.ifa_next = NULL;
|
||||
ifaddr.ifa_name = (char*) "eth0";
|
||||
ifaddr.ifa_addr = ai_addr->ai_addr;
|
||||
ifaddr.ifa_netmask = ai_netmask->ai_addr;
|
||||
|
||||
EXPECT_EQ("fe80::/64", GetNetworkStringUnix(&ifaddr));
|
||||
|
||||
freeaddrinfo(ai_addr);
|
||||
freeaddrinfo(ai_netmask);
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
} // namespace xtreemfs
|
||||
335
cpp/test/libxtreemfs/metadata_cache_test.cpp
Normal file
335
cpp/test/libxtreemfs/metadata_cache_test.cpp
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "libxtreemfs/metadata_cache.h"
|
||||
#include "libxtreemfs/helper.h"
|
||||
#include "util/logging.h"
|
||||
#include "xtreemfs/MRC.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs;
|
||||
using namespace xtreemfs::pbrpc;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
class MetadataCacheTestSize2 : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
|
||||
metadata_cache_ = new MetadataCache(2, 3600); // Max 2 entries, 1 hour.
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete metadata_cache_;
|
||||
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
|
||||
shutdown_logger();
|
||||
}
|
||||
|
||||
MetadataCache* metadata_cache_;
|
||||
};
|
||||
|
||||
class MetadataCacheTestSize1024 : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
|
||||
metadata_cache_ = new MetadataCache(1024, 3600); // Max 1k entries, 1 hour.
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete metadata_cache_;
|
||||
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
|
||||
shutdown_logger();
|
||||
}
|
||||
|
||||
MetadataCache* metadata_cache_;
|
||||
};
|
||||
|
||||
/** If a Stat entry gets updated through UpdateStatTime(), the new timeout must
|
||||
* be respected in case of an eviction. */
|
||||
TEST_F(MetadataCacheTestSize2, UpdateStatTimeKeepsSequentialTimeoutOrder) {
|
||||
Stat a, b, c;
|
||||
InitializeStat(&a);
|
||||
InitializeStat(&b);
|
||||
InitializeStat(&c);
|
||||
a.set_ino(0);
|
||||
b.set_ino(1);
|
||||
c.set_ino(2);
|
||||
|
||||
metadata_cache_->UpdateStat("/a", a);
|
||||
metadata_cache_->UpdateStat("/b", b);
|
||||
// Cache is full now. a would be first item to get evicted.
|
||||
metadata_cache_->UpdateStatTime("/a", 0, SETATTR_MTIME);
|
||||
// "b" should be the oldest Stat element now and get evicted.
|
||||
metadata_cache_->UpdateStat("/c", c);
|
||||
// Was "a" found or did "b" survive?
|
||||
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/a", &a));
|
||||
EXPECT_EQ(0, a.ino());
|
||||
// "c" is also still there.
|
||||
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/c", &c));
|
||||
EXPECT_EQ(2, c.ino());
|
||||
}
|
||||
|
||||
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
|
||||
* respected in case of an eviction. */
|
||||
TEST_F(MetadataCacheTestSize2, UpdateStatKeepsSequentialTimeoutOrder) {
|
||||
Stat a, b, c;
|
||||
InitializeStat(&a);
|
||||
InitializeStat(&b);
|
||||
InitializeStat(&c);
|
||||
a.set_ino(0);
|
||||
b.set_ino(1);
|
||||
c.set_ino(2);
|
||||
|
||||
metadata_cache_->UpdateStat("/a", a);
|
||||
metadata_cache_->UpdateStat("/b", b);
|
||||
// Cache is full now. a would be first item to get evicted.
|
||||
metadata_cache_->UpdateStat("/a", a);
|
||||
// "b" should be the oldest Stat element now and get evicted.
|
||||
metadata_cache_->UpdateStat("/c", c);
|
||||
// Was "a" found or did "b" survive?
|
||||
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/a", &a));
|
||||
EXPECT_EQ(0, a.ino());
|
||||
// "c" is also still there.
|
||||
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/c", &c));
|
||||
EXPECT_EQ(2, c.ino());
|
||||
}
|
||||
|
||||
/** Test if Size is updated correctly after UpdateStat() or Invalidate(). */
|
||||
TEST_F(MetadataCacheTestSize2, CheckSizeAfterUpdateAndInvalidate) {
|
||||
Stat a, b, c;
|
||||
InitializeStat(&a);
|
||||
InitializeStat(&b);
|
||||
InitializeStat(&c);
|
||||
EXPECT_EQ(0, metadata_cache_->Size());
|
||||
metadata_cache_->UpdateStat("/a", a);
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
metadata_cache_->UpdateStat("/b", b);
|
||||
EXPECT_EQ(2, metadata_cache_->Size());
|
||||
metadata_cache_->UpdateStat("/c", b);
|
||||
// Cache has only room for two entries.
|
||||
EXPECT_EQ(2, metadata_cache_->Size());
|
||||
|
||||
metadata_cache_->Invalidate("/b");
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
metadata_cache_->Invalidate("/c");
|
||||
EXPECT_EQ(0, metadata_cache_->Size());
|
||||
}
|
||||
|
||||
/** Test (Get|Update)DirEntries methods. */
|
||||
TEST_F(MetadataCacheTestSize2, UpdateAndGetDirEntries) {
|
||||
DirectoryEntries dir_entries;
|
||||
int chunk_size = 1024;
|
||||
int entry_count = chunk_size;
|
||||
string dir = "/";
|
||||
|
||||
// Fill dir_entries;
|
||||
for (int i = 0; i < entry_count; i++) {
|
||||
Stat a;
|
||||
a.set_ino(i);
|
||||
dir_entries.add_entries();
|
||||
dir_entries.mutable_entries(dir_entries.entries_size()-1)
|
||||
->mutable_stbuf()->CopyFrom(a);
|
||||
string path_to_stat = dir + boost::lexical_cast<std::string>(i);
|
||||
dir_entries.mutable_entries(dir_entries.entries_size()-1)
|
||||
->set_name(path_to_stat);
|
||||
}
|
||||
metadata_cache_->UpdateDirEntries(dir, dir_entries);
|
||||
|
||||
boost::scoped_ptr<DirectoryEntries> dir_entries_read;
|
||||
|
||||
// Read all entries.
|
||||
dir_entries_read.reset(metadata_cache_->GetDirEntries(dir, 0, entry_count));
|
||||
EXPECT_EQ(entry_count, dir_entries_read->entries_size());
|
||||
for (int i = 0; i < dir_entries_read->entries_size(); i++) {
|
||||
string path_to_stat = dir + boost::lexical_cast<std::string>(i);
|
||||
EXPECT_EQ(i, dir_entries_read->entries(i).stbuf().ino());
|
||||
EXPECT_EQ(path_to_stat, dir_entries_read->entries(i).name());
|
||||
}
|
||||
|
||||
// Read a subset.
|
||||
dir_entries_read.reset(metadata_cache_
|
||||
->GetDirEntries(dir, entry_count/2, entry_count/2-1));
|
||||
EXPECT_EQ(entry_count/2-1, dir_entries_read->entries_size());
|
||||
int offset = entry_count/2;
|
||||
for (int i = 0; i < dir_entries_read->entries_size(); i++) {
|
||||
string path_to_stat = dir + boost::lexical_cast<std::string>(offset);
|
||||
EXPECT_EQ(path_to_stat, dir_entries_read->entries(i).name());
|
||||
EXPECT_EQ(offset, dir_entries_read->entries(i).stbuf().ino());
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
|
||||
* respected in case of an eviction. */
|
||||
TEST_F(MetadataCacheTestSize1024, InvalidatePrefix) {
|
||||
Stat a, b, c, d;
|
||||
InitializeStat(&a);
|
||||
InitializeStat(&b);
|
||||
InitializeStat(&c);
|
||||
InitializeStat(&d);
|
||||
a.set_ino(0);
|
||||
b.set_ino(1);
|
||||
c.set_ino(2);
|
||||
d.set_ino(3);
|
||||
|
||||
metadata_cache_->UpdateStat("/dir", a);
|
||||
metadata_cache_->UpdateStat("/dir/file1", b);
|
||||
metadata_cache_->UpdateStat("/dir.file1", c);
|
||||
metadata_cache_->UpdateStat("/dirZfile1", d);
|
||||
|
||||
metadata_cache_->InvalidatePrefix("/dir");
|
||||
|
||||
// Invalidate of all matching entries successful?
|
||||
EXPECT_EQ(MetadataCache::kStatNotCached,
|
||||
metadata_cache_->GetStat("/dir", &a));
|
||||
EXPECT_EQ(MetadataCache::kStatNotCached,
|
||||
metadata_cache_->GetStat("/dir/file1", &b));
|
||||
|
||||
// Similiar entries which do not match the prefix "/dir/" have not been
|
||||
// invalidated.
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat("/dir.file1", &c));
|
||||
EXPECT_EQ(2, c.ino());
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat("/dirZfile1", &d));
|
||||
EXPECT_EQ(3, d.ino());
|
||||
}
|
||||
|
||||
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
|
||||
* respected in case of an eviction. */
|
||||
TEST_F(MetadataCacheTestSize1024, RenamePrefix) {
|
||||
Stat a, b, c, d;
|
||||
InitializeStat(&a);
|
||||
InitializeStat(&b);
|
||||
InitializeStat(&c);
|
||||
InitializeStat(&d);
|
||||
a.set_ino(0);
|
||||
b.set_ino(1);
|
||||
c.set_ino(2);
|
||||
d.set_ino(3);
|
||||
|
||||
metadata_cache_->UpdateStat("/dir", a);
|
||||
metadata_cache_->UpdateStat("/dir/file1", b);
|
||||
metadata_cache_->UpdateStat("/dir.file1", c);
|
||||
metadata_cache_->UpdateStat("/dirZfile1", d);
|
||||
EXPECT_EQ(4, metadata_cache_->Size());
|
||||
|
||||
metadata_cache_->RenamePrefix("/dir", "/newdir");
|
||||
EXPECT_EQ(4, metadata_cache_->Size());
|
||||
|
||||
// Rename of all matching entries successful?
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat("/newdir", &a));
|
||||
EXPECT_EQ(0, a.ino());
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat("/newdir/file1", &b));
|
||||
EXPECT_EQ(1, b.ino());
|
||||
|
||||
// Similiar entries which do not match the prefix "/dir/" have not been
|
||||
// renamed.
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat("/dir.file1", &c));
|
||||
EXPECT_EQ(2, c.ino());
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat("/dirZfile1", &d));
|
||||
EXPECT_EQ(3, d.ino());
|
||||
}
|
||||
|
||||
/** Are large nanoseconds values correctly updated by
|
||||
* UpdateStatAttributes? */
|
||||
TEST_F(MetadataCacheTestSize1024, UpdateStatAttributes) {
|
||||
string path = "/file";
|
||||
Stat stat, update_stat;
|
||||
InitializeStat(&stat);
|
||||
InitializeStat(&update_stat);
|
||||
stat.set_ino(0);
|
||||
update_stat.set_ino(1);
|
||||
|
||||
metadata_cache_->UpdateStat(path, stat);
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat(path, &stat));
|
||||
EXPECT_EQ(0, stat.ino());
|
||||
EXPECT_EQ(0, stat.mtime_ns());
|
||||
|
||||
uint64_t time = 1234567890;
|
||||
time *= 1000000000;
|
||||
update_stat.set_atime_ns(time);
|
||||
update_stat.set_mtime_ns(time);
|
||||
metadata_cache_->UpdateStatAttributes(
|
||||
path,
|
||||
update_stat,
|
||||
static_cast<Setattrs>(SETATTR_ATIME | SETATTR_MTIME));
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat(path, &stat));
|
||||
EXPECT_EQ(0, stat.ino());
|
||||
EXPECT_EQ(time, stat.atime_ns());
|
||||
EXPECT_EQ(time, stat.mtime_ns());
|
||||
}
|
||||
|
||||
/** Changing the file access mode may only modify the last 12 bits (3 bits for
|
||||
* sticky bit, set GID and set UID and 3 * 3 bits for the file access mode). */
|
||||
TEST_F(MetadataCacheTestSize1024, UpdateStatAttributesPreservesModeBits) {
|
||||
string path = "/file";
|
||||
Stat stat, cached_stat;
|
||||
InitializeStat(&stat);
|
||||
stat.set_ino(0);
|
||||
stat.set_mode(33188); // Octal: 100644 (regular file + 644).
|
||||
|
||||
metadata_cache_->UpdateStat(path, stat);
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat(path, &cached_stat));
|
||||
EXPECT_EQ(0, cached_stat.ino());
|
||||
EXPECT_EQ(33188, cached_stat.mode());
|
||||
|
||||
stat.set_mode(420); // Octal: 644.
|
||||
metadata_cache_->UpdateStatAttributes(path, stat, SETATTR_MODE);
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat(path, &cached_stat));
|
||||
EXPECT_EQ(0, cached_stat.ino());
|
||||
EXPECT_EQ(33188, cached_stat.mode());
|
||||
|
||||
stat.set_mode(263076); // Octal: 1001644 (regular file + sticky bit + 644).
|
||||
|
||||
metadata_cache_->UpdateStat(path, stat);
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat(path, &cached_stat));
|
||||
EXPECT_EQ(0, cached_stat.ino());
|
||||
EXPECT_EQ(263076, cached_stat.mode());
|
||||
|
||||
stat.set_mode(511); // Octal: 0777 (no sticky bit + 777).
|
||||
metadata_cache_->UpdateStatAttributes(path, stat, SETATTR_MODE);
|
||||
EXPECT_EQ(1, metadata_cache_->Size());
|
||||
EXPECT_EQ(MetadataCache::kStatCached,
|
||||
metadata_cache_->GetStat(path, &cached_stat));
|
||||
EXPECT_EQ(0, cached_stat.ino());
|
||||
EXPECT_EQ(262655, cached_stat.mode()); // Octal: 1000777.
|
||||
}
|
||||
|
||||
/** Ideas:
|
||||
*
|
||||
* test TTL expiration.
|
||||
* test invalidateprefix.
|
||||
*
|
||||
* test correct setting of maximum ttl for every operation
|
||||
*
|
||||
*/
|
||||
292
cpp/test/libxtreemfs/object_cache_test.cpp
Normal file
292
cpp/test/libxtreemfs/object_cache_test.cpp
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c) 2013 by Felix Hupfeld.
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "common/test_environment.h"
|
||||
#include "common/test_rpc_server_dir.h"
|
||||
#include "common/test_rpc_server_mrc.h"
|
||||
#include "common/test_rpc_server_osd.h"
|
||||
#include "libxtreemfs/client.h"
|
||||
#include "libxtreemfs/file_handle.h"
|
||||
#include "libxtreemfs/helper.h"
|
||||
#include "libxtreemfs/object_cache.h"
|
||||
#include "libxtreemfs/options.h"
|
||||
#include "libxtreemfs/volume.h"
|
||||
#include "libxtreemfs/xtreemfs_exception.h"
|
||||
#include "rpc/client.h"
|
||||
#include "rpc/sync_callback.h"
|
||||
#include "util/logging.h"
|
||||
#include "xtreemfs/OSDServiceConstants.h"
|
||||
|
||||
namespace xtreemfs {
|
||||
|
||||
const int kObjectSize = 10;
|
||||
|
||||
class FakeOsdFile {
|
||||
public:
|
||||
FakeOsdFile() : size_(0), reads_(0), writes_(0) {
|
||||
data_.reset(new char[500]);
|
||||
}
|
||||
int Read(int object_no, char* buffer) {
|
||||
reads_++;
|
||||
const int offset = object_no * kObjectSize;
|
||||
const int bytes_to_read = std::min(kObjectSize, size_ - offset);
|
||||
if (bytes_to_read <= 0) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(buffer, &data_[offset], bytes_to_read);
|
||||
return bytes_to_read;
|
||||
}
|
||||
void Write(int object_no, const char* data, int bytes_to_write) {
|
||||
writes_++;
|
||||
const int offset = object_no * kObjectSize;
|
||||
memcpy(&data_[offset], data, bytes_to_write);
|
||||
size_ = std::max(size_, offset + bytes_to_write);
|
||||
}
|
||||
void Truncate(int new_size) {
|
||||
size_ = new_size;
|
||||
}
|
||||
boost::scoped_array<char> data_;
|
||||
int size_;
|
||||
int reads_;
|
||||
int writes_;
|
||||
};
|
||||
|
||||
class ObjectCacheTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
util::initialize_logger(util::LEVEL_WARN);
|
||||
cache_.reset(new ObjectCache(2, kObjectSize));
|
||||
reader_ = boost::bind(&FakeOsdFile::Read, &osd_file_, _1, _2);
|
||||
writer_ = boost::bind(&FakeOsdFile::Write, &osd_file_, _1, _2, _3);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
util::shutdown_logger();
|
||||
}
|
||||
|
||||
FakeOsdFile osd_file_;
|
||||
boost::scoped_ptr<ObjectCache> cache_;
|
||||
ObjectReaderFunction reader_;
|
||||
ObjectWriterFunction writer_;
|
||||
};
|
||||
|
||||
TEST_F(ObjectCacheTest, BasicUse) {
|
||||
cache_->Write(0, 0, "TestData", 9, reader_, writer_);
|
||||
EXPECT_EQ(1, osd_file_.reads_);
|
||||
EXPECT_EQ(0, osd_file_.writes_);
|
||||
EXPECT_EQ(0, osd_file_.size_);
|
||||
|
||||
char buffer[10];
|
||||
EXPECT_EQ(9, cache_->Read(0, 0, buffer, 10, reader_, writer_));
|
||||
EXPECT_EQ(0, strcmp(buffer, "TestData"));
|
||||
EXPECT_EQ(1, osd_file_.reads_);
|
||||
EXPECT_EQ(0, osd_file_.writes_);
|
||||
EXPECT_EQ(0, osd_file_.size_);
|
||||
|
||||
cache_->Flush(writer_);
|
||||
EXPECT_EQ(1, osd_file_.reads_);
|
||||
EXPECT_EQ(1, osd_file_.writes_);
|
||||
EXPECT_EQ(9, osd_file_.size_);
|
||||
|
||||
cache_->Flush(writer_);
|
||||
EXPECT_EQ(1, osd_file_.reads_);
|
||||
EXPECT_EQ(1, osd_file_.writes_);
|
||||
EXPECT_EQ(9, osd_file_.size_);
|
||||
}
|
||||
|
||||
TEST_F(ObjectCacheTest, Truncate) {
|
||||
cache_->Write(0, 0, "TestDataTe", 10, reader_, writer_);
|
||||
cache_->Write(1, 0, "stData", 6, reader_, writer_);
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
EXPECT_EQ(0, osd_file_.writes_);
|
||||
EXPECT_EQ(0, osd_file_.size_);
|
||||
|
||||
// Shrink
|
||||
cache_->Truncate(12);
|
||||
osd_file_.Truncate(12);
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
EXPECT_EQ(0, osd_file_.writes_);
|
||||
EXPECT_EQ(12, osd_file_.size_);
|
||||
|
||||
char buffer[17];
|
||||
EXPECT_EQ(2, cache_->Read(1, 0, buffer, 10, reader_, writer_));
|
||||
EXPECT_EQ(0, strncmp(buffer, "st", 2));
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
EXPECT_EQ(0, osd_file_.writes_);
|
||||
EXPECT_EQ(12, osd_file_.size_);
|
||||
|
||||
cache_->Flush(writer_);
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
EXPECT_EQ(2, osd_file_.writes_);
|
||||
EXPECT_EQ(12, osd_file_.size_);
|
||||
|
||||
// Extend
|
||||
cache_->Truncate(20);
|
||||
osd_file_.Truncate(20);
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
EXPECT_EQ(2, osd_file_.writes_);
|
||||
EXPECT_EQ(20, osd_file_.size_);
|
||||
|
||||
char buffer2[10];
|
||||
EXPECT_EQ(10, cache_->Read(1, 0, buffer2, 10, reader_, writer_));
|
||||
EXPECT_EQ(0, strncmp(buffer2, "st\0\0\0\0\0\0", 10));
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
EXPECT_EQ(2, osd_file_.writes_);
|
||||
EXPECT_EQ(20, osd_file_.size_);
|
||||
|
||||
cache_->Flush(writer_);
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
// We need not to flush out shrunk objects as the layer above us will
|
||||
// take care of it by sending a truncate to the OSD.
|
||||
EXPECT_EQ(2, osd_file_.writes_);
|
||||
EXPECT_EQ(20, osd_file_.size_);
|
||||
}
|
||||
|
||||
TEST_F(ObjectCacheTest, WriteBack) {
|
||||
cache_->Write(0, 0, "TestData", 9, reader_, writer_);
|
||||
EXPECT_EQ(1, osd_file_.reads_);
|
||||
EXPECT_EQ(0, osd_file_.writes_);
|
||||
EXPECT_EQ(0, osd_file_.size_);
|
||||
|
||||
cache_->Write(1, 0, "TestData", 9, reader_, writer_);
|
||||
EXPECT_EQ(2, osd_file_.reads_);
|
||||
EXPECT_EQ(0, osd_file_.writes_);
|
||||
EXPECT_EQ(0, osd_file_.size_);
|
||||
|
||||
cache_->Write(2, 0, "TestData", 9, reader_, writer_);
|
||||
EXPECT_EQ(3, osd_file_.reads_);
|
||||
EXPECT_EQ(1, osd_file_.writes_);
|
||||
EXPECT_EQ(9, osd_file_.size_);
|
||||
|
||||
cache_->Flush(writer_);
|
||||
EXPECT_EQ(3, osd_file_.reads_);
|
||||
EXPECT_EQ(3, osd_file_.writes_);
|
||||
EXPECT_EQ(29, osd_file_.size_);
|
||||
|
||||
// Read back in
|
||||
char buffer[10];
|
||||
EXPECT_EQ(10, cache_->Read(0, 0, buffer, 10, reader_, writer_));
|
||||
EXPECT_EQ(0, strncmp(buffer, "TestData\0\0", 10));
|
||||
EXPECT_EQ(4, osd_file_.reads_);
|
||||
EXPECT_EQ(3, osd_file_.writes_);
|
||||
EXPECT_EQ(29, osd_file_.size_);
|
||||
}
|
||||
|
||||
class ObjectCacheEndToEndTest : public ::testing::Test {
|
||||
protected:
|
||||
static const int kBlockSize = 1024 * 128;
|
||||
|
||||
virtual void SetUp() {
|
||||
util::initialize_logger(util::LEVEL_WARN);
|
||||
test_env.options.connect_timeout_s = 15;
|
||||
test_env.options.request_timeout_s = 5;
|
||||
test_env.options.retry_delay_s = 5;
|
||||
test_env.options.object_cache_size = 2;
|
||||
|
||||
test_env.options.periodic_xcap_renewal_interval_s = 2;
|
||||
ASSERT_TRUE(test_env.Start());
|
||||
|
||||
// Open a volume
|
||||
volume = test_env.client->OpenVolume(
|
||||
test_env.volume_name_,
|
||||
NULL, // No SSL options.
|
||||
test_env.options);
|
||||
|
||||
// Open a file.
|
||||
file = volume->OpenFile(
|
||||
test_env.user_credentials,
|
||||
"/test_file",
|
||||
static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_TRUNC |
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDWR));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
//volume->Close();
|
||||
test_env.Stop();
|
||||
}
|
||||
|
||||
void CheckData(char* data, int buffer_size) {
|
||||
for (int i = 0; i < buffer_size; ++i) {
|
||||
ASSERT_EQ('0' + (i % 3), data[i]);
|
||||
}
|
||||
}
|
||||
void CheckDataIsNull(char* data, int buffer_size) {
|
||||
for (int i = 0; i < buffer_size; ++i) {
|
||||
ASSERT_EQ(0, data[i]);
|
||||
}
|
||||
}
|
||||
void FillData(char* data, int buffer_size) {
|
||||
for (int i = 0; i < buffer_size; ++i) {
|
||||
data[i] = '0' + (i % 3);
|
||||
}
|
||||
}
|
||||
TestEnvironment test_env;
|
||||
Volume* volume;
|
||||
FileHandle* file;
|
||||
};
|
||||
|
||||
TEST_F(ObjectCacheEndToEndTest, Persistence) {
|
||||
const size_t blocks = 5;
|
||||
const size_t buffer_size = kBlockSize * blocks;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]);
|
||||
FillData(write_buf.get(), buffer_size);
|
||||
|
||||
EXPECT_EQ(buffer_size, file->Write(write_buf.get(), buffer_size, 0));
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
|
||||
file = volume->OpenFile(
|
||||
test_env.user_credentials,
|
||||
"/test_file",
|
||||
static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
|
||||
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDONLY));
|
||||
memset(write_buf.get(), 0, buffer_size);
|
||||
EXPECT_EQ(buffer_size, file->Read(write_buf.get(), buffer_size, 0));
|
||||
CheckData(write_buf.get(), buffer_size);
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
}
|
||||
|
||||
TEST_F(ObjectCacheEndToEndTest, NormalWrite) {
|
||||
const size_t blocks = 5;
|
||||
const size_t buffer_size = kBlockSize * blocks;
|
||||
boost::scoped_array<char> write_buf(new char[buffer_size]);
|
||||
FillData(write_buf.get(), buffer_size);
|
||||
|
||||
EXPECT_EQ(0, file->Read(write_buf.get(), buffer_size, 0));
|
||||
|
||||
EXPECT_EQ(buffer_size, file->Write(write_buf.get(), buffer_size, 0));
|
||||
|
||||
EXPECT_EQ(buffer_size, file->Read(write_buf.get(), buffer_size + 5, 0));
|
||||
CheckData(write_buf.get(), buffer_size);
|
||||
|
||||
ASSERT_NO_THROW(file->Flush());
|
||||
|
||||
ASSERT_NO_THROW(file->Truncate(test_env.user_credentials, 0));
|
||||
EXPECT_EQ(0, file->Read(write_buf.get(), buffer_size, 0));
|
||||
|
||||
ASSERT_NO_THROW(file->Truncate(test_env.user_credentials, buffer_size));
|
||||
EXPECT_EQ(buffer_size, file->Read(write_buf.get(), buffer_size, 0));
|
||||
CheckDataIsNull(write_buf.get(), buffer_size);
|
||||
|
||||
file->Write(write_buf.get(), buffer_size, 0);
|
||||
ASSERT_NO_THROW(file->Close());
|
||||
|
||||
EXPECT_EQ(5 + 5, test_env.osds[0]->GetReceivedWrites().size());
|
||||
}
|
||||
|
||||
} // namespace xtreemfs
|
||||
91
cpp/test/libxtreemfs/options_test.cpp
Normal file
91
cpp/test/libxtreemfs/options_test.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2014 by Robert Schmidtke, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "libxtreemfs/options.h"
|
||||
#include "libxtreemfs/xtreemfs_exception.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
namespace xtreemfs {
|
||||
|
||||
class OptionsTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
shutdown_logger();
|
||||
}
|
||||
|
||||
xtreemfs::Options options_;
|
||||
};
|
||||
|
||||
TEST_F(OptionsTest, TestFailOnMultipleLogLevelOptions) {
|
||||
const char* n_argv[] = { "executable", "--log-level=DEBUG", "-dINFO" };
|
||||
ASSERT_THROW(
|
||||
options_.ParseCommandLine(3, const_cast<char**>(n_argv)),
|
||||
InvalidCommandLineParametersException
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(OptionsTest, TestExplicitSpecificationOverride) {
|
||||
const char* n_argv[] = {
|
||||
"executable",
|
||||
"--log-level=DEBUG",
|
||||
"-o", "-d=INFO"
|
||||
};
|
||||
options_.ParseCommandLine(4, const_cast<char**>(n_argv));
|
||||
ASSERT_EQ("DEBUG", options_.log_level_string);
|
||||
}
|
||||
|
||||
TEST_F(OptionsTest, TestExplicitAndAlternativeCombination) {
|
||||
const char* n_argv[] = {
|
||||
"executable",
|
||||
"--log-file-path=/tmp/test.log",
|
||||
"-o", "-d=INFO"
|
||||
};
|
||||
options_.ParseCommandLine(4, const_cast<char**>(n_argv));
|
||||
ASSERT_EQ("/tmp/test.log", options_.log_file_path);
|
||||
ASSERT_EQ("INFO", options_.log_level_string);
|
||||
}
|
||||
|
||||
TEST_F(OptionsTest, TestMultipleLongAlternativeOptions) {
|
||||
const char* n_argv[] = {
|
||||
"executable",
|
||||
"-o", "log-file-path=/tmp/test.log",
|
||||
"-o", "log-level=INFO"
|
||||
};
|
||||
options_.ParseCommandLine(5, const_cast<char**>(n_argv));
|
||||
ASSERT_EQ("/tmp/test.log", options_.log_file_path);
|
||||
ASSERT_EQ("INFO", options_.log_level_string);
|
||||
}
|
||||
|
||||
TEST_F(OptionsTest, TestCommaSeparatedAlternativeOptions) {
|
||||
const char* n_argv[] = {
|
||||
"executable",
|
||||
"-o", "log-file-path=/tmp/test.log,-d=INFO"
|
||||
};
|
||||
options_.ParseCommandLine(3, const_cast<char**>(n_argv));
|
||||
ASSERT_EQ("/tmp/test.log", options_.log_file_path);
|
||||
ASSERT_EQ("INFO", options_.log_level_string);
|
||||
}
|
||||
|
||||
TEST_F(OptionsTest, TestFstabExample) {
|
||||
const char* n_argv[] = {
|
||||
"executable",
|
||||
"-o", "_netdev,pkcs12-file-path=/tmp/pkcs12.p12,log-level=DEBUG"
|
||||
};
|
||||
options_.ParseCommandLine(3, const_cast<char**>(n_argv));
|
||||
ASSERT_EQ("/tmp/pkcs12.p12", options_.ssl_pkcs12_path);
|
||||
ASSERT_EQ("DEBUG", options_.log_level_string);
|
||||
}
|
||||
|
||||
} // namespace xtreemfs
|
||||
222
cpp/test/libxtreemfs/pbprpc_url_test.cpp
Normal file
222
cpp/test/libxtreemfs/pbprpc_url_test.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2012 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "libxtreemfs/pbrpc_url.h"
|
||||
#include "libxtreemfs/xtreemfs_exception.h"
|
||||
#include "util/logging.h"
|
||||
#include "xtreemfs/GlobalTypes.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs::pbrpc;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
namespace xtreemfs {
|
||||
|
||||
class PBRPCURLTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
|
||||
volume_name = "test";
|
||||
default_scheme = PBRPCURL::GetSchemePBRPC();
|
||||
default_port = DIR_PBRPC_PORT_DEFAULT;
|
||||
|
||||
servers.push_back("localhost");
|
||||
servers.push_back("127.0.0.1");
|
||||
servers.push_back("somehost");
|
||||
|
||||
ports.push_back(1234);
|
||||
ports.push_back(4242);
|
||||
ports.push_back(31337);
|
||||
|
||||
pbrpc_url_.reset(new PBRPCURL());
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
shutdown_logger();
|
||||
}
|
||||
|
||||
typedef list<string> ServerList;
|
||||
typedef list<uint16_t> PortList;
|
||||
|
||||
ServerList servers;
|
||||
PortList ports;
|
||||
string volume_name;
|
||||
string default_scheme;
|
||||
uint16_t default_port;
|
||||
|
||||
boost::scoped_ptr<PBRPCURL> pbrpc_url_;
|
||||
};
|
||||
|
||||
TEST_F(PBRPCURLTest, URLWithOneAddressAndVolume) {
|
||||
stringstream url_to_parse;
|
||||
url_to_parse << servers.front() << '/' << volume_name;
|
||||
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
|
||||
|
||||
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
|
||||
|
||||
EXPECT_FALSE(addresses.empty());
|
||||
EXPECT_EQ(volume_name, pbrpc_url_->volume());
|
||||
EXPECT_EQ(PBRPCURL::GetSchemePBRPC(), pbrpc_url_->scheme());
|
||||
|
||||
stringstream expected_address;
|
||||
expected_address << "localhost:" << DIR_PBRPC_PORT_DEFAULT;
|
||||
EXPECT_EQ(expected_address.str(), addresses.GetAddresses().front());
|
||||
}
|
||||
|
||||
TEST_F(PBRPCURLTest, URLWithMultipleAddressesAndVolume) {
|
||||
// Build an URL to parse
|
||||
stringstream url_to_parse;
|
||||
ServerList::const_iterator servers_it = servers.begin();
|
||||
|
||||
for (; servers_it != servers.end(); ++servers_it) {
|
||||
if(servers_it != servers.begin()) {
|
||||
url_to_parse << ',';
|
||||
}
|
||||
url_to_parse << *servers_it;
|
||||
}
|
||||
url_to_parse << '/' << volume_name;
|
||||
|
||||
// Parse URL and get addresses
|
||||
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
|
||||
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
|
||||
|
||||
// Check expectations
|
||||
EXPECT_EQ(servers.size(), addresses.size());
|
||||
EXPECT_EQ(volume_name, pbrpc_url_->volume());
|
||||
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
|
||||
|
||||
servers_it = servers.begin();
|
||||
stringstream expected_address;
|
||||
ServiceAddresses::Addresses services = addresses.GetAddresses();
|
||||
for(ServiceAddresses::Addresses::iterator it = services.begin();
|
||||
it != services.end();
|
||||
++it, ++servers_it) {
|
||||
expected_address.str("");
|
||||
expected_address << *servers_it << ':' << default_port;
|
||||
EXPECT_EQ(expected_address.str(), *it);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PBRPCURLTest, URLWithMultipleAddressesPortsAndVolume) {
|
||||
// Build an URL to parse
|
||||
stringstream url_to_parse;
|
||||
ServerList::const_iterator servers_it = servers.begin();
|
||||
PortList::const_iterator ports_it = ports.begin();
|
||||
|
||||
for (; servers_it != servers.end(); ++servers_it, ++ports_it) {
|
||||
if(servers_it != servers.begin()) {
|
||||
url_to_parse << ',';
|
||||
}
|
||||
url_to_parse << *servers_it << ':' << *ports_it;
|
||||
}
|
||||
url_to_parse << '/' << volume_name;
|
||||
|
||||
// Parse URL and get addresses
|
||||
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
|
||||
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
|
||||
|
||||
// Check expectations
|
||||
EXPECT_EQ(servers.size(), addresses.size());
|
||||
EXPECT_EQ(volume_name, pbrpc_url_->volume());
|
||||
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
|
||||
|
||||
servers_it = servers.begin();
|
||||
ports_it = ports.begin();
|
||||
stringstream expected_address;
|
||||
ServiceAddresses::Addresses services = addresses.GetAddresses();
|
||||
for(ServiceAddresses::Addresses::iterator it = services.begin();
|
||||
it != services.end();
|
||||
++it, ++servers_it, ++ports_it) {
|
||||
expected_address.str("");
|
||||
expected_address << *servers_it << ':' << *ports_it;
|
||||
EXPECT_EQ(expected_address.str(), *it);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PBRPCURLTest, URLWithMultipleAddressesProtocolsPortsAndVolume) {
|
||||
// Build an URL to parse
|
||||
stringstream url_to_parse;
|
||||
ServerList::const_iterator servers_it = servers.begin();
|
||||
PortList::const_iterator ports_it = ports.begin();
|
||||
|
||||
for (; servers_it != servers.end(); ++servers_it, ++ports_it) {
|
||||
if(servers_it != servers.begin()) {
|
||||
url_to_parse << ',';
|
||||
}
|
||||
url_to_parse << default_scheme << "://" << *servers_it << ':' << *ports_it;
|
||||
}
|
||||
url_to_parse << '/' << volume_name;
|
||||
// Parse URL and get addresses
|
||||
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
|
||||
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
|
||||
|
||||
// Check expectations
|
||||
EXPECT_EQ(servers.size(), addresses.size());
|
||||
EXPECT_EQ(volume_name, pbrpc_url_->volume());
|
||||
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
|
||||
|
||||
servers_it = servers.begin();
|
||||
ports_it = ports.begin();
|
||||
stringstream expected_address;
|
||||
ServiceAddresses::Addresses services = addresses.GetAddresses();
|
||||
for(ServiceAddresses::Addresses::iterator it = services.begin();
|
||||
it != services.end();
|
||||
++it, ++servers_it, ++ports_it) {
|
||||
expected_address.str("");
|
||||
expected_address << *servers_it << ':' << *ports_it;
|
||||
EXPECT_EQ(expected_address.str(), *it);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PBRPCURLTest, URLWithMultipleAddressesProtocolsAndVolume) {
|
||||
// Build an URL to parse
|
||||
stringstream url_to_parse;
|
||||
ServerList::const_iterator servers_it = servers.begin();
|
||||
|
||||
for (; servers_it != servers.end(); ++servers_it) {
|
||||
if(servers_it != servers.begin()) {
|
||||
url_to_parse << ',';
|
||||
}
|
||||
url_to_parse << default_scheme << "://" << *servers_it;
|
||||
}
|
||||
url_to_parse << '/' << volume_name;
|
||||
// Parse URL and get addresses
|
||||
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
|
||||
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
|
||||
|
||||
// Check expectations
|
||||
EXPECT_EQ(servers.size(), addresses.size());
|
||||
EXPECT_EQ(volume_name, pbrpc_url_->volume());
|
||||
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
|
||||
|
||||
servers_it = servers.begin();
|
||||
stringstream expected_address;
|
||||
ServiceAddresses::Addresses services = addresses.GetAddresses();
|
||||
for(ServiceAddresses::Addresses::iterator it = services.begin();
|
||||
it != services.end();
|
||||
++it, ++servers_it) {
|
||||
expected_address.str("");
|
||||
expected_address << *servers_it << ':' << default_port;
|
||||
EXPECT_EQ(expected_address.str(), *it);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(PBRPCURLTest, URLAddressesWithDifferentProtocols) {
|
||||
EXPECT_THROW({
|
||||
pbrpc_url_->ParseURL("pbrpc://localhost,pbrpcg://remote/" + volume_name,
|
||||
PBRPCURL::GetSchemePBRPC(),
|
||||
DIR_PBRPC_PORT_DEFAULT);
|
||||
}, InvalidURLException);
|
||||
}
|
||||
|
||||
} // namespace xtreemfs
|
||||
206
cpp/test/libxtreemfs/system_user_mapping_unix_test.cpp
Normal file
206
cpp/test/libxtreemfs/system_user_mapping_unix_test.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libxtreemfs/system_user_mapping_unix.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
class UserMappingUnixTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
shutdown_logger();
|
||||
}
|
||||
|
||||
SystemUserMappingUnix user_mapping_;
|
||||
};
|
||||
|
||||
// Test user functions.
|
||||
TEST_F(UserMappingUnixTest, UsernameRootIs0) {
|
||||
// Assuming the entry "root" exists.
|
||||
EXPECT_EQ(0, user_mapping_.UsernameToUID("root"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Username1Is1) {
|
||||
// Assuming there's no user with name "1111", the number itself should be
|
||||
// returned.
|
||||
EXPECT_EQ(1111, user_mapping_.UsernameToUID("1111"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Username2Pow32Minus1IsSame) {
|
||||
// 2^32-2 would overflow in case of int32_t but remains 2^32-1 with uint32_t.
|
||||
// 2^32-1 is the reserved -1.
|
||||
EXPECT_EQ(4294967294, user_mapping_.UsernameToUID("4294967294"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, UsernameMinus1isNobody) {
|
||||
// -1 is used by chown to tell that uid or gid should not change. However,
|
||||
// the reverse mapping is not possible and should map to nobody.
|
||||
// (uid_t)-1 = 4294967295.
|
||||
EXPECT_EQ(65534, user_mapping_.UsernameToUID("-1"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Username2Pow32IsNobody) {
|
||||
// Out of range of uid_t.
|
||||
EXPECT_EQ(65534, user_mapping_.UsernameToUID("4294967296"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, UsernameMinus2Pow31Minus1IsNobody) {
|
||||
// Negative values should be detected as errors (except -1).
|
||||
// Even -2^31-1 which must not overflow to 2^32-1.
|
||||
EXPECT_EQ(65534, user_mapping_.UsernameToUID("-2147483649"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Username1aIsNobody) {
|
||||
// Assuming the user "1a" does not exist.
|
||||
EXPECT_EQ(65534, user_mapping_.UsernameToUID("1a"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, UID0isRoot) {
|
||||
// Assuming the entry "root" exists.
|
||||
EXPECT_EQ("root", user_mapping_.UIDToUsername(0));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, UID1111Is1111) {
|
||||
// Assuming user with id 1111 does not exist.
|
||||
EXPECT_EQ("1111", user_mapping_.UIDToUsername(1111));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, UID2Pow32Minus2IsSame) {
|
||||
// Assuming the group "4294967294" does not exist in the system.
|
||||
// 4294967294 is the max allowed id as chown's -1 corresponds to 4294967295.
|
||||
EXPECT_EQ("4294967294", user_mapping_.UIDToUsername(4294967294));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, UID2Pow32Minus1IsMinus1) {
|
||||
// (uid_t)-1 = 4294967295.
|
||||
EXPECT_EQ("-1", user_mapping_.UIDToUsername((uid_t)-1));
|
||||
}
|
||||
|
||||
/** Tests for the range of UIDs from 0 to 2^16, if they do exist on this system
|
||||
* and check if the mapping back from the user name to the UID does work.
|
||||
*
|
||||
* In case there exists no user name for a UID, the UID itself will be
|
||||
* returned as string. Therefore, this test should work on the complete range
|
||||
* of UIDs (except for the special ID (uid_t)-1 (= 4294967295).
|
||||
*
|
||||
* We only test till 2^10 (1024) and not the complete range (4294967294). */
|
||||
TEST_F(UserMappingUnixTest, UIDsFrom0To2Pow10MapBackCorrectlyToUsernames) {
|
||||
for (uid_t uid = 0; uid <= 1024; uid++) {
|
||||
std::string username_for_uid = user_mapping_.UIDToUsername(uid);
|
||||
EXPECT_EQ(uid, user_mapping_.UsernameToUID(username_for_uid));
|
||||
}
|
||||
}
|
||||
|
||||
// Test Group functions.
|
||||
TEST_F(UserMappingUnixTest, GroupnameRootIs0) {
|
||||
// Assuming the entry "root" exists.
|
||||
EXPECT_EQ(0, user_mapping_.GroupnameToGID("root"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Groupname1Is1) {
|
||||
// Assuming there's no user with name "1111", the number itself should be
|
||||
// returned.
|
||||
EXPECT_EQ(1111, user_mapping_.GroupnameToGID("1111"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Groupname2Pow32Minus1IsSame) {
|
||||
// 2^32-2 would overflow in case of int32_t but remains 2^32-1 with uint32_t.
|
||||
// 2^32-1 is the reserved -1.
|
||||
EXPECT_EQ(4294967294, user_mapping_.GroupnameToGID("4294967294"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, GroupnameMinus1isNobody) {
|
||||
// -1 is used by chown to tell that gid or gid should not change. However,
|
||||
// the reverse mapping is not possible and should map to nobody.
|
||||
// (gid_t)-1 = 4294967295.
|
||||
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("-1"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Groupname2Pow32IsNobody) {
|
||||
// Out of range of gid_t.
|
||||
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("4294967296"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, GroupnameMinus2Pow31Minus1IsNobody) {
|
||||
// Negative values should be detected as errors (except -1).
|
||||
// Even -2^31-1 which must not overflow to 2^32-1.
|
||||
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("-2147483649"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, Groupname1aIsNobody) {
|
||||
// Assuming the user "1a" does not exist.
|
||||
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("1a"));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, GID0isRoot) {
|
||||
// Assuming the entry "root" exists.
|
||||
EXPECT_EQ("root", user_mapping_.GIDToGroupname(0));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, GID1111Is1111) {
|
||||
// Assuming user with id 1111 does not exist.
|
||||
EXPECT_EQ("1111", user_mapping_.GIDToGroupname(1111));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, GID2Pow32Minus2IsSame) {
|
||||
// Assuming the group "4294967294" does not exist in the system.
|
||||
// 4294967294 is the max allowed id as chown's -1 corresponds to 4294967295.
|
||||
EXPECT_EQ("4294967294", user_mapping_.GIDToGroupname(4294967294));
|
||||
}
|
||||
|
||||
TEST_F(UserMappingUnixTest, GID2Pow32Minus1IsMinus1) {
|
||||
// (gid_t)-1 = 4294967295.
|
||||
EXPECT_EQ("-1", user_mapping_.GIDToGroupname((gid_t)-1));
|
||||
}
|
||||
|
||||
/** Tests for the range of GIDs from 0 to 2^16, if they do exist on this system
|
||||
* and check if the mapping back from the group name to the GID does work.
|
||||
*
|
||||
* In case there exists no group name for a GID, the GID itself will be
|
||||
* returned as string. Therefore, this test should work on the complete range
|
||||
* of GIDs (except for the special ID (gid_t)-1 (= 4294967295).
|
||||
*
|
||||
* We only test till 2^10 (1024) and not the complete range (4294967294). */
|
||||
TEST_F(UserMappingUnixTest, GIDsFrom0To2Pow10MapBackCorrectlyToGroupnames) {
|
||||
for (gid_t gid = 0; gid <= 1024; gid++) {
|
||||
std::string groupname_for_gid = user_mapping_.GIDToGroupname(gid);
|
||||
EXPECT_EQ(gid, user_mapping_.GroupnameToGID(groupname_for_gid));
|
||||
}
|
||||
}
|
||||
|
||||
// GetGroupnames() tests.
|
||||
|
||||
/** Test if GetGroupnames returns at least the primary group of the user and
|
||||
* its the first in list. */
|
||||
TEST_F(UserMappingUnixTest, GetGroupnamesReturnsPrimaryGroupAtFirst) {
|
||||
string primary_group_of_current_user =
|
||||
user_mapping_.GIDToGroupname(getegid());
|
||||
list<string> groupnames;
|
||||
user_mapping_.GetGroupnames(0, getegid(), getpid(), &groupnames);
|
||||
ASSERT_GE(groupnames.size(), 1);
|
||||
|
||||
EXPECT_EQ(primary_group_of_current_user,
|
||||
(*groupnames.begin()));
|
||||
}
|
||||
|
||||
#endif // !WIN32
|
||||
137
cpp/test/libxtreemfs/user_mapping_gridmap_globus_test.cpp
Normal file
137
cpp/test/libxtreemfs/user_mapping_gridmap_globus_test.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2012 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "libxtreemfs/user_mapping.h"
|
||||
#include "libxtreemfs/user_mapping_gridmap_globus.h"
|
||||
#include "util/logging.h"
|
||||
#include "pbrpc/RPC.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs;
|
||||
using namespace xtreemfs::pbrpc;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
class UserMappingGridmapGlobusTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
|
||||
gridmap_file_path_ = "gridmap_file_globus";
|
||||
|
||||
// Check if file already exists.
|
||||
struct stat stat_buf;
|
||||
if (!stat(gridmap_file_path_.c_str(), &stat_buf)) {
|
||||
ASSERT_FALSE("The temporary gridmap file does already exist");
|
||||
}
|
||||
|
||||
// Create a temporary gridmap file in the working directory.
|
||||
ofstream out(gridmap_file_path_.c_str());
|
||||
out << "\"/C=DE/O=GridGermany/OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)/OU=CSR/CN=Michael Berlin\" root\n";
|
||||
out.close();
|
||||
ASSERT_FALSE(out.fail());
|
||||
|
||||
user_mapping_.reset(new UserMappingGridmapGlobus(
|
||||
gridmap_file_path_,
|
||||
1)); // Check every second for changes.
|
||||
user_mapping_->Start();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
user_mapping_->Stop();
|
||||
|
||||
// Delete gridmap file.
|
||||
remove(gridmap_file_path_.c_str());
|
||||
|
||||
shutdown_logger();
|
||||
atexit(google::protobuf::ShutdownProtobufLibrary);
|
||||
}
|
||||
std::string gridmap_file_path_;
|
||||
|
||||
boost::scoped_ptr<UserMapping> user_mapping_;
|
||||
};
|
||||
|
||||
TEST_F(UserMappingGridmapGlobusTest, TestBasicDNAndOUResolving) {
|
||||
string result;
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE", // NOLINT
|
||||
result);
|
||||
user_mapping_->GlobalToLocalUsername("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE",
|
||||
&result);
|
||||
EXPECT_EQ("root",
|
||||
result);
|
||||
|
||||
// All OUs will be returned as list of groups of UserCredentials.
|
||||
UserCredentials uc;
|
||||
user_mapping_->GetGroupnames("root", &uc);
|
||||
ASSERT_EQ(2, uc.groups_size());
|
||||
EXPECT_EQ("CSR", uc.groups(0));
|
||||
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
|
||||
uc.groups(1));
|
||||
|
||||
// Groups in general do not work and always return "root" or 0.
|
||||
user_mapping_->LocalToGlobalGroupname("CSR", &result);
|
||||
EXPECT_EQ("root", result);
|
||||
user_mapping_->LocalToGlobalGroupname("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)", &result); // NOLINT
|
||||
EXPECT_EQ("root", result);
|
||||
user_mapping_->GlobalToLocalGroupname("unknowngroup", &result);
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
// List of groups is empty for unknown users.
|
||||
UserCredentials uc2;
|
||||
user_mapping_->GetGroupnames("nobody", &uc2);
|
||||
ASSERT_EQ(0, uc2.groups_size());
|
||||
}
|
||||
|
||||
TEST_F(UserMappingGridmapGlobusTest, GridmapFileReload) {
|
||||
string result;
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE", // NOLINT
|
||||
result);
|
||||
user_mapping_->GlobalToLocalUsername("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE", &result); // NOLINT
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
UserCredentials uc;
|
||||
user_mapping_->GetGroupnames("root", &uc);
|
||||
ASSERT_EQ(2, uc.groups_size());
|
||||
EXPECT_EQ("CSR", uc.groups(0));
|
||||
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
|
||||
uc.groups(1));
|
||||
|
||||
// Rewrite file with another entry.
|
||||
ofstream out(gridmap_file_path_.c_str());
|
||||
out << "\"/C=DE/O=GridGermany/OU=Dummy OU 1/CN=Dummy Username\" root\n";
|
||||
out.close();
|
||||
ASSERT_FALSE(out.fail());
|
||||
// Wait for reload.
|
||||
boost::this_thread::sleep(boost::posix_time::seconds(2));
|
||||
|
||||
// Old entry is no longer visible.
|
||||
string dn = "CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE"; // NOLINT
|
||||
user_mapping_->GlobalToLocalUsername(dn, &result);
|
||||
EXPECT_EQ(dn, result);
|
||||
// New entry can be seen.
|
||||
string new_dn = "CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE";
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ(new_dn, result);
|
||||
user_mapping_->GlobalToLocalUsername(new_dn, &result);
|
||||
EXPECT_EQ("root", result);
|
||||
UserCredentials uc2;
|
||||
user_mapping_->GetGroupnames("root", &uc2);
|
||||
ASSERT_EQ(1, uc2.groups_size());
|
||||
EXPECT_EQ("Dummy OU 1", uc2.groups(0));
|
||||
}
|
||||
224
cpp/test/libxtreemfs/user_mapping_gridmap_unicore_test.cpp
Normal file
224
cpp/test/libxtreemfs/user_mapping_gridmap_unicore_test.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2014 by Michael Berlin, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include "libxtreemfs/user_mapping.h"
|
||||
#include "libxtreemfs/user_mapping_gridmap_unicore.h"
|
||||
#include "util/logging.h"
|
||||
#include "pbrpc/RPC.pb.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs;
|
||||
using namespace xtreemfs::util;
|
||||
using namespace xtreemfs::pbrpc;
|
||||
|
||||
class UserMappingGridmapUnicoreTestGeneral : public ::testing::Test {
|
||||
protected:
|
||||
static const int kWaittimeForReload = 2;
|
||||
|
||||
virtual string GetGridmapFileContent() = 0;
|
||||
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
|
||||
gridmap_file_path_ = "gridmap_file_unicore";
|
||||
|
||||
// Check if file already exists.
|
||||
struct stat stat_buf;
|
||||
if (!stat(gridmap_file_path_.c_str(), &stat_buf)) {
|
||||
ASSERT_FALSE("The temporary gridmap file does already exist");
|
||||
}
|
||||
|
||||
// Create a temporary gridmap file in the working directory.
|
||||
ofstream out(gridmap_file_path_.c_str());
|
||||
out << GetGridmapFileContent();
|
||||
out.close();
|
||||
ASSERT_FALSE(out.fail());
|
||||
|
||||
user_mapping_.reset(new UserMappingGridmapUnicore(
|
||||
gridmap_file_path_,
|
||||
1)); // Check every second for changes.
|
||||
user_mapping_->Start();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
user_mapping_->Stop();
|
||||
|
||||
// Delete gridmap file.
|
||||
remove(gridmap_file_path_.c_str());
|
||||
|
||||
shutdown_logger();
|
||||
atexit(google::protobuf::ShutdownProtobufLibrary);
|
||||
}
|
||||
|
||||
std::string gridmap_file_path_;
|
||||
|
||||
boost::scoped_ptr<UserMapping> user_mapping_;
|
||||
};
|
||||
|
||||
class UserMappingGridmapUnicore6Test
|
||||
: public UserMappingGridmapUnicoreTestGeneral {
|
||||
private:
|
||||
virtual string GetGridmapFileContent() {
|
||||
return "225;zib;root:dgms0006:dgls0050;user;mosgrid:lifescience;CN=Patrick Schaefer,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE\n"; // NOLINT
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(UserMappingGridmapUnicore6Test, TestBasicDNAndOUResolving) {
|
||||
string dn = "CN=Patrick Schaefer,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE"; // NOLINT
|
||||
string result;
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ(dn, result);
|
||||
user_mapping_->GlobalToLocalUsername(dn, &result);
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
// All OUs will be returned as list of groups.
|
||||
UserCredentials uc;
|
||||
user_mapping_->GetGroupnames("root", &uc);
|
||||
ASSERT_EQ(2, uc.groups_size());
|
||||
EXPECT_EQ("CSR", uc.groups(0));
|
||||
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
|
||||
uc.groups(1));
|
||||
|
||||
// Groups in general do not work and always return "root" or 0.
|
||||
user_mapping_->LocalToGlobalGroupname("CSR", &result);
|
||||
EXPECT_EQ("root", result);
|
||||
user_mapping_->LocalToGlobalGroupname("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)", &result); // NOLINT
|
||||
EXPECT_EQ("root", result);
|
||||
user_mapping_->GlobalToLocalGroupname("unknowngroup", &result);
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
// List of groups is empty for unknown users.
|
||||
UserCredentials uc2;
|
||||
user_mapping_->GetGroupnames("nobody", &uc2);
|
||||
ASSERT_EQ(0, uc2.groups_size());
|
||||
}
|
||||
|
||||
TEST_F(UserMappingGridmapUnicore6Test, GridmapFileReload) {
|
||||
string dn = "CN=Patrick Schaefer,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE"; // NOLINT
|
||||
|
||||
string result;
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ(dn, result);
|
||||
user_mapping_->GlobalToLocalUsername(dn, &result);
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
UserCredentials uc;
|
||||
user_mapping_->GetGroupnames("root", &uc);
|
||||
ASSERT_EQ(2, uc.groups_size());
|
||||
EXPECT_EQ("CSR", uc.groups(0));
|
||||
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
|
||||
uc.groups(1));
|
||||
|
||||
// Rewrite file with another entry.
|
||||
ofstream out(gridmap_file_path_.c_str());
|
||||
out << "225;zib;root:dgms0006:dgls0050;user;mosgrid:lifescience;CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE\n"; // NOLINT
|
||||
out.close();
|
||||
ASSERT_FALSE(out.fail());
|
||||
// Wait for reload.
|
||||
boost::this_thread::sleep(boost::posix_time::seconds(kWaittimeForReload));
|
||||
|
||||
// Old entry is no longer visible.
|
||||
user_mapping_->GlobalToLocalUsername(dn, &result);
|
||||
EXPECT_EQ(dn, result);
|
||||
// New entry can be seen.
|
||||
string new_dn = "CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE";
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ(new_dn, result);
|
||||
user_mapping_->GlobalToLocalUsername(new_dn, &result);
|
||||
EXPECT_EQ("root", result);
|
||||
UserCredentials uc2;
|
||||
user_mapping_->GetGroupnames("root", &uc2);
|
||||
ASSERT_EQ(1, uc2.groups_size());
|
||||
EXPECT_EQ("Dummy OU 1", uc2.groups(0));
|
||||
}
|
||||
|
||||
// Test for Unicore Version < 6.
|
||||
class UserMappingGridmapUnicoreTest
|
||||
: public UserMappingGridmapUnicoreTestGeneral {
|
||||
private:
|
||||
virtual string GetGridmapFileContent() {
|
||||
return "root:dgms0006=CN=Patrick Schaefer,OU=CSR,O=GridGermany,C=DE\n";
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(UserMappingGridmapUnicoreTest, TestBasicDNAndOUResolving) {
|
||||
string dn = "CN=Patrick Schaefer,OU=CSR,O=GridGermany,C=DE";
|
||||
|
||||
string result;
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ(dn, result);
|
||||
user_mapping_->GlobalToLocalUsername(dn, &result);
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
// All OUs will be returned as list of groups.
|
||||
UserCredentials uc;
|
||||
user_mapping_->GetGroupnames("root", &uc);
|
||||
ASSERT_EQ(1, uc.groups_size());
|
||||
EXPECT_EQ("CSR", uc.groups(0));
|
||||
|
||||
// Groups in general do not work and always return "root" or 0.
|
||||
user_mapping_->LocalToGlobalGroupname("CSR", &result);
|
||||
EXPECT_EQ("root", result);
|
||||
user_mapping_->LocalToGlobalGroupname("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)", &result); // NOLINT
|
||||
EXPECT_EQ("root", result);
|
||||
user_mapping_->GlobalToLocalGroupname("unknowngroup", &result);
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
// List of groups is empty for unknown users.
|
||||
UserCredentials uc2;
|
||||
user_mapping_->GetGroupnames("nobody", &uc2);
|
||||
ASSERT_EQ(0, uc2.groups_size());
|
||||
}
|
||||
|
||||
TEST_F(UserMappingGridmapUnicoreTest, GridmapFileReload) {
|
||||
string dn = "CN=Patrick Schaefer,OU=CSR,O=GridGermany,C=DE";
|
||||
|
||||
string result;
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ(dn, result);
|
||||
user_mapping_->GlobalToLocalUsername(dn, &result);
|
||||
EXPECT_EQ("root", result);
|
||||
|
||||
UserCredentials uc;
|
||||
user_mapping_->GetGroupnames("root", &uc);
|
||||
ASSERT_EQ(1, uc.groups_size());
|
||||
EXPECT_EQ("CSR", uc.groups(0));
|
||||
|
||||
// Rewrite file with another entry.
|
||||
ofstream out(gridmap_file_path_.c_str());
|
||||
out << "root:dgms0006=CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE\n"; // NOLINT
|
||||
out.close();
|
||||
ASSERT_FALSE(out.fail());
|
||||
// Wait for reload.
|
||||
boost::this_thread::sleep(boost::posix_time::seconds(kWaittimeForReload));
|
||||
|
||||
// Old entry is no longer visible.
|
||||
user_mapping_->GlobalToLocalUsername(dn, &result);
|
||||
EXPECT_EQ(dn, result);
|
||||
// New entry can be seen.
|
||||
string new_dn = "CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE";
|
||||
user_mapping_->LocalToGlobalUsername("root", &result);
|
||||
EXPECT_EQ(new_dn, result);
|
||||
user_mapping_->GlobalToLocalUsername(new_dn, &result);
|
||||
EXPECT_EQ("root", result);
|
||||
UserCredentials uc2;
|
||||
user_mapping_->GetGroupnames("root", &uc2);
|
||||
ASSERT_EQ(1, uc2.groups_size());
|
||||
EXPECT_EQ("Dummy OU 1", uc2.groups(0));
|
||||
}
|
||||
337
cpp/test/libxtreemfs/uuid_iterator_test.cpp
Normal file
337
cpp/test/libxtreemfs/uuid_iterator_test.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
|
||||
* 2012 by Matthias Noack, Zuse Institute Berlin
|
||||
*
|
||||
* Licensed under the BSD License, see LICENSE file for details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "libxtreemfs/stripe_translator.h"
|
||||
#include "libxtreemfs/uuid_container.h"
|
||||
#include "libxtreemfs/uuid_item.h"
|
||||
#include "libxtreemfs/container_uuid_iterator.h"
|
||||
#include "libxtreemfs/simple_uuid_iterator.h"
|
||||
#include "libxtreemfs/xtreemfs_exception.h"
|
||||
#include "util/logging.h"
|
||||
#include "xtreemfs/GlobalTypes.pb.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace xtreemfs::util;
|
||||
|
||||
namespace xtreemfs {
|
||||
|
||||
// factory function, works fine with all default constructible iterator types
|
||||
template<class IteratorType>
|
||||
UUIDIterator* CreateUUIDIterator() {
|
||||
return new IteratorType();
|
||||
}
|
||||
|
||||
// functor to encapsulate different uuid adding mechanisms
|
||||
template<class IteratorType>
|
||||
struct UUIDAdder;
|
||||
|
||||
template<>
|
||||
struct UUIDAdder<SimpleUUIDIterator> {
|
||||
void operator()(UUIDIterator* it, string uuid) {
|
||||
static_cast<SimpleUUIDIterator*>(it)->AddUUID(uuid);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct UUIDAdder<ContainerUUIDIterator> {
|
||||
void operator()(UUIDIterator* it, string uuid) {
|
||||
created_items_.push_back(new UUIDItem(uuid));
|
||||
// safe downcast here
|
||||
static_cast<ContainerUUIDIterator*>(it)->AddUUIDItem(created_items_.back());
|
||||
}
|
||||
|
||||
typedef vector<UUIDItem*> ItemPtrList;
|
||||
|
||||
~UUIDAdder() {
|
||||
for (ItemPtrList::iterator it = created_items_.begin();
|
||||
it != created_items_.end();
|
||||
++it) {
|
||||
delete *it;
|
||||
}
|
||||
created_items_.clear();
|
||||
}
|
||||
private:
|
||||
vector<UUIDItem*> created_items_;
|
||||
};
|
||||
|
||||
|
||||
template<class IteratorType>
|
||||
class UUIDIteratorTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
initialize_logger(LEVEL_WARN);
|
||||
|
||||
uuid_iterator_.reset(CreateUUIDIterator<IteratorType>());
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
shutdown_logger();
|
||||
atexit(google::protobuf::ShutdownProtobufLibrary);
|
||||
}
|
||||
|
||||
boost::scoped_ptr<UUIDIterator> uuid_iterator_;
|
||||
UUIDAdder<IteratorType> adder_;
|
||||
};
|
||||
|
||||
#if GTEST_HAS_TYPED_TEST
|
||||
|
||||
using testing::Types;
|
||||
|
||||
typedef Types<SimpleUUIDIterator, ContainerUUIDIterator> Implementations;
|
||||
|
||||
TYPED_TEST_CASE(UUIDIteratorTest, Implementations);
|
||||
|
||||
TYPED_TEST(UUIDIteratorTest, ListWithOneUUID) {
|
||||
string uuid1 = "uuid1";
|
||||
string current_uuid;
|
||||
|
||||
this->adder_(this->uuid_iterator_.get(), uuid1);
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
|
||||
// If there is only one UUID and it fails, there is no other choice and it
|
||||
// should be always returned.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
this->uuid_iterator_->MarkUUIDAsFailed(current_uuid);
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(UUIDIteratorTest, ClearLeavesEmptyList) {
|
||||
string uuid1 = "uuid1";
|
||||
string uuid2 = "uuid2";
|
||||
this->adder_(this->uuid_iterator_.get(), uuid1);
|
||||
this->adder_(this->uuid_iterator_.get(), uuid2);
|
||||
|
||||
// Clear the list.
|
||||
this->uuid_iterator_->Clear();
|
||||
|
||||
// There should be no element left and GetUUID should fail.
|
||||
string current_uuid;
|
||||
EXPECT_THROW({this->uuid_iterator_->GetUUID(¤t_uuid);},
|
||||
UUIDIteratorListIsEmpyException);
|
||||
}
|
||||
|
||||
TYPED_TEST(UUIDIteratorTest, ResetAfterEndOfList) {
|
||||
string uuid1 = "uuid1";
|
||||
string uuid2 = "uuid2";
|
||||
string current_uuid;
|
||||
|
||||
// Fill iterator with two UUIDs.
|
||||
this->adder_(this->uuid_iterator_.get(), uuid1);
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
|
||||
this->adder_(this->uuid_iterator_.get(), uuid2);
|
||||
// No entry is marked as failed yet, the first UUID is still available.
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
|
||||
// Mark the current entry (uuid1) as failed. uuid2 now current?
|
||||
this->uuid_iterator_->MarkUUIDAsFailed(uuid1);
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid2, current_uuid);
|
||||
|
||||
// Mark uuid1 again as failed - should have no consequences.
|
||||
this->uuid_iterator_->MarkUUIDAsFailed(uuid1);
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid2, current_uuid);
|
||||
|
||||
// Also mark uuid2 as failed now. Now all entries have failed. As we did reach
|
||||
// the end of the list, the status of all entries should be reset and set to
|
||||
// the first entry in the list.
|
||||
this->uuid_iterator_->MarkUUIDAsFailed(uuid2);
|
||||
for (list<UUIDItem*>::iterator it
|
||||
= this->uuid_iterator_->uuids_.begin();
|
||||
it != this->uuid_iterator_->uuids_.end();
|
||||
++it) {
|
||||
EXPECT_FALSE((*it)->IsFailed());
|
||||
}
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
}
|
||||
|
||||
TYPED_TEST(UUIDIteratorTest, SetCurrentUUID) {
|
||||
string uuid1 = "uuid1";
|
||||
string uuid2 = "uuid2";
|
||||
string uuid3 = "uuid3";
|
||||
string current_uuid;
|
||||
|
||||
this->adder_(this->uuid_iterator_.get(), uuid1);
|
||||
this->adder_(this->uuid_iterator_.get(), uuid2);
|
||||
this->adder_(this->uuid_iterator_.get(), uuid3);
|
||||
|
||||
// First UUID is current.
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
|
||||
// Set third as current one.
|
||||
this->uuid_iterator_->SetCurrentUUID(uuid3);
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid3, current_uuid);
|
||||
|
||||
// Fail the third (and last one): current UUID should be the first again.
|
||||
this->uuid_iterator_->MarkUUIDAsFailed(uuid3);
|
||||
this->uuid_iterator_->GetUUID(¤t_uuid);
|
||||
for (list<UUIDItem*>::iterator it
|
||||
= this->uuid_iterator_->uuids_.begin();
|
||||
it != this->uuid_iterator_->uuids_.end();
|
||||
++it) {
|
||||
EXPECT_FALSE((*it)->IsFailed());
|
||||
}
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
}
|
||||
|
||||
TYPED_TEST(UUIDIteratorTest, DebugString) {
|
||||
string uuid1 = "uuid1";
|
||||
string uuid2 = "uuid2";
|
||||
string uuid3 = "uuid3";
|
||||
|
||||
EXPECT_EQ("[ ]", this->uuid_iterator_->DebugString());
|
||||
|
||||
this->adder_(this->uuid_iterator_.get(), uuid1);
|
||||
EXPECT_EQ("[ [ uuid1, 0] ]", this->uuid_iterator_->DebugString());
|
||||
|
||||
this->adder_(this->uuid_iterator_.get(), uuid2);
|
||||
EXPECT_EQ("[ [ uuid1, 0], [ uuid2, 0] ]", this->uuid_iterator_->DebugString());
|
||||
|
||||
this->adder_(this->uuid_iterator_.get(), uuid3);
|
||||
EXPECT_EQ("[ [ uuid1, 0], [ uuid2, 0], [ uuid3, 0] ]", this->uuid_iterator_->DebugString());
|
||||
}
|
||||
|
||||
#endif // GTEST_HAS_TYPED_TEST
|
||||
|
||||
// Tests for SimpleUUIDIterator
|
||||
|
||||
class SimpleUUIDIteratorTest : public UUIDIteratorTest<SimpleUUIDIterator> {
|
||||
public:
|
||||
void SetUp() {
|
||||
UUIDIteratorTest<SimpleUUIDIterator>::SetUp();
|
||||
simple_uuid_iterator_ =
|
||||
static_cast<SimpleUUIDIterator*>(uuid_iterator_.get());
|
||||
}
|
||||
protected:
|
||||
SimpleUUIDIterator* simple_uuid_iterator_;
|
||||
};
|
||||
|
||||
TEST_F(SimpleUUIDIteratorTest, LaterAddsDoNotBreakIterator) {
|
||||
string uuid1 = "uuid1";
|
||||
string uuid2 = "uuid2";
|
||||
string uuid3 = "uuid3";
|
||||
string current_uuid;
|
||||
|
||||
this->adder_(this->uuid_iterator_.get(), uuid1);
|
||||
this->adder_(this->uuid_iterator_.get(), uuid2);
|
||||
|
||||
// Mark first uuid as failed and the second takes over.
|
||||
uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
uuid_iterator_->MarkUUIDAsFailed(uuid1);
|
||||
uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid2, current_uuid);
|
||||
|
||||
// Add a third uuid which should be the next element if the second does fail.
|
||||
this->adder_(this->uuid_iterator_.get(), uuid3);
|
||||
uuid_iterator_->MarkUUIDAsFailed(uuid2);
|
||||
uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid3, current_uuid);
|
||||
|
||||
// After all uuids have failed, the first will be returned again.
|
||||
uuid_iterator_->MarkUUIDAsFailed(uuid3);
|
||||
uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
}
|
||||
|
||||
TEST_F(SimpleUUIDIteratorTest, SetCurrentUUIDAddsUnknownUUID) {
|
||||
string uuid1 = "uuid1";
|
||||
string current_uuid;
|
||||
|
||||
// UUID1 not added so far. Setting it will automatically add it.
|
||||
uuid_iterator_->SetCurrentUUID(uuid1);
|
||||
uuid_iterator_->GetUUID(¤t_uuid);
|
||||
EXPECT_EQ(uuid1, current_uuid);
|
||||
}
|
||||
|
||||
// Tests for ContainerUUIDIterator
|
||||
|
||||
class ContainerUUIDIteratorTest
|
||||
: public UUIDIteratorTest<ContainerUUIDIterator> {};
|
||||
|
||||
TEST_F(ContainerUUIDIteratorTest, CreateContainerAndGetUUID) {
|
||||
// Create container from XLocList
|
||||
pbrpc::XLocSet xlocs;
|
||||
pbrpc::StripingPolicyType type = pbrpc::STRIPING_POLICY_RAID0;
|
||||
const int replicaCount = 3;
|
||||
stringstream sstream;
|
||||
|
||||
for (int i = 0; i < replicaCount; ++i) {
|
||||
pbrpc::Replica* replica = xlocs.add_replicas();
|
||||
const int stripe_width = i + 2;
|
||||
|
||||
for (int j = 0; j < stripe_width; ++j) {
|
||||
sstream.str("");
|
||||
sstream << "uuid" << i << j;
|
||||
replica->add_osd_uuids(sstream.str());
|
||||
replica->mutable_striping_policy()->set_type(type);
|
||||
replica->mutable_striping_policy()->set_stripe_size(128); // kb
|
||||
replica->mutable_striping_policy()->set_width(stripe_width);
|
||||
}
|
||||
}
|
||||
|
||||
// get all striping policies
|
||||
StripeTranslator::PolicyContainer striping_policies;
|
||||
for (int i = 0; i < xlocs.replicas_size(); ++i) {
|
||||
striping_policies.push_back(&(xlocs.replicas(i).striping_policy()));
|
||||
}
|
||||
|
||||
boost::shared_ptr<UUIDContainer> uuid_container =
|
||||
boost::make_shared<UUIDContainer>(xlocs);
|
||||
|
||||
// NOTE: We assume that all replicas use the same striping policy type and
|
||||
// that all replicas use the same stripe size.
|
||||
boost::scoped_ptr<StripeTranslator> translator(new StripeTranslatorRaid0());
|
||||
// Map offset to corresponding OSDs.
|
||||
std::vector<ReadOperation> operations;
|
||||
off_t read_offsets[] = {0, 128 * 1024};
|
||||
|
||||
for (int i = 0; i < sizeof(read_offsets) / sizeof(off_t); ++i) {
|
||||
operations.clear();
|
||||
translator->TranslateReadRequest(NULL,
|
||||
128 * 1024,
|
||||
read_offsets[i],
|
||||
striping_policies,
|
||||
&operations);
|
||||
|
||||
// Create a UUIDIterator for a specific set of offsets
|
||||
boost::scoped_ptr<ContainerUUIDIterator> uuid_iterator(
|
||||
new ContainerUUIDIterator(uuid_container,
|
||||
operations[0].osd_offsets));
|
||||
|
||||
// check results
|
||||
string actual;
|
||||
for (int j = 0; j < replicaCount; ++j) {
|
||||
uuid_iterator->GetUUID(&actual);
|
||||
uuid_iterator->MarkUUIDAsFailed(actual);
|
||||
sstream.str("");
|
||||
sstream << "uuid" << j << operations[0].osd_offsets[j];
|
||||
EXPECT_EQ(sstream.str(), actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace xtreemfs
|
||||
1042
cpp/test/libxtreemfs/volume_implementation_test.cpp
Normal file
1042
cpp/test/libxtreemfs/volume_implementation_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user