blob: b35f1b86140fcc04e21bc43df3df40aeaaa403b1 [file] [log] [blame]
#include <c10/util/irange.h>
#include "StoreTestCommon.hpp"
#ifndef _WIN32
#include <unistd.h>
#endif
#include <iostream>
#include <thread>
#include <gtest/gtest.h>
#include <c10d/FileStore.hpp>
#include <c10d/PrefixStore.hpp>
#ifdef _WIN32
std::string tmppath() {
return c10d::test::autoGenerateTmpFilePath();
}
#else
std::string tmppath() {
const char* tmpdir = getenv("TMPDIR");
if (tmpdir == nullptr) {
tmpdir = "/tmp";
}
// Create template
std::vector<char> tmp(256);
auto len = snprintf(tmp.data(), tmp.size(), "%s/testXXXXXX", tmpdir);
tmp.resize(len);
// Create temporary file
auto fd = mkstemp(&tmp[0]);
if (fd == -1) {
throw std::system_error(errno, std::system_category());
}
close(fd);
return std::string(tmp.data(), tmp.size());
}
#endif
void testGetSet(std::string path, std::string prefix = "") {
// Basic Set/Get on File Store
{
auto fileStore = c10::make_intrusive<c10d::FileStore>(path, 2);
c10d::PrefixStore store(prefix, fileStore);
c10d::test::set(store, "key0", "value0");
c10d::test::set(store, "key1", "value1");
c10d::test::set(store, "key2", "value2");
c10d::test::check(store, "key0", "value0");
c10d::test::check(store, "key1", "value1");
c10d::test::check(store, "key2", "value2");
auto numKeys = fileStore->getNumKeys();
EXPECT_EQ(numKeys, 3);
// Check compareSet, does not check return value
c10d::test::compareSet(store, "key0", "wrongExpectedValue", "newValue");
c10d::test::check(store, "key0", "value0");
c10d::test::compareSet(store, "key0", "value0", "newValue");
c10d::test::check(store, "key0", "newValue");
// Check deleteKey
c10d::test::deleteKey(store, "key1");
numKeys = fileStore->getNumKeys();
EXPECT_EQ(numKeys, 2);
c10d::test::check(store, "key0", "newValue");
c10d::test::check(store, "key2", "value2");
c10d::test::set(store, "-key0", "value-");
c10d::test::check(store, "key0", "newValue");
c10d::test::check(store, "-key0", "value-");
numKeys = fileStore->getNumKeys();
EXPECT_EQ(numKeys, 3);
c10d::test::deleteKey(store, "-key0");
numKeys = fileStore->getNumKeys();
EXPECT_EQ(numKeys, 2);
c10d::test::check(store, "key0", "newValue");
c10d::test::check(store, "key2", "value2");
}
// Perform get on new instance
{
auto fileStore = c10::make_intrusive<c10d::FileStore>(path, 2);
c10d::PrefixStore store(prefix, fileStore);
c10d::test::check(store, "key0", "newValue");
auto numKeys = fileStore->getNumKeys();
// There will be 3 keys since we still use the same underlying file as the
// other store above.
EXPECT_EQ(numKeys, 3);
}
}
void stressTestStore(std::string path, std::string prefix = "") {
// Hammer on FileStore::add
const auto numThreads = 4;
const auto numIterations = 100;
std::vector<std::thread> threads;
c10d::test::Semaphore sem1, sem2;
for (C10_UNUSED const auto i : c10::irange(numThreads)) {
threads.emplace_back(std::thread([&] {
auto fileStore =
c10::make_intrusive<c10d::FileStore>(path, numThreads + 1);
c10d::PrefixStore store(prefix, fileStore);
sem1.post();
sem2.wait();
for (C10_UNUSED const auto j : c10::irange(numIterations)) {
store.add("counter", 1);
}
}));
}
sem1.wait(numThreads);
sem2.post(numThreads);
for (auto& thread : threads) {
thread.join();
}
// Check that the counter has the expected value
{
auto fileStore = c10::make_intrusive<c10d::FileStore>(path, numThreads + 1);
c10d::PrefixStore store(prefix, fileStore);
std::string expected = std::to_string(numThreads * numIterations);
c10d::test::check(store, "counter", expected);
}
}
class FileStoreTest : public ::testing::Test {
protected:
void SetUp() override {
path_ = tmppath();
}
void TearDown() override {
unlink(path_.c_str());
}
std::string path_;
};
TEST_F(FileStoreTest, testGetAndSet) {
testGetSet(path_);
}
TEST_F(FileStoreTest, testGetAndSetWithPrefix) {
testGetSet(path_, "testPrefix");
}
TEST_F(FileStoreTest, testStressStore) {
stressTestStore(path_);
}
TEST_F(FileStoreTest, testStressStoreWithPrefix) {
stressTestStore(path_, "testPrefix");
}