New upstream version 8.1.0

This commit is contained in:
geos_one
2025-08-10 01:34:16 +02:00
commit c891bb7105
4398 changed files with 838833 additions and 0 deletions

21
fsck/tests/FlatTest.cpp Normal file
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

115
fsck/tests/TestDatabase.h Normal file
View 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
View 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);
}

View 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
View 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);
}

View 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
View 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
View 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