New upstream version 8.1.0
This commit is contained in:
21
fsck/tests/FlatTest.cpp
Normal file
21
fsck/tests/FlatTest.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "FlatTest.h"
|
||||
|
||||
#include <program/Program.h>
|
||||
#include <common/toolkit/StorageTk.h>
|
||||
|
||||
FlatTest::FlatTest()
|
||||
{
|
||||
dirName = "./fsck.test.dir/";
|
||||
this->fileName = this->dirName + "set";
|
||||
}
|
||||
|
||||
void FlatTest::SetUp()
|
||||
{
|
||||
if(::mkdir(this->dirName.c_str(), 0770) < 0 && errno != EEXIST)
|
||||
throw std::runtime_error("could not create dir");
|
||||
}
|
||||
|
||||
void FlatTest::TearDown()
|
||||
{
|
||||
StorageTk::removeDirRecursive(this->dirName);
|
||||
}
|
||||
31
fsck/tests/FlatTest.h
Normal file
31
fsck/tests/FlatTest.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef FLATTEST_H_
|
||||
#define FLATTEST_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class FlatTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
FlatTest();
|
||||
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
struct Data {
|
||||
uint64_t id;
|
||||
char dummy[1024 - sizeof(uint64_t)];
|
||||
|
||||
typedef uint64_t KeyType;
|
||||
uint64_t pkey() const { return id; }
|
||||
};
|
||||
|
||||
protected:
|
||||
std::string dirName;
|
||||
std::string fileName;
|
||||
};
|
||||
|
||||
#endif
|
||||
64
fsck/tests/TestConfig.cpp
Normal file
64
fsck/tests/TestConfig.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "TestConfig.h"
|
||||
|
||||
void TestConfig::SetUp()
|
||||
{
|
||||
emptyConfigFile = DUMMY_EMPTY_CONFIG_FILE;
|
||||
this->dummyConfigFile = DUMMY_NOEXIST_CONFIG_FILE;
|
||||
}
|
||||
|
||||
void TestConfig::TearDown()
|
||||
{
|
||||
// delete generated config file
|
||||
if ( StorageTk::pathExists(this->emptyConfigFile) )
|
||||
{
|
||||
// return value of remove is ignored now
|
||||
remove(this->emptyConfigFile.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestConfig, defaultConfigFile)
|
||||
{
|
||||
log.log(Log_DEBUG, "testDefaultConfigFile started");
|
||||
|
||||
// get the path where the binary resides
|
||||
int BUFSIZE = 255;
|
||||
char exePathBuf[BUFSIZE];
|
||||
// read only BUFSIZE-1, as we need to terminate the string manually later
|
||||
ssize_t len = readlink("/proc/self/exe", exePathBuf, BUFSIZE-1);
|
||||
|
||||
/* In case of an error, failure will indicate the error */
|
||||
if (len < 0)
|
||||
FAIL() << "Internal error";
|
||||
|
||||
/* in case of insufficient buffer size, failure will indicate the error */
|
||||
if (len >= BUFSIZE)
|
||||
FAIL() << "Internal error";
|
||||
|
||||
// readlink does NOT null terminate the string, so we do it here to be safe
|
||||
exePathBuf[len] = '\0';
|
||||
|
||||
// construct the path to the default config file
|
||||
std::string defaultFileName = std::string(dirname(exePathBuf))
|
||||
+ "/" + DEFAULT_CONFIG_FILE_RELATIVE;
|
||||
|
||||
// create config with the default file and see what happens while parsing
|
||||
int argc = 2;
|
||||
char* argv[2];
|
||||
|
||||
std::string appNameStr = APP_NAME;
|
||||
appNameStr += '\0';
|
||||
|
||||
std::string cfgLineStr = "cfgFile=" + defaultFileName;
|
||||
cfgLineStr += '\0';
|
||||
|
||||
argv[0] = &appNameStr[0];
|
||||
argv[1] = &cfgLineStr[0];
|
||||
|
||||
try {
|
||||
Config config(argc, argv);
|
||||
} catch (ConnAuthFileException& e) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.log(Log_DEBUG, "testDefaultConfigFile finished");
|
||||
}
|
||||
29
fsck/tests/TestConfig.h
Normal file
29
fsck/tests/TestConfig.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef TESTCONFIG_H_
|
||||
#define TESTCONFIG_H_
|
||||
|
||||
#include <app/config/Config.h>
|
||||
|
||||
#include <libgen.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <common/app/config/ConnAuthFileException.h>
|
||||
|
||||
#define DUMMY_NOEXIST_CONFIG_FILE "/tmp/nonExistantConfigFile.fsck"
|
||||
#define DUMMY_EMPTY_CONFIG_FILE "/tmp/emptyConfigFile.fsck"
|
||||
#define DEFAULT_CONFIG_FILE_RELATIVE "dist/etc/beegfs-client.conf"
|
||||
#define APP_NAME "beegfs-fsck";
|
||||
|
||||
class TestConfig: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
LogContext log;
|
||||
|
||||
// we have these filenames as member variables because
|
||||
// we might need to delete them in tearDown function
|
||||
std::string dummyConfigFile;
|
||||
std::string emptyConfigFile;
|
||||
};
|
||||
|
||||
#endif /* TESTCONFIG_H_ */
|
||||
254
fsck/tests/TestCursors.cpp
Normal file
254
fsck/tests/TestCursors.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
#include <database/Distinct.h>
|
||||
#include <database/Filter.h>
|
||||
#include <database/Group.h>
|
||||
#include <database/LeftJoinEq.h>
|
||||
#include <database/Select.h>
|
||||
#include <database/Union.h>
|
||||
#include <database/VectorSource.h>
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
template<typename Obj, size_t Size>
|
||||
static VectorSource<Obj> vectorSource(const Obj (&data)[Size])
|
||||
{
|
||||
std::vector<Obj> vector(data, data + Size);
|
||||
|
||||
return VectorSource<Obj>(vector);
|
||||
}
|
||||
|
||||
template<typename Source, typename Data, size_t Size, typename EqFn>
|
||||
static void expect(const Source& source, const Data (&data)[Size], EqFn eqFn)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(Size > 2);
|
||||
|
||||
for(size_t markPos = 0; markPos < Size; markPos++)
|
||||
{
|
||||
boost::shared_ptr<typename Source::MarkerType> mark;
|
||||
Source current = source;
|
||||
|
||||
for(size_t i = 0; i < Size; i++)
|
||||
{
|
||||
ASSERT_TRUE(current.step());
|
||||
|
||||
if(i == markPos)
|
||||
mark = boost::make_shared<typename Source::MarkerType>(current.mark() );
|
||||
|
||||
ASSERT_TRUE(eqFn(*current.get(), data[i]));
|
||||
}
|
||||
|
||||
ASSERT_FALSE(current.step());
|
||||
|
||||
current.restore(*mark);
|
||||
|
||||
for(size_t i = markPos; i < Size; i++)
|
||||
{
|
||||
ASSERT_TRUE(eqFn(*current.get(), data[i]));
|
||||
|
||||
if(i < Size - 1) {
|
||||
ASSERT_TRUE(current.step());
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_FALSE(current.step());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Source, typename Data, size_t Size>
|
||||
static void expect(Source source, const Data (&data)[Size])
|
||||
{
|
||||
expect(source, data, std::equal_to<Data>() );
|
||||
}
|
||||
|
||||
TEST(Cursors, vectorSource)
|
||||
{
|
||||
const int data[] = { 1, 2, 3 };
|
||||
|
||||
VectorSource<int> source = vectorSource(data);
|
||||
|
||||
expect(source, data);
|
||||
}
|
||||
|
||||
TEST(Cursors, union)
|
||||
{
|
||||
const std::pair<int, int> data1[] = {
|
||||
std::make_pair(1, 1),
|
||||
std::make_pair(1, 2),
|
||||
std::make_pair(2, 2)
|
||||
};
|
||||
|
||||
const std::pair<int, int> data2[] = {
|
||||
std::make_pair(1, 3),
|
||||
std::make_pair(2, 3)
|
||||
};
|
||||
|
||||
const std::pair<int, int> expected[] = {
|
||||
std::make_pair(1, 3),
|
||||
std::make_pair(1, 1),
|
||||
std::make_pair(1, 2),
|
||||
std::make_pair(2, 3),
|
||||
std::make_pair(2, 2)
|
||||
};
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static int key(std::pair<int, int> pair)
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
db::unionBy(
|
||||
Fn::key,
|
||||
vectorSource(data1),
|
||||
vectorSource(data2) ),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, select)
|
||||
{
|
||||
const int data[] = { 1, 2, 3, 4 };
|
||||
|
||||
const int expected[] = { -1, -2, -3, -4 };
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static int op(int i)
|
||||
{
|
||||
return -i;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::select(Fn::op),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, leftJoinEq)
|
||||
{
|
||||
const int data1[] = { 1, 2, 2, 3, 4 };
|
||||
const int data2[] = { 1, 2, 3, 3, 5 };
|
||||
|
||||
const std::pair<int, int> expected[] = {
|
||||
std::make_pair(1, 1),
|
||||
std::make_pair(2, 2),
|
||||
std::make_pair(2, 2),
|
||||
std::make_pair(3, 3),
|
||||
std::make_pair(3, 3),
|
||||
std::make_pair(4, 0),
|
||||
};
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static bool eq(std::pair<int, int*> left, std::pair<int, int> right)
|
||||
{
|
||||
return left.first == right.first &&
|
||||
( (right.second == 0 && left.second == NULL) ||
|
||||
(right.second != 0 && left.second != NULL && right.second == *left.second) );
|
||||
}
|
||||
|
||||
static int key(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
db::leftJoinBy(
|
||||
Fn::key,
|
||||
vectorSource(data1),
|
||||
vectorSource(data2) ),
|
||||
expected,
|
||||
Fn::eq);
|
||||
}
|
||||
|
||||
struct TestGroupOps
|
||||
{
|
||||
typedef int KeyType;
|
||||
typedef int ProjType;
|
||||
typedef int GroupType;
|
||||
|
||||
int count;
|
||||
|
||||
TestGroupOps()
|
||||
: count(0)
|
||||
{}
|
||||
|
||||
KeyType key(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
ProjType project(int i)
|
||||
{
|
||||
return -i;
|
||||
}
|
||||
|
||||
void step(int i)
|
||||
{
|
||||
this->count += 1;
|
||||
}
|
||||
|
||||
int finish()
|
||||
{
|
||||
int result = this->count;
|
||||
this->count = 0;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Cursors, group)
|
||||
{
|
||||
const int data[] = { 1, 2, 2, 3 };
|
||||
const std::pair<int, int> expected[] = {
|
||||
std::make_pair(-1, 1),
|
||||
std::make_pair(-2, 2),
|
||||
std::make_pair(-3, 1)
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::groupBy(TestGroupOps() ),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, filter)
|
||||
{
|
||||
const int data[] = { 1, 2, 2, 3 };
|
||||
const int expected[] = { 2, 2, 3 };
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static bool fn(int i)
|
||||
{
|
||||
return i > 1;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::where(Fn::fn),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, distinct)
|
||||
{
|
||||
const int data[] = { 1, 2, 2, 3 };
|
||||
const int expected[] = { 1, 2, 3 };
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static int fn(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::distinctBy(Fn::fn),
|
||||
expected);
|
||||
}
|
||||
1772
fsck/tests/TestDatabase.cpp
Normal file
1772
fsck/tests/TestDatabase.cpp
Normal file
File diff suppressed because it is too large
Load Diff
115
fsck/tests/TestDatabase.h
Normal file
115
fsck/tests/TestDatabase.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef TESTDATABASE_H_
|
||||
#define TESTDATABASE_H_
|
||||
|
||||
#include <common/net/message/NetMessage.h>
|
||||
#include <database/FsckDB.h>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class TestDatabase: public ::testing::Test
|
||||
{
|
||||
public:
|
||||
TestDatabase();
|
||||
virtual ~TestDatabase();
|
||||
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
protected:
|
||||
std::string databasePath;
|
||||
boost::scoped_ptr<FsckDB> db;
|
||||
|
||||
void testInsertAndReadSingleDentry();
|
||||
void testInsertAndReadDentries();
|
||||
|
||||
void testUpdateDentries();
|
||||
void testUpdateDirInodes();
|
||||
void testUpdateFileInodes();
|
||||
void testUpdateChunks();
|
||||
|
||||
void testInsertAndReadSingleFileInode();
|
||||
|
||||
void testInsertAndReadSingleDirInode();
|
||||
void testInsertAndReadDirInodes();
|
||||
|
||||
void testInsertAndReadSingleChunk();
|
||||
void testInsertAndReadChunks();
|
||||
|
||||
void testInsertAndReadSingleContDir();
|
||||
void testInsertAndReadContDirs();
|
||||
|
||||
void testInsertAndReadSingleFsID();
|
||||
void testInsertAndReadFsIDs();
|
||||
|
||||
void testFindDuplicateInodeIDs();
|
||||
void testFindDuplicateChunks();
|
||||
void testFindDuplicateContDirs();
|
||||
void testFindMismirroredDentries();
|
||||
void testFindMismirroredDirectories();
|
||||
|
||||
void testCheckForAndInsertDanglingDirEntries();
|
||||
void testCheckForAndInsertInodesWithWrongOwner();
|
||||
void testCheckForAndInsertDirEntriesWithWrongOwner();
|
||||
void testCheckForAndInsertMissingDentryByIDFile();
|
||||
void testCheckForAndInsertOrphanedDentryByIDFiles();
|
||||
void testCheckForAndInsertOrphanedDirInodes();
|
||||
void testCheckForAndInsertOrphanedFileInodes();
|
||||
void testCheckForAndInsertOrphanedChunks();
|
||||
void testCheckForAndInsertInodesWithoutContDir();
|
||||
void testCheckForAndInsertOrphanedContDirs();
|
||||
void testCheckForAndInsertFileInodesWithWrongAttribs();
|
||||
void testCheckForAndInsertDirInodesWithWrongAttribs();
|
||||
void testCheckForAndInsertFilesWithMissingStripeTargets();
|
||||
void testCheckForAndInsertChunksWithWrongPermissions();
|
||||
void testCheckForAndInsertChunksInWrongPath();
|
||||
|
||||
void testDeleteDentries();
|
||||
void testDeleteChunks();
|
||||
void testDeleteFsIDs();
|
||||
|
||||
void testGetRowCount();
|
||||
|
||||
void testGetFullPath();
|
||||
|
||||
template<typename Table, typename Items>
|
||||
static bool setRepairActions(Table* table, std::list<Items>& items, FsckRepairAction action)
|
||||
{
|
||||
for (typename std::list<Items>::iterator it = items.begin(), end = items.end();
|
||||
it != end;
|
||||
++it)
|
||||
{
|
||||
if (!table->setRepairAction(*it, action) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Item, typename Source>
|
||||
static void drainToList(Source source, std::list<Item>& list)
|
||||
{
|
||||
list.clear();
|
||||
while(source.step() )
|
||||
list.push_back(*source.get() );
|
||||
}
|
||||
|
||||
template<typename Item, typename Source>
|
||||
static std::list<Item> drain(Source source)
|
||||
{
|
||||
std::list<Item> list;
|
||||
drainToList(source, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
static size_t countCursor(Source source)
|
||||
{
|
||||
size_t result = 0;
|
||||
while(source.step() )
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* TESTDATABASE_H_ */
|
||||
35
fsck/tests/TestFsckTk.cpp
Normal file
35
fsck/tests/TestFsckTk.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <common/toolkit/FsckTk.h>
|
||||
#include <common/toolkit/StringTk.h>
|
||||
#include <toolkit/DatabaseTk.h>
|
||||
#include <toolkit/FsckTkEx.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(FsckTk, dirEntryTypeConversion)
|
||||
{
|
||||
FsckDirEntryType entryTypeOut;
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_INVALID);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_INVALID);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_REGULARFILE);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_REGULARFILE);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_SYMLINK);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SYMLINK);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_DIRECTORY);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_DIRECTORY);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_BLOCKDEV);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_CHARDEV);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_FIFO);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_SOCKET);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
}
|
||||
77
fsck/tests/TestSerialization.cpp
Normal file
77
fsck/tests/TestSerialization.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <common/net/message/nodes/HeartbeatRequestMsg.h>
|
||||
#include <common/net/message/nodes/HeartbeatMsg.h>
|
||||
#include <common/toolkit/ZipIterator.h>
|
||||
#include <net/message/NetMessageFactory.h>
|
||||
#include <toolkit/DatabaseTk.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
template<typename Obj>
|
||||
static void testObjectRoundTrip(Obj& data)
|
||||
{
|
||||
Serializer ser;
|
||||
ser % data;
|
||||
|
||||
std::vector<char> buffer(ser.size());
|
||||
|
||||
ser = Serializer(&buffer[0], buffer.size());
|
||||
ser % data;
|
||||
ASSERT_TRUE(ser.good());
|
||||
ASSERT_EQ(ser.size(), buffer.size());
|
||||
|
||||
Deserializer des(&buffer[0], buffer.size());
|
||||
|
||||
Obj read;
|
||||
|
||||
des % read;
|
||||
ASSERT_TRUE(des.good());
|
||||
ASSERT_EQ(des.size(), buffer.size());
|
||||
|
||||
ser = Serializer();
|
||||
ser % read;
|
||||
ASSERT_EQ(ser.size(), buffer.size());
|
||||
|
||||
std::vector<char> rtBuffer(buffer.size());
|
||||
|
||||
ser = Serializer(&rtBuffer[0], rtBuffer.size());
|
||||
ser % read;
|
||||
ASSERT_TRUE(ser.good());
|
||||
ASSERT_EQ(ser.size(), rtBuffer.size());
|
||||
ASSERT_EQ(buffer, rtBuffer);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckDirEntrySerialization)
|
||||
{
|
||||
FsckDirEntry dirEntryIn = DatabaseTk::createDummyFsckDirEntry();
|
||||
testObjectRoundTrip(dirEntryIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckDirInodeSerialization)
|
||||
{
|
||||
FsckDirInode dirInodeIn = DatabaseTk::createDummyFsckDirInode();
|
||||
testObjectRoundTrip(dirInodeIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckFileInodeSerialization)
|
||||
{
|
||||
FsckFileInode fileInodeIn = DatabaseTk::createDummyFsckFileInode();
|
||||
testObjectRoundTrip(fileInodeIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckChunkSerialization)
|
||||
{
|
||||
FsckChunk chunkIn = DatabaseTk::createDummyFsckChunk();
|
||||
testObjectRoundTrip(chunkIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckContDirSerialization)
|
||||
{
|
||||
FsckContDir contDirIn = DatabaseTk::createDummyFsckContDir();
|
||||
testObjectRoundTrip(contDirIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckFsIDSerialization)
|
||||
{
|
||||
FsckFsID fsIDIn = DatabaseTk::createDummyFsckFsID();
|
||||
testObjectRoundTrip(fsIDIn);
|
||||
}
|
||||
177
fsck/tests/TestSet.cpp
Normal file
177
fsck/tests/TestSet.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include <database/Set.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "FlatTest.h"
|
||||
|
||||
class TestSet : public FlatTest {
|
||||
};
|
||||
|
||||
TEST_F(TestSet, open)
|
||||
{
|
||||
// break config
|
||||
int cfgFile = ::creat((fileName + ".t").c_str(), 0660);
|
||||
ASSERT_GE(cfgFile, 0);
|
||||
ASSERT_EQ(::truncate( (this->fileName + ".t").c_str(), 5), 0);
|
||||
do {
|
||||
try {
|
||||
Set<Data> set(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("cannot read set"), std::string::npos);
|
||||
ASSERT_NE(std::string(e.what()).find("with version 0"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::write(cfgFile, "v1\n1\n", 5), 5);
|
||||
do {
|
||||
try {
|
||||
Set<Data> set(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("bad set description"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::close(cfgFile), 0);
|
||||
ASSERT_EQ(::unlink((this->fileName + ".t").c_str()), 0);
|
||||
|
||||
// reopen with saved config
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
}
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
}
|
||||
|
||||
ASSERT_EQ(::truncate((this->fileName + ".t").c_str(), 5000), 0);
|
||||
do {
|
||||
try {
|
||||
Set<Data> set(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("bad set description"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, drop)
|
||||
{
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.newFragment();
|
||||
}
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.drop();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::rmdir(this->dirName.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, clear)
|
||||
{
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.newFragment();
|
||||
}
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.clear();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::unlink( (this->fileName + ".t").c_str()), 0);
|
||||
ASSERT_EQ(::rmdir(this->dirName.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, merge)
|
||||
{
|
||||
Data d = {0, {}};
|
||||
|
||||
{
|
||||
Set<Data> set1(this->fileName + "1");
|
||||
set1.newFragment()->append(d);
|
||||
|
||||
Set<Data> set2(this->fileName + "2");
|
||||
set2.newFragment()->append(d);
|
||||
}
|
||||
|
||||
{
|
||||
Set<Data> set1(this->fileName + "1");
|
||||
Set<Data> set2(this->fileName + "2");
|
||||
set2.mergeInto(set1);
|
||||
}
|
||||
|
||||
{
|
||||
Set<Data> set1(this->fileName + "1");
|
||||
set1.drop();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::unlink((this->fileName + "2.t").c_str()), 0);
|
||||
ASSERT_EQ(::rmdir(this->dirName.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, dataOps)
|
||||
{
|
||||
static const unsigned FRAG_COUNT = 2 * 3 * 4;
|
||||
|
||||
Set<Data> set(this->fileName);
|
||||
|
||||
ASSERT_FALSE(set.cursor().step());
|
||||
|
||||
{
|
||||
for(unsigned i = 0; i < FRAG_COUNT; i++)
|
||||
{
|
||||
Data d = { i, {} };
|
||||
SetFragment<Data>* frag = set.newFragment();
|
||||
|
||||
frag->append(d);
|
||||
frag->append(d);
|
||||
|
||||
ASSERT_EQ(set.size(), 2 * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Set<Data>::Cursor cursor = set.cursor();
|
||||
|
||||
for(unsigned i = 0; i < FRAG_COUNT; i++) {
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, i);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, i);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
set.makeUnique();
|
||||
ASSERT_EQ(set.size(), FRAG_COUNT);
|
||||
|
||||
{
|
||||
Set<Data>::Cursor cursor = set.cursor();
|
||||
|
||||
for(unsigned i = 0; i < FRAG_COUNT; i++) {
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, i);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(set.getByKey(FRAG_COUNT / 2).first);
|
||||
ASSERT_EQ(set.getByKey(FRAG_COUNT / 2).second.id, FRAG_COUNT / 2);
|
||||
|
||||
ASSERT_FALSE(set.getByKey(FRAG_COUNT * 2).first);
|
||||
|
||||
struct ops
|
||||
{
|
||||
static int64_t key(uint64_t key) { return key + 6; }
|
||||
};
|
||||
|
||||
ASSERT_TRUE(set.getByKeyProjection(7, ops::key).first);
|
||||
ASSERT_EQ(set.getByKeyProjection(7, ops::key).second.id, 1u);
|
||||
}
|
||||
203
fsck/tests/TestSetFragment.cpp
Normal file
203
fsck/tests/TestSetFragment.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#include <database/SetFragment.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "FlatTest.h"
|
||||
|
||||
class TestSetFragment : public FlatTest {
|
||||
};
|
||||
|
||||
TEST_F(TestSetFragment, open)
|
||||
{
|
||||
if(::getuid() == 0)
|
||||
{
|
||||
std::cerr << "Test run as root, skipping DAC tests\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
// bad directory permissions (can't open or create)
|
||||
ASSERT_EQ(::chmod(this->dirName.c_str(), 0), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("could not open fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::chmod(this->dirName.c_str(), 0770), 0);
|
||||
}
|
||||
|
||||
// not a regular file
|
||||
ASSERT_EQ(::mkfifo(this->fileName.c_str(), 0660), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("error while opening fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::unlink(this->fileName.c_str()), 0);
|
||||
|
||||
// corrupted config area
|
||||
ASSERT_EQ(::close(::creat(this->fileName.c_str(), 0660)), 0);
|
||||
ASSERT_EQ(::truncate(this->fileName.c_str(), SetFragment<Data>::CONFIG_AREA_SIZE - 1), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("error while opening fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::unlink(this->fileName.c_str()), 0);
|
||||
|
||||
// corrupted data
|
||||
ASSERT_EQ(::close(::creat(this->fileName.c_str(), 0660)), 0);
|
||||
ASSERT_EQ(::truncate(this->fileName.c_str(),
|
||||
SetFragment<Data>::CONFIG_AREA_SIZE + sizeof(Data) - 1), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("error while opening fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::unlink(this->fileName.c_str()), 0);
|
||||
|
||||
// should work
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, drop)
|
||||
{
|
||||
struct stat stat;
|
||||
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
}
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), 0);
|
||||
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
frag.drop();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), -1);
|
||||
ASSERT_EQ(errno, ENOENT);
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, flush)
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
Data d = {0, {}};
|
||||
struct stat stat;
|
||||
|
||||
frag.append(d);
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), 0);
|
||||
ASSERT_EQ(stat.st_size, 0);
|
||||
|
||||
frag.flush();
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), 0);
|
||||
ASSERT_EQ(size_t(stat.st_size), frag.CONFIG_AREA_SIZE + sizeof(d));
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, appendAndAccess)
|
||||
{
|
||||
struct ops
|
||||
{
|
||||
static void fill(SetFragment<Data>& frag, unsigned limit)
|
||||
{
|
||||
Data d = {0, {}};
|
||||
|
||||
for (unsigned i = 0; i < limit; i++) {
|
||||
d.id = i;
|
||||
frag.append(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void check(SetFragment<Data>& frag, unsigned limit)
|
||||
{
|
||||
ASSERT_EQ(frag.size(), limit);
|
||||
|
||||
for (unsigned i = 0; i < limit; i++) {
|
||||
ASSERT_EQ(frag[i].id, i);
|
||||
ASSERT_EQ(
|
||||
size_t(std::count(
|
||||
frag[i].dummy,
|
||||
frag[i].dummy + sizeof(frag[i].dummy),
|
||||
0)),
|
||||
sizeof(frag[i].dummy));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const unsigned LIMIT = 2 * SetFragment<Data>::BUFFER_SIZE / sizeof(Data);
|
||||
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
ops::fill(frag, LIMIT);
|
||||
ops::check(frag, LIMIT);
|
||||
}
|
||||
|
||||
// read everything again, with a new instance
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
ops::check(frag, LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, sortAndGet)
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
// generate a pseudo-random sequence as {0, 1, ...} * 4001 mod 4003 (both prime)
|
||||
for (unsigned i = 0; i < 4003; i++)
|
||||
{
|
||||
Data d = { i * 4001 % 4003, {} };
|
||||
frag.append(d);
|
||||
}
|
||||
|
||||
frag.sort();
|
||||
|
||||
for (unsigned i = 0; i < 4003; i++)
|
||||
ASSERT_EQ(frag[i].id, i);
|
||||
|
||||
ASSERT_TRUE(frag.getByKey(17).first);
|
||||
ASSERT_EQ(frag.getByKey(17).second.id, 17u);
|
||||
|
||||
ASSERT_TRUE(!frag.getByKey(170000).first);
|
||||
|
||||
struct ops
|
||||
{
|
||||
static int64_t key(uint64_t key) { return key + 6; }
|
||||
};
|
||||
|
||||
ASSERT_TRUE(frag.getByKeyProjection(23, ops::key).first);
|
||||
ASSERT_EQ(frag.getByKeyProjection(23, ops::key).second.id, 17u);
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, rename)
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
frag.rename(this->fileName + "1");
|
||||
|
||||
ASSERT_EQ(::close(::open( (this->fileName + "1").c_str(), O_RDWR)), 0);
|
||||
|
||||
frag.rename(this->fileName);
|
||||
}
|
||||
182
fsck/tests/TestTable.cpp
Normal file
182
fsck/tests/TestTable.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "TestTable.h"
|
||||
|
||||
void TestTable::SetUp()
|
||||
{
|
||||
FlatTest::SetUp();
|
||||
this->table.reset(new Table<Data>(this->fileName, 4096) );
|
||||
}
|
||||
|
||||
void TestTable::TearDown()
|
||||
{
|
||||
if(this->table)
|
||||
this->table->drop();
|
||||
|
||||
FlatTest::TearDown();
|
||||
}
|
||||
|
||||
TEST_F(TestTable, dataHandling)
|
||||
{
|
||||
Data d = {0, {}};
|
||||
|
||||
// first, only ordered inserts
|
||||
d.id = 0;
|
||||
this->table->insert(d);
|
||||
d.id = 1;
|
||||
this->table->insert(d);
|
||||
|
||||
// complete the base set
|
||||
this->table->commitChanges();
|
||||
|
||||
// insert "in order"
|
||||
d.id = 2;
|
||||
this->table->insert(d);
|
||||
|
||||
// unordered delete should fail
|
||||
ASSERT_THROW(this->table->remove(1), std::runtime_error);
|
||||
|
||||
// ordered delete should work
|
||||
this->table->remove(3);
|
||||
|
||||
// reinsert of same id should work
|
||||
d.id = 3;
|
||||
this->table->insert(d);
|
||||
|
||||
// unordered insert should now fail
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
|
||||
// reset changes
|
||||
this->table->commitChanges();
|
||||
|
||||
// unordered insert should work
|
||||
d.id = 0;
|
||||
this->table->insert(d);
|
||||
d.id = 4;
|
||||
this->table->insert(d);
|
||||
d.id = 1;
|
||||
this->table->insert(d);
|
||||
|
||||
// delete should fail
|
||||
ASSERT_THROW(this->table->remove(1), std::runtime_error);
|
||||
|
||||
// reset changes
|
||||
this->table->commitChanges();
|
||||
|
||||
// ordered delete should work
|
||||
this->table->remove(1);
|
||||
this->table->remove(2);
|
||||
this->table->remove(3);
|
||||
|
||||
// unordered insert should fail
|
||||
d.id = 2;
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
|
||||
// unordered delete should work
|
||||
this->table->remove(3);
|
||||
|
||||
// insert should fail after unordered delete
|
||||
d.id = 10;
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
|
||||
// uncomitted changes should yield the sequence [0, 0, 1, 1, 2, 3, 4]
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
unsigned expect[7] = { 0, 0, 1, 1, 2, 3, 4 };
|
||||
for(unsigned i = 0; i < 7; i++)
|
||||
{
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, expect[i]);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
// commited should contain the sequence [0, 0, 4]
|
||||
this->table->commitChanges();
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 0u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 0u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 4u);
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(this->table->getByKey(4).first);
|
||||
ASSERT_EQ(this->table->getByKey(4).second.id, 4u);
|
||||
|
||||
ASSERT_FALSE(this->table->getByKey(3).first);
|
||||
|
||||
struct ops
|
||||
{
|
||||
static int64_t key(uint64_t key) { return key + 6; }
|
||||
};
|
||||
|
||||
ASSERT_TRUE(this->table->getByKeyProjection(10, ops::key).first);
|
||||
ASSERT_EQ(this->table->getByKeyProjection(10, ops::key).second.id, 4u);
|
||||
}
|
||||
|
||||
TEST_F(TestTable, bulkInsert)
|
||||
{
|
||||
{
|
||||
boost::shared_ptr<Buffer<Data> > buf1, buf2;
|
||||
|
||||
buf1 = this->table->bulkInsert();
|
||||
buf2 = this->table->bulkInsert();
|
||||
|
||||
Data d = { 0, {} };
|
||||
|
||||
d.id = 1;
|
||||
buf1->append(d);
|
||||
|
||||
d.id = 2;
|
||||
buf2->append(d);
|
||||
}
|
||||
|
||||
// all insert must have gone to base, queryable immediatly
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 1u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 2u);
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
// another bulk insert is fine, no modifications happened yet
|
||||
{
|
||||
boost::shared_ptr<Buffer<Data> > buf;
|
||||
|
||||
buf = this->table->bulkInsert();
|
||||
|
||||
Data d = { 3, {} };
|
||||
buf->append(d);
|
||||
|
||||
// can't do normal insert or remove during bulk insert
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
ASSERT_THROW(this->table->remove(4), std::runtime_error);
|
||||
}
|
||||
|
||||
// all insert must have gone to base, queryable immediatly
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 1u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 2u);
|
||||
ASSERT_TRUE(cursor.step() );
|
||||
ASSERT_EQ(cursor.get()->id, 3u);
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
this->table->remove(1);
|
||||
ASSERT_THROW(this->table->bulkInsert(), std::runtime_error);
|
||||
|
||||
this->table->commitChanges();
|
||||
ASSERT_THROW(this->table->bulkInsert(), std::runtime_error);
|
||||
}
|
||||
20
fsck/tests/TestTable.h
Normal file
20
fsck/tests/TestTable.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef TESTTABLE_H_
|
||||
#define TESTTABLE_H_
|
||||
|
||||
#include <database/Table.h>
|
||||
#include "FlatTest.h"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class TestTable : public FlatTest
|
||||
{
|
||||
public:
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
protected:
|
||||
boost::scoped_ptr<Table<Data> > table;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user