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

View File

@@ -0,0 +1,251 @@
#include <common/toolkit/BitStore.h>
#include <common/toolkit/Random.h>
#include <cmath>
#include <gtest/gtest.h>
#define TESTBITSTORE_MAX_BITSTORE_SIZE 242
#define TESTBITSTORE_RANDOM_VALUES_COUNT 75
struct TestBitStoreBlockCountResult
{
int blockCount; // the needed blockCount
};
/**
* The number of blocks required to store a certain number of bits.
*/
struct TestBitStoreBlockCountResult const __TESTBITSTORE_BLOCK_COUNT_RESULTS[] =
{
{1 + ( ( (32*1)-1) / BitStore::LIMB_SIZE) }, // result for bits 1 to 32
{1 + ( ( (32*2)-1) / BitStore::LIMB_SIZE) }, // result for bits 33 to 64
{1 + ( ( (32*3)-1) / BitStore::LIMB_SIZE) }, // result for bits 65 to 96
{1 + ( ( (32*4)-1) / BitStore::LIMB_SIZE) }, // result for bits 97 to 128
{1 + ( ( (32*5)-1) / BitStore::LIMB_SIZE) }, // result for bits 129 to 160
{1 + ( ( (32*6)-1) / BitStore::LIMB_SIZE) }, // result for bits 161 to 192
{1 + ( ( (32*7)-1) / BitStore::LIMB_SIZE) }, // result for bits 193 to 224
{1 + ( ( (32*8)-1) / BitStore::LIMB_SIZE) }, // result for bits 125 to 256
};
class TestBitStore : public ::testing::Test {
protected:
BitStore::limb_type& lowerBits(BitStore& store) { return store.lowerBits; }
BitStore::limb_type* higherBits(BitStore& store) { return store.higherBits.get(); }
};
TEST_F(TestBitStore, calculateBitBlockCount)
{
unsigned blockCount;
unsigned resultIndex = 0;
for(int size = 1; size < TESTBITSTORE_MAX_BITSTORE_SIZE; size++)
{
if(size < 33)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[0].blockCount;
else
if(size < 65)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[1].blockCount;
else
if(size < 97)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[2].blockCount;
else
if(size < 129)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[3].blockCount;
else
if(size < 161)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[4].blockCount;
else
if(size < 193)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[5].blockCount;
else
if(size < 225)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[6].blockCount;
else
if(size < 257)
resultIndex = __TESTBITSTORE_BLOCK_COUNT_RESULTS[7].blockCount;
blockCount = BitStore::calculateBitBlockCount(size);
EXPECT_EQ(blockCount, resultIndex);
}
}
TEST_F(TestBitStore, setSizeAndReset)
{
const int initValue = 32896;
int size = 1;
bool reverse = false;
bool finished = false;
BitStore store(size);
while(finished)
{
int higherBitsBlockCount = BitStore::calculateBitBlockCount(size) - 1;
//set some data to the bit store
lowerBits(store) = initValue;
for(int blockIndex = 0; blockIndex < higherBitsBlockCount; blockIndex++)
{
higherBits(store)[blockIndex] = initValue;
}
// set the new size and reset all values
store.setSize(size);
store.clearBits();
//check the lower bits
EXPECT_EQ(lowerBits(store), 0u);
//check the higher bits
for(int blockIndex = 0; blockIndex < higherBitsBlockCount; blockIndex++)
EXPECT_EQ(higherBits(store)[blockIndex], 0u);
if(size == TESTBITSTORE_MAX_BITSTORE_SIZE)
reverse = true;
if(reverse)
size--;
else
size++;
//
// finish test or set the new size
if(reverse && size == 0)
finished = true;
}
}
TEST_F(TestBitStore, getter)
{
BitStore store(TESTBITSTORE_MAX_BITSTORE_SIZE);
// check the getter for all bits
for(int index = 0; index < TESTBITSTORE_MAX_BITSTORE_SIZE; index++)
{
// set a bit
int blockIndexOfBit = index / BitStore::LIMB_SIZE;
BitStore::limb_type value = 1UL << (index % BitStore::LIMB_SIZE);
if(blockIndexOfBit == 0)
lowerBits(store) = value;
else
higherBits(store)[blockIndexOfBit - 1] = value;
// check if all bits contain the correct value
for(int testIndex = 0; testIndex < TESTBITSTORE_MAX_BITSTORE_SIZE; testIndex++)
{
if(testIndex != index)
EXPECT_FALSE(store.getBitNonAtomic(testIndex));
else
EXPECT_TRUE(store.getBitNonAtomic(testIndex));
}
store.clearBits();
}
}
TEST_F(TestBitStore, setter)
{
BitStore store(TESTBITSTORE_MAX_BITSTORE_SIZE);
int higherBitsBlockCount = BitStore::calculateBitBlockCount(TESTBITSTORE_MAX_BITSTORE_SIZE) - 1;
// check the setter for all bits
for(int index = 0; index < TESTBITSTORE_MAX_BITSTORE_SIZE; index++)
{
store.setBit(index, true);
// check if all bit blocks contains the correct value
int blockIndexOfBit = index / BitStore::LIMB_SIZE;
BitStore::limb_type neededValue = 1UL << (index % BitStore::LIMB_SIZE);
// check lower bit block
if(blockIndexOfBit == 0)
EXPECT_EQ(lowerBits(store), neededValue);
else
EXPECT_EQ(lowerBits(store), 0u);
// check higher bit blocks
for(int blockIndex = 0; blockIndex < higherBitsBlockCount; blockIndex++)
{
if(blockIndex != (blockIndexOfBit - 1) )
EXPECT_EQ(higherBits(store)[blockIndex], 0u);
else
EXPECT_EQ(higherBits(store)[blockIndex], neededValue);
}
store.clearBits();
}
}
static void checkSerialization(BitStore* store)
{
BitStore secondStore;
unsigned serialLen;
{
Serializer ser;
ser % *store;
serialLen = ser.size();
}
boost::scoped_array<char> buf(new char[serialLen]);
unsigned serializationRes;
{
Serializer ser(buf.get(), serialLen);
ser % *store;
serializationRes = ser.size();
}
ASSERT_EQ(serialLen, serializationRes);
unsigned deserLen;
bool success;
{
Deserializer des(buf.get(), serialLen);
des % secondStore;
deserLen = des.size();
success = des.good();
}
EXPECT_TRUE(success) << "BitStore deserialization failed.";
EXPECT_EQ(deserLen, serialLen);
EXPECT_EQ(*store, secondStore);
}
TEST_F(TestBitStore, serialization)
{
// check serialization for different BitStore size
for(int size = 1; size <= TESTBITSTORE_MAX_BITSTORE_SIZE; size++)
{
BitStore store(size);
// check serialization for all bits, only one bit is set
for(int index = 0; index < size; index++)
{
store.setBit(index, true);
checkSerialization(&store);
store.clearBits();
}
}
// check serialization for BitStore with random values
BitStore store(TESTBITSTORE_MAX_BITSTORE_SIZE);
Random rand;
for(int count = 0; count < TESTBITSTORE_RANDOM_VALUES_COUNT; count++)
{
int randIndex = rand.getNextInRange(0, TESTBITSTORE_MAX_BITSTORE_SIZE - 1);
store.setBit(randIndex, true);
}
checkSerialization(&store);
}

View File

@@ -0,0 +1,29 @@
#include <common/toolkit/EntryIdTk.h>
#include <gtest/gtest.h>
TEST(EntryIdTk, isValidHexToken)
{
using EntryIdTk::isValidHexToken;
EXPECT_TRUE(isValidHexToken("0"));
EXPECT_FALSE(isValidHexToken(""));
EXPECT_TRUE(isValidHexToken(std::string(8, '0')));
EXPECT_FALSE(isValidHexToken(std::string(9, '0')));
EXPECT_TRUE(isValidHexToken("123ADB"));
EXPECT_FALSE(isValidHexToken("123ADBS"));
}
TEST(EntryIdTk, isValidEntryIdFormat)
{
using EntryIdTk::isValidEntryIdFormat;
EXPECT_TRUE(isValidEntryIdFormat("0-59F03330-1"));
EXPECT_TRUE(isValidEntryIdFormat("0-0-0"));
EXPECT_FALSE(isValidEntryIdFormat("0-0-0-0"));
EXPECT_FALSE(isValidEntryIdFormat("0-0"));
EXPECT_FALSE(isValidEntryIdFormat("-0"));
EXPECT_FALSE(isValidEntryIdFormat("0-"));
EXPECT_FALSE(isValidEntryIdFormat("--"));
}

View File

@@ -0,0 +1,99 @@
#include <common/net/sock/NetworkInterfaceCard.cpp>
#include <gtest/gtest.h>
struct TestIPv4NetworkParseResult
{
const char* cidr;
bool valid;
struct in_addr address;
uint8_t prefix;
struct in_addr netmask;
};
struct TestIPv4NetworkParseResult const __TESTIPV4NETWORK_PARSE_RESULTS[] =
{
{"0.0.0.0/0", true, in_addr_ctor(0), 0, in_addr_ctor(0)},
{"10.0.0.0/8", true, in_addr_ctor(0x0a), 8, in_addr_ctor(0x0ff)},
{"10.11.0.0/16", true, in_addr_ctor(0x0b0a), 16, in_addr_ctor(0x0ffff)},
{"10.11.12.0/24", true, in_addr_ctor(0x0c0b0a), 24, in_addr_ctor(0x0ffffff)},
{"10.11.12.1/32", true, in_addr_ctor(0x010c0b0a), 32, in_addr_ctor(0x0ffffffff)},
{"10.11.12.1/", false},
{"10.11.12.1/33", false},
{"10.11.12.1/-1", false},
{"foo.com/16", false},
{"", false},
{"/", false},
{"10.11.12.1/16", true, in_addr_ctor(0x0b0a), 16, in_addr_ctor(0x0ffff)},
{NULL}
};
class TestIPv4Network : public ::testing::Test
{
};
TEST_F(TestIPv4Network, parseCidr)
{
for (int i = 0; __TESTIPV4NETWORK_PARSE_RESULTS[i].cidr != NULL; ++i)
{
auto& r = __TESTIPV4NETWORK_PARSE_RESULTS[i];
IPv4Network n;
//std::cerr << "parse " << r.cidr << std::endl;
bool v = IPv4Network::parse(r.cidr, n);
EXPECT_EQ(r.valid, v) << r.cidr << " should be " << (r.valid? "valid" : "invalid");
if (v)
{
EXPECT_EQ(r.address, n.address) << r.cidr << " address should be " << std::hex << r.address.s_addr;
EXPECT_EQ(r.netmask, n.netmask) << r.cidr << " netmask should be " << std::hex << r.netmask.s_addr;
EXPECT_EQ(r.prefix, n.prefix) << r.cidr << " prefix should be " << (int) r.prefix;
}
}
}
TEST_F(TestIPv4Network, matches24)
{
IPv4Network net;
struct in_addr ina;
EXPECT_EQ(true, IPv4Network::parse("192.168.1.0/24", net));
for (int i = 1; i <= 255; ++i)
{
std::string a = std::string("192.168.1.") + std::to_string(i);
EXPECT_EQ(1, inet_pton(AF_INET, a.c_str(), &ina));
EXPECT_EQ(true, net.matches(ina));
}
for (int i = 1; i <= 255; ++i)
{
std::string a = std::string("192.168.2.") + std::to_string(i);
EXPECT_EQ(1, inet_pton(AF_INET, a.c_str(), &ina));
EXPECT_EQ(false, net.matches(ina));
}
}
TEST_F(TestIPv4Network, matches32)
{
IPv4Network net;
struct in_addr ina;
EXPECT_EQ(true, IPv4Network::parse("192.168.1.128/32", net));
for (int i = 1; i <= 255; ++i)
{
std::string a = std::string("192.168.1.") + std::to_string(i);
EXPECT_EQ(1, inet_pton(AF_INET, a.c_str(), &ina));
EXPECT_EQ(i == 128, net.matches(ina));
}
}
TEST_F(TestIPv4Network, matches0)
{
IPv4Network net;
struct in_addr ina;
EXPECT_EQ(true, IPv4Network::parse("0.0.0.0/0", net));
for (int i = 1; i <= 255; ++i)
{
std::string a = std::string("192.168.1.") + std::to_string(i);
EXPECT_EQ(1, inet_pton(AF_INET, a.c_str(), &ina));
EXPECT_EQ(true, net.matches(ina));
}
}

View File

@@ -0,0 +1,23 @@
#include <common/toolkit/ListTk.h>
#include <gtest/gtest.h>
TEST(ListTk, advance)
{
IntList list;
IntListIter iter;
for (int i=0; i<10;i++)
{
list.push_back(i);
}
iter = list.begin();
ListTk::advance(list, iter, 5);
ASSERT_EQ(*iter, 5);
ListTk::advance(list, iter, 44);
ASSERT_EQ(iter, list.end());
}

View File

@@ -0,0 +1,99 @@
#include <common/toolkit/LockFD.h>
#include <common/toolkit/StorageTk.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <gtest/gtest.h>
class TestLockFD : public ::testing::Test {
protected:
std::string tmpDir;
void SetUp() override
{
tmpDir = "tmpXXXXXX";
tmpDir += '\0';
ASSERT_NE(mkdtemp(&tmpDir[0]), nullptr);
tmpDir.resize(tmpDir.size() - 1);
}
void TearDown() override
{
StorageTk::removeDirRecursive(tmpDir);
}
};
TEST_F(TestLockFD, testInitialLock)
{
const auto path = tmpDir + "/initial";
auto lock = LockFD::lock(path, false);
ASSERT_TRUE(lock);
FDHandle fd(open(path.c_str(), O_RDONLY));
ASSERT_TRUE(fd.valid());
ASSERT_EQ(flock(*fd, LOCK_EX | LOCK_NB), -1);
ASSERT_EQ(errno, EWOULDBLOCK);
}
TEST_F(TestLockFD, testLockTwice)
{
const auto path = tmpDir + "/twice";
auto lock1 = LockFD::lock(path, false);
ASSERT_TRUE(lock1);
auto lock2 = LockFD::lock(path, false);
ASSERT_FALSE(lock2);
ASSERT_EQ(lock2.error().value(), EWOULDBLOCK);
}
TEST_F(TestLockFD, testDoesUnlink)
{
const auto path = tmpDir + "/unlinks";
auto lock = LockFD::lock(path, false);
ASSERT_TRUE(lock);
lock = {};
ASSERT_EQ(access(path.c_str(), R_OK), -1);
}
TEST_F(TestLockFD, testUpdate)
{
const auto path = tmpDir + "/update";
{
auto lock = LockFD::lock(path, false).release_value();
ASSERT_TRUE(lock.update("test"));
ASSERT_TRUE(lock.updateWithPID());
}
{
auto lock = LockFD::lock(path, true).release_value();
{
ASSERT_FALSE(lock.update("test"));
std::ifstream file(path);
char buf[10];
file.read(buf, 5);
ASSERT_TRUE(file.eof());
ASSERT_EQ(file.gcount(), 4);
ASSERT_EQ(memcmp(buf, "test", 4), 0);
}
{
ASSERT_FALSE(lock.updateWithPID());
std::ifstream file(path);
char buf[100];
const auto expected = std::to_string(getpid()) + '\n';
file.read(buf, 100);
ASSERT_TRUE(file.eof());
ASSERT_EQ(size_t(file.gcount()), expected.size());
ASSERT_EQ(memcmp(buf, &expected[0], expected.size()), 0);
}
}
}

17
common/tests/TestNIC.cpp Normal file
View File

@@ -0,0 +1,17 @@
#include <common/net/sock/NetworkInterfaceCard.h>
#include <gtest/gtest.h>
TEST(NIC, nic)
{
NicAddressList list;
StringList allowedInterfaces;
ASSERT_TRUE(NetworkInterfaceCard::findAll(&allowedInterfaces, true, &list));
ASSERT_FALSE(list.empty());
NicAddress nicAddr;
ASSERT_TRUE(NetworkInterfaceCard::findByName(list.begin()->name, &nicAddr));
}

View File

@@ -0,0 +1,88 @@
#include <common/toolkit/NetFilter.h>
#include <gtest/gtest.h>
class TestNetFilter : public ::testing::Test {
protected:
std::string fileName;
void createFile(const char* content)
{
fileName = "/tmp/XXXXXX";
int fd = mkstemp(&fileName[0]);
ASSERT_GE(fd, 0);
ASSERT_EQ(write(fd, content, strlen(content)), ssize_t(strlen(content)));
close(fd);
}
void TearDown() override
{
unlink(fileName.c_str());
}
};
TEST_F(TestNetFilter, noFile)
{
NetFilter filter("");
ASSERT_EQ(filter.getNumFilterEntries(), 0u);
// always allow loopback
EXPECT_TRUE(filter.isAllowed(htonl(INADDR_LOOPBACK)));
EXPECT_TRUE(filter.isContained(htonl(INADDR_LOOPBACK)));
// can't check for all ips, try a random one
EXPECT_TRUE(filter.isAllowed(htonl(0xd5c1a899)));
EXPECT_FALSE(filter.isContained(htonl(0xd5c1a899)));
}
TEST_F(TestNetFilter, netmask0)
{
createFile("10.0.0.0/0");
NetFilter filter(fileName);
ASSERT_EQ(filter.getNumFilterEntries(), 1u);
// always allow loopback
EXPECT_TRUE(filter.isAllowed(htonl(INADDR_LOOPBACK)));
EXPECT_TRUE(filter.isContained(htonl(INADDR_LOOPBACK)));
// 10.0.0.x should definitely be allowed
EXPECT_TRUE(filter.isAllowed(htonl(0x0a010203)));
EXPECT_TRUE(filter.isContained(htonl(0x0a010203)));
// netfilter /0 says everything else is allowed too
EXPECT_TRUE(filter.isAllowed(htonl(0xd5c1a899)));
EXPECT_TRUE(filter.isContained(htonl(0xd5c1a899)));
}
TEST_F(TestNetFilter, netmaskExclusive)
{
createFile("10.0.0.0/24\n10.1.0.0/24");
NetFilter filter(fileName);
ASSERT_EQ(filter.getNumFilterEntries(), 2u);
// always allow loopback
EXPECT_TRUE(filter.isAllowed(htonl(INADDR_LOOPBACK)));
EXPECT_TRUE(filter.isContained(htonl(INADDR_LOOPBACK)));
// 10.0.0.x should definitely be allowed
EXPECT_TRUE(filter.isAllowed(htonl(0x0a000003)));
EXPECT_TRUE(filter.isContained(htonl(0x0a000003)));
// 10.0.1.x should not be allowed
EXPECT_FALSE(filter.isAllowed(htonl(0x0a000103)));
EXPECT_FALSE(filter.isContained(htonl(0x0a000103)));
// same thing for 10.1.0.x
EXPECT_TRUE(filter.isAllowed(htonl(0x0a010003)));
EXPECT_TRUE(filter.isContained(htonl(0x0a010003)));
EXPECT_FALSE(filter.isAllowed(htonl(0x0a010103)));
EXPECT_FALSE(filter.isContained(htonl(0x0a010103)));
// other addresses are forbidden
EXPECT_FALSE(filter.isAllowed(htonl(0xd5c1a899)));
EXPECT_FALSE(filter.isContained(htonl(0xd5c1a899)));
}

23
common/tests/TestPath.cpp Normal file
View File

@@ -0,0 +1,23 @@
#include <common/storage/Path.h>
#include <gtest/gtest.h>
TEST(Path, path)
{
std::vector<std::string> origPathElems = {
"xyz",
"subdir",
"file",
};
std::string pathStr;
for (auto iter = origPathElems.begin(); iter != origPathElems.end(); iter++)
pathStr += "/" + *iter;
Path path(pathStr);
ASSERT_EQ(path.size(), origPathElems.size());
for (size_t i = 0; i < path.size(); i++)
ASSERT_EQ(path[i], origPathElems[i]);
}

View File

@@ -0,0 +1,65 @@
#include <common/toolkit/PreallocatedFile.h>
#include <common/toolkit/StorageTk.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/file.h>
#include <gtest/gtest.h>
class TestPreallocatedFile : public ::testing::Test {
protected:
std::string tmpDir;
void SetUp() override
{
tmpDir = "tmpXXXXXX";
tmpDir += '\0';
ASSERT_NE(mkdtemp(&tmpDir[0]), nullptr);
tmpDir.resize(tmpDir.size() - 1);
}
void TearDown() override
{
StorageTk::removeDirRecursive(tmpDir);
}
};
TEST_F(TestPreallocatedFile, allocation)
{
const auto path = tmpDir + "/file";
PreallocatedFile<char, 1024*1024> file(path, 0600);
struct stat st;
ASSERT_EQ(::stat(path.c_str(), &st), 0);
ASSERT_EQ(st.st_size, 1024*1024+1);
ASSERT_GE(st.st_blocks * 512, 1024*1024+1);
}
TEST_F(TestPreallocatedFile, allocationFailure)
{
// This should throw because filesystems can't provide 2**63 writable bytes in a single file as
// of writing this test and probably won't for a long time after.
ASSERT_THROW(([this] {
PreallocatedFile<char, std::numeric_limits<off_t>::max()> file(tmpDir + "/file", 0600);
})(),
std::system_error);
}
TEST_F(TestPreallocatedFile, readWrite)
{
const auto goodPath = tmpDir + "/good";
PreallocatedFile<uint64_t> good(goodPath, 0600);
PreallocatedFile<uint64_t, 1> bad(tmpDir + "/bad", 0600);
good.write(0x0102030405060708ULL);
ASSERT_THROW(bad.write(0), std::runtime_error);
ASSERT_EQ(*good.read(), 0x0102030405060708ULL);
ASSERT_EQ(bad.read(), boost::none);
ASSERT_EQ(::truncate(goodPath.c_str(), 0), 0);
ASSERT_THROW(good.read(), std::system_error);
}

834
common/tests/TestRWLock.cpp Normal file
View File

@@ -0,0 +1,834 @@
#include <common/app/log/LogContext.h>
#include <common/threading/RWLock.h>
#include <common/toolkit/Random.h>
#include <common/toolkit/StringTk.h>
#include <common/toolkit/Time.h>
#include "TestRWLock.h"
// rwlock tests are disabled by default, probably due to long runtime.
#define RWLOCK_TEST(id) TEST_F(TestRWLock, DISABLED_ ## id)
/*
* sorts all threads by the lock timestamp, make it easier to verify the execution order
*/
void TestRWLock::sortThreadsInLockTimestamp(TestLockThread* threads,
Time* startTime)
{
bool changesDone = false;
do
{
changesDone = false;
for (int id = 0; id < (TestRWLock_THREAD_COUNT -1); id++)
{
int elapsedTimeID = threads[id].getLockTimestamp().
elapsedSinceMS(startTime);
int elapsedTimeNextID = threads[id + 1].getLockTimestamp().
elapsedSinceMS(startTime);
if (elapsedTimeID > elapsedTimeNextID)
{
TestLockThread tmpTestLockThread;
tmpTestLockThread.copy(&threads[id]);
threads[id].copy(&threads[id + 1]);
threads[id + 1 ].copy(&tmpTestLockThread);
changesDone = true;
}
}
}
while (changesDone);
}
/*
* analyze the run time for random thread execution, if the run time was long enough
*/
void TestRWLock::checkRandomRuntime(TestLockThread* threads, int runtimeMS)
{
int minimalRuntimeMS = 0;
int longestSleepMS = 0;
for (int id = 0; id < TestRWLock_THREAD_COUNT; id++)
{
// ignore threads which didn't get a lock
if (!threads[id].getLockSuccess())
{
continue;
}
if (threads[id].getDoReadLock())
{ // reader thread, find the longest sleep time
for (int nextId = id + 1; nextId < TestRWLock_THREAD_COUNT; nextId++)
{
// check all reader threads which was executed at the same time
if(threads[nextId].getDoReadLock())
{ // next thread was a reader, find the longest sleep time
if (threads[nextId].getLockSuccess() &&
(longestSleepMS < threads[nextId].getSleepTimeMS()))
{
longestSleepMS = threads[nextId].getSleepTimeMS();
}
}
else
{ // next thread was a writer, stop searching of longest sleep time
id = nextId - 1;
break;
}
}
minimalRuntimeMS = minimalRuntimeMS + longestSleepMS;
}
else
{ // writer thread, add the the sleep time
minimalRuntimeMS = minimalRuntimeMS + threads[id].getSleepTimeMS();
}
}
// check if the sleep time is bigger then the runtime
if (minimalRuntimeMS > runtimeMS)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* analyze the execution order for random thread execution
*/
void TestRWLock::checkRandomExecutionOrder(TestLockThread* threads)
{
for (int id = 1; id < (TestRWLock_THREAD_COUNT); id++)
{
// ignore threads which didn't get a lock
if(!threads[id].getLockSuccess())
{
continue;
}
// check if the unlock was successful
if(!threads[id].getUnlockSuccess())
{
FAIL() << "Test thread didn't unlock the lock.";
}
// check the execution order
if(threads[id].getDoReadLock())
{
checkRandomExecutionOrderReader(threads, id);
}
else
{
checkRandomExecutionOrderWriter(threads, id);
}
}
}
/*
* analyze the execution order for random thread execution for a reader thread
*/
void TestRWLock::checkRandomExecutionOrderReader(TestLockThread* threads, int threadID)
{
for (int beforeId = threadID - 1; beforeId >= 0; beforeId--)
{
if(threads[beforeId].getLockSuccess())
{
Time unlockTimeBefore = threads[beforeId].getUnlockTimestamp();
Time lockTime = threads[threadID].getLockTimestamp();
// check if the thread before was a writer and check if the thread before has unlocked
// the rwlock before this reader thread locks the rwlock
// if the thread before was a reader it is OK to get the lock without a unlock
if((!threads[beforeId].getDoReadLock()) && (unlockTimeBefore > lockTime))
{
std::cerr << "execution order failed, time diff in micro sec: " << StringTk::uintToStr(
unlockTimeBefore.elapsedSinceMicro(&lockTime)) << std::endl;
FAIL() << "Test thread got the lock, but it wasn't possible to get the lock.";
}
}
}
}
/*
* analyze the execution order for random thread execution for a writer thread
*/
void TestRWLock::checkRandomExecutionOrderWriter(TestLockThread* threads, int threadID)
{
for (int beforeId = threadID - 1; beforeId >= 0; beforeId--)
{
if(threads[beforeId].getLockSuccess())
{
Time unlockTimeBefore = threads[beforeId].getUnlockTimestamp();
Time lockTime = threads[threadID].getLockTimestamp();
// check if the thread before has unlocked the rwlock before this writer thread locks
// the rwlock
if(unlockTimeBefore > lockTime)
{
std::cerr << "execution order failed, time diff in micro sec: " << StringTk::uintToStr(
unlockTimeBefore.elapsedSinceMicro(&lockTime)) << std::endl;
FAIL() << "Test thread got the lock, but it wasn't possible to get the lock.";
}
}
}
}
/*
* tests a reader thread on a read lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(readerOnReader)
{
// creates a read lock
RWLock lock;
lock.readLock();
Time startTime;
// creates a read lock
TestLockThread thread(&lock, true, false);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (!thread.getLockSuccess())
{
FAIL() << "The test thread didn't get the lock, but it was possible to get the lock.";
}
if (!thread.getUnlockSuccess())
{
FAIL() << "The test thread didn't unlock the lock.";
}
if (TestRWLock_SLEEP_TIME_MS > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests a reader thread on a write lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(readerOnWriter)
{
// creates a write lock
RWLock lock;
lock.writeLock();
Time startTime;
// creates a read lock
TestLockThread thread(&lock, true, false);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(2 * TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (!thread.getLockSuccess())
{
FAIL() << "The test thread didn't get the lock, but it was possible to get the lock.";
}
if (!thread.getUnlockSuccess())
{
FAIL() << "The test thread didn't unlock the lock.";
}
if ((TestRWLock_SLEEP_TIME_MS * 2) > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests a writer thread on a read lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(writerOnReader)
{
// creates a read lock
RWLock lock;
lock.readLock();
Time startTime;
// creates a write lock
TestLockThread thread(&lock, false, false);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(2 * TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (!thread.getLockSuccess())
{
FAIL() << "The test thread didn't get the lock, but it was possible to get the lock.";
}
if (!thread.getUnlockSuccess())
{
FAIL() << "The test thread didn't unlock the lock.";
}
if ((TestRWLock_SLEEP_TIME_MS * 2) > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests a writer thread on a write lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(writerOnWriter)
{
// creates a write lock
RWLock lock;
lock.writeLock();
Time startTime;
// creates a write lock
TestLockThread thread(&lock, false, false);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(2 * TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (!thread.getLockSuccess())
{
FAIL() << "The test thread didn't get the lock, but it was possible to get the lock.";
}
if (!thread.getUnlockSuccess())
{
FAIL() << "The test thread didn't unlock the lock.";
}
if ((TestRWLock_SLEEP_TIME_MS * 2) > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests reader threads and writer thread on a read lock, checks massive amount of lock operations
*/
RWLOCK_TEST(randomOnReader)
{
RWLock lock;
Random randomizer;
TestLockThread threadList[TestRWLock_THREAD_COUNT];
int id = 0;
threadList[0].init(&lock, true, false, TestRWLock_SLEEP_TIME_MS, 1);
// create all threads for the test
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
bool tmpDoReadLock;
// random initialization: is reader or writer
tmpDoReadLock = randomizer.getNextInRange(0, 1) == 1;
// random initialization: sleep time
int tmpSleepTimeMS = randomizer.getNextInRange(
TestRWLock_RANDOM_SLEEP_TIME_MIN_MS,
TestRWLock_RANDOM_SLEEP_TIME_MAX_MS);
// random initialization: start delay time
int tmpLockDelayMS = randomizer.getNextInRange(
TestRWLock_RANDOM_LOCK_DELAY_MIN_MS,
TestRWLock_RANDOM_LOCK_DELAY_MAX_MS);
threadList[id].init(&lock, tmpDoReadLock, false, tmpSleepTimeMS, tmpLockDelayMS);
}
Time startTime;
// start all threads
threadList[0].start();
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
threadList[id].start();
}
bool notTimedOut = true;
Time startTimeout;
// collect all threads and check timeout
for (id = 0; id < TestRWLock_THREAD_COUNT; id++)
{
int nextTimeout = TestRWLock_MULTI_TIMEOUT_MS - startTimeout.elapsedMS();
if (nextTimeout < 500)
{
nextTimeout = 500;
}
if (!threadList[id].timedjoin(nextTimeout))
{
notTimedOut = false;
}
}
int runtimeMS = startTime.elapsedMS();
// check the constraints
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
sortThreadsInLockTimestamp(threadList, &startTime);
checkRandomExecutionOrder(threadList);
checkRandomRuntime(threadList, runtimeMS);
}
/*
* tests reader threads and writer thread on a write lock, checks massive amount of lock operations
*/
RWLOCK_TEST(randomOnWriter)
{
RWLock lock;
Random randomizer;
TestLockThread threadList[TestRWLock_THREAD_COUNT];
int id = 0;
threadList[0].init(&lock, false, false, 5000, 1);
// create all threads for the test
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
bool tmpDoReadLock;
// random initialization: is reader or writer
tmpDoReadLock = randomizer.getNextInRange(0, 1) == 1;
// random initialization: sleep time
int tmpSleepTimeMS = randomizer.getNextInRange(
TestRWLock_RANDOM_SLEEP_TIME_MIN_MS,
TestRWLock_RANDOM_SLEEP_TIME_MAX_MS);
// random initialization: start delay time
int tmpLockDelayMS = randomizer.getNextInRange(
TestRWLock_RANDOM_LOCK_DELAY_MIN_MS,
TestRWLock_RANDOM_LOCK_DELAY_MAX_MS);
threadList[id].init(&lock, tmpDoReadLock, false, tmpSleepTimeMS, tmpLockDelayMS);
}
Time startTime;
// start all threads
threadList[0].start();
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
threadList[id].start();
}
bool notTimedOut = true;
Time startTimeout;
// collect all threads and check timeout
for (id = 0; id < TestRWLock_THREAD_COUNT; id++)
{
int nextTimeout = TestRWLock_MULTI_TIMEOUT_MS - startTimeout.elapsedMS();
if (nextTimeout < 500)
{
nextTimeout = 500;
}
if (!threadList[id].timedjoin(nextTimeout))
{
notTimedOut = false;
}
}
int runtimeMS = startTime.elapsedMS();
// check the constraints
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
sortThreadsInLockTimestamp(threadList, &startTime);
checkRandomExecutionOrder(threadList);
checkRandomRuntime(threadList, runtimeMS);
}
/*
* tests a tryReadLock on a read lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(tryReadOnReader)
{
// creates a read lock with try method
RWLock lock;
bool success = lock.tryReadLock();
if (!success)
{
FAIL() << "Couldn't get initial lock.";
}
Time startTime;
// creates a read lock with try method
TestLockThread thread(&lock, true, true);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (!thread.getLockSuccess())
{
FAIL() << "The test thread didn't get the lock, but it was possible to get the lock.";
}
if (!thread.getUnlockSuccess())
{
FAIL() << "The test thread didn't unlock the lock.";
}
if (TestRWLock_SLEEP_TIME_MS > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests a tryReadLock on a write lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(tryReadOnWriter)
{
// creates a write lock with try method
RWLock lock;
bool success = lock.tryWriteLock();
if (!success)
{
FAIL() << "Couldn't get initial lock.";
}
Time startTime;
// creates a read lock with try method
TestLockThread thread(&lock, true, true);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(2 * TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (thread.getLockSuccess())
{
FAIL() << "The test thread got the lock, but it wasn't possible to get the lock.";
}
if ((TestRWLock_SLEEP_TIME_MS) > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests a tryWriteLock on a read lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(tryWriteOnReader)
{
// creates a read lock with try method
RWLock lock;
bool success = lock.tryReadLock();
if (!success)
{
FAIL() << "Couldn't get initial lock.";
}
Time startTime;
// creates a write lock with try method
TestLockThread thread(&lock, false, true);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(2 * TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (thread.getLockSuccess())
{
FAIL() << "The test thread got the lock, but it wasn't possible to get the lock.";
}
if ((TestRWLock_SLEEP_TIME_MS) > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests a tryWriteLock on a write lock, checks the basic functions of a rwlock
*/
RWLOCK_TEST(tryWriteOnWriter)
{
// creates a write lock with try method
RWLock lock;
bool success = lock.tryWriteLock();
if (!success)
{
FAIL() << "Couldn't get initial lock.";
}
Time startTime;
// creates a write lock with try method
TestLockThread thread(&lock, false, true);
thread.start();
// wait a few second before unlock the lock
PThread::sleepMS(TestRWLock_SLEEP_TIME_MS);
lock.unlock();
// wait for timeout
bool notTimedOut = thread.timedjoin(2 * TestRWLock_SINGLE_TIMEOUT_MS);
int runtime = startTime.elapsedMS();
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
if (thread.getLockSuccess())
{
FAIL() << "The test thread got the lock, but it wasn't possible to get the lock.";
}
if ((TestRWLock_SLEEP_TIME_MS) > runtime)
{
FAIL() << "Runtime is to short. A lock didn't work.";
}
}
/*
* tests tryReadLock and tryWriteLock on a read lock, checks massive amount of lock operations
*/
RWLOCK_TEST(randomTryOnReader)
{
RWLock lock;
Random randomizer;
TestLockThread threadList[TestRWLock_THREAD_COUNT];
int id = 0;
threadList[0].init(&lock, true, true, 5000, 1);
// create all threads for the test
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
bool tmpDoReadLock;
// random initialization: is reader or writer
tmpDoReadLock = randomizer.getNextInRange(0, 1) == 1;
// random initialization: sleep time
int tmpSleepTimeMS = randomizer.getNextInRange(
TestRWLock_RANDOM_SLEEP_TIME_MIN_MS,
TestRWLock_RANDOM_SLEEP_TIME_MAX_MS);
// random initialization: start delay time
int tmpLockDelayMS = randomizer.getNextInRange(
TestRWLock_RANDOM_LOCK_DELAY_MIN_MS,
TestRWLock_RANDOM_LOCK_DELAY_MAX_MS);
threadList[id].init(&lock, tmpDoReadLock, true, tmpSleepTimeMS, tmpLockDelayMS);
}
Time startTime;
// start all threads
threadList[0].start();
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
threadList[id].start();
}
bool notTimedOut = true;
Time startTimeout;
// collect all threads and check timeout
for (id = 0; id < TestRWLock_THREAD_COUNT; id++)
{
int nextTimeout = TestRWLock_MULTI_TIMEOUT_MS - startTimeout.elapsedMS();
if (nextTimeout < 500)
{
nextTimeout = 500;
}
if (!threadList[id].timedjoin(nextTimeout))
{
notTimedOut = false;
}
}
int runtimeMS = startTime.elapsedMS();
// check the constraints
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
sortThreadsInLockTimestamp(threadList, &startTime);
checkRandomExecutionOrder(threadList);
checkRandomRuntime(threadList, runtimeMS);
}
/*
* tests tryReadLock and tryWriteLock on a write lock, checks massive amount of lock operations
*/
RWLOCK_TEST(randomTryOnWriter)
{
RWLock lock;
Random randomizer;
TestLockThread threadList[TestRWLock_THREAD_COUNT];
int id = 0;
threadList[0].init(&lock, false, true, 5000, 1);
// create all threads for the test
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
bool tmpDoReadLock;
// random initialization: is reader or writer
tmpDoReadLock = randomizer.getNextInRange(0, 1) == 1;
// random initialization: sleep time
int tmpSleepTimeMS = randomizer.getNextInRange(
TestRWLock_RANDOM_SLEEP_TIME_MIN_MS,
TestRWLock_RANDOM_SLEEP_TIME_MAX_MS);
// random initialization: start delay time
int tmpLockDelayMS = randomizer.getNextInRange(
TestRWLock_RANDOM_LOCK_DELAY_MIN_MS,
TestRWLock_RANDOM_LOCK_DELAY_MAX_MS);
threadList[id].init(&lock, tmpDoReadLock, true, tmpSleepTimeMS, tmpLockDelayMS);
}
Time startTime;
// start all threads
threadList[0].start();
for (id = 1; id < TestRWLock_THREAD_COUNT; id++)
{
threadList[id].start();
}
bool notTimedOut = true;
Time startTimeout;
// collect all threads and check timeout
for (id = 0; id < TestRWLock_THREAD_COUNT; id++)
{
int nextTimeout = TestRWLock_MULTI_TIMEOUT_MS - startTimeout.elapsedMS();
if (nextTimeout < 500)
{
nextTimeout = 500;
}
if (!threadList[id].timedjoin(nextTimeout))
{
notTimedOut = false;
}
}
int runtimeMS = startTime.elapsedMS();
// check the constraints
if (!notTimedOut)
{
FAIL() << "Test ran into a timeout. Maybe a dead-lock";
}
sortThreadsInLockTimestamp(threadList, &startTime);
checkRandomExecutionOrder(threadList);
checkRandomRuntime(threadList, runtimeMS);
}

230
common/tests/TestRWLock.h Normal file
View File

@@ -0,0 +1,230 @@
#pragma once
#include <common/threading/Condition.h>
#include <common/threading/RWLock.h>
#include <common/threading/PThread.h>
#include <gtest/gtest.h>
#include <unistd.h>
// configuration for the tests
#define TestRWLock_THREAD_COUNT 50
#define TestRWLock_LOCK_DELAY_MS 1000
#define TestRWLock_SLEEP_TIME_S 3
#define TestRWLock_SLEEP_TIME_MS (TestRWLock_SLEEP_TIME_S * 1000)
#define TestRWLock_RANDOM_SLEEP_TIME_MIN_MS 1000
#define TestRWLock_RANDOM_SLEEP_TIME_MAX_MS 4000
#define TestRWLock_RANDOM_LOCK_DELAY_MIN_MS 2000
#define TestRWLock_RANDOM_LOCK_DELAY_MAX_MS 8000
#define TestRWLock_SINGLE_TIMEOUT_MS 15000
#define TestRWLock_MULTI_TIMEOUT_MS (TestRWLock_SINGLE_TIMEOUT_MS * TestRWLock_THREAD_COUNT)
class TestRWLock: public ::testing::Test
{
protected:
/**
* Thread for testing the concurrent access to a shared resource which is secured by
* an rwlock
*/
class TestLockThread: public PThread
{
public:
TestLockThread() : PThread("RWLockTester")
{
this->lock = NULL;
this->sleepTimeMS = TestRWLock_SLEEP_TIME_MS;
this->lockDelayMS = TestRWLock_LOCK_DELAY_MS;
this->doReadLock = true;
this->doTry = false;
this->lockSuccess = false;
this->unlockSuccess = false;
this->lockTimestamp.setToNow();
this->unlockTimestamp.setToNow();
this->initTimestamp.setToNow();
}
TestLockThread(RWLock* lock, bool doReadLock, bool doTry) : PThread("RWLockTester")
{
this->lock = lock;
this->sleepTimeMS = TestRWLock_SLEEP_TIME_MS;
this->lockDelayMS = TestRWLock_LOCK_DELAY_MS;
this->doReadLock = doReadLock;
this->doTry = doTry;
this->lockSuccess = false;
this->unlockSuccess = false;
this->lockTimestamp.setToNow();
this->unlockTimestamp.setToNow();
this->initTimestamp.setToNow();
}
TestLockThread(RWLock* lock, bool doReadLock, bool doTry, int sleepTimeMS,
int lockDelayMS) : PThread("RWLockTester")
{
this->lock = lock;
this->sleepTimeMS = sleepTimeMS;
this->lockDelayMS = lockDelayMS;
this->doReadLock = doReadLock;
this->doTry = doTry;
this->lockSuccess = false;
this->unlockSuccess = false;
this->lockTimestamp.setToNow();
this->unlockTimestamp.setToNow();
this->initTimestamp.setToNow();
}
protected:
private:
RWLock* lock; // the rwlock which all threads are use
int sleepTimeMS; // amount of to time to wait before start locking the rwlock
int lockDelayMS; // the delay between the lock and the unlock
bool doReadLock; // true if this thread tries a read lock
bool doTry; // true if a try*Lock will be used
bool lockSuccess; // true if the lock was successful
bool unlockSuccess; // true if the unlock was successful
Time lockTimestamp; // the timestamp of the successful lock
Time unlockTimestamp; // the timestamp of the successful unlock
Time initTimestamp; // the timestamp at the end of the initialization
public:
//public inliner
void init(RWLock* lock, bool doReadLock, bool doTry, int sleepTimeMS, int lockDelayMS)
{
this->lock = lock;
this->sleepTimeMS = sleepTimeMS;
this->lockDelayMS = lockDelayMS;
this->doReadLock = doReadLock;
this->doTry = doTry;
this->lockTimestamp.setToNow();
this->unlockTimestamp.setToNow();
this->initTimestamp.setToNow();
}
void copy(TestLockThread* origin)
{
this->lock = origin->lock;
this->sleepTimeMS = origin->sleepTimeMS;
this->lockDelayMS = origin->lockDelayMS;
this->doReadLock = origin->doReadLock;
this->doTry = origin->doTry;
this->lockSuccess = origin->lockSuccess;
this->unlockSuccess = origin->unlockSuccess;
this->lockTimestamp = Time(origin->lockTimestamp);
this->unlockTimestamp = Time(origin->unlockTimestamp);
this->initTimestamp = Time(origin->initTimestamp);
}
bool getDoReadLock()
{
return this->doReadLock;
}
bool getSleepTimeMS()
{
return this->sleepTimeMS;
}
bool getLockSuccess()
{
return this->lockSuccess;
}
bool getUnlockSuccess()
{
return this->unlockSuccess;
}
Time getLockTimestamp()
{
return this->lockTimestamp;
}
Time getUnlockTimestamp()
{
return this->unlockTimestamp;
}
void run()
{
// start delay for random start of the threads, this is needed for a random
// execution order of the test threads, is necessary for a good test
sleepMS(this->lockDelayMS);
if (this->doReadLock)
{
if(this->doTry)
{
this->lockSuccess = this->lock->tryReadLock();
if (this->lockSuccess)
this->lockTimestamp.setToNow();
}
else
{
this->lock->readLock();
this->lockTimestamp.setToNow();
this->lockSuccess = true;
}
}
else
{
if(this->doTry)
{
this->lockSuccess = this->lock->tryWriteLock();
if (this->lockSuccess)
this->lockTimestamp.setToNow();
}
else
{
this->lock->writeLock();
this->lockTimestamp.setToNow();
this->lockSuccess = true;
}
}
// delay between lock and unlock for random execution order of the locking,
// is necessary for a good test
sleepMS(this->sleepTimeMS);
if (this->lockSuccess)
{
this->unlockTimestamp.setToNow();
this->lock->unlock();
this->unlockSuccess = true;
}
}
};
void sortThreadsInLockTimestamp(TestLockThread* threads, Time* startTime);
void checkRandomRuntime(TestLockThread* threads, int runtimeMS);
void checkRandomExecutionOrder(TestLockThread* threads);
void checkRandomExecutionOrderReader(TestLockThread* threads, int threadID);
void checkRandomExecutionOrderWriter(TestLockThread* threads, int threadID);
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
#include <common/net/sock/Socket.cpp>
#include <gtest/gtest.h>
class TestSocket : public ::testing::Test
{
};
TEST_F(TestSocket, endpointAddrToStrSockaddr)
{
std::string addr = "10.20.30.40";
short port = 1234;
struct sockaddr_in sin;
sin.sin_port = htons(port);
EXPECT_EQ(1, inet_pton(AF_INET, addr.c_str(), &sin.sin_addr));
EXPECT_EQ(addr + ":" + std::to_string(port), Socket::endpointAddrToStr(&sin));
}

View File

@@ -0,0 +1,90 @@
#include <common/toolkit/StorageTk.h>
#include <gtest/gtest.h>
// full set of characters allowed in path components, properly escaped as by /proc/mounts
#define FULL_SET "\001\002\003\004\005\006\007\010\\011\\012\013\014\015\016\017\020\021" \
"\022\023\024\025\026\027\030\031\032\033\034\035\036\037\\040!\"#$%&'()*+,-.0123456789:;<=>?" \
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\134]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204" \
"\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233" \
"\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262" \
"\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311" \
"\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340" \
"\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367" \
"\370\371\372\373\374\375\376\377"
#define FULL_SET_RAW "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021" \
"\022\023\024\025\026\027\030\031\032\033\034\035\036\037\040!\"#$%&'()*+,-.0123456789:;<=>?" \
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\134]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204" \
"\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233" \
"\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262" \
"\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311" \
"\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340" \
"\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367" \
"\370\371\372\373\374\375\376\377"
TEST(StorageTk, findMountedPrefix)
{
static const char* inputFile =
"/dev/root / tmpfs rw 0 0\n"
"/dev/sda1 /test/foo/bar xfs rw 0 0\n"
"/dev/sda1 /test/fo ext4 rw 0 0\n"
"/dev/sda1 /test/" FULL_SET " ext4 rw 0 0\n"
"/dev/sda1 /test/foo ext4 rw 0 0\n";
{
std::stringstream file;
ASSERT_FALSE(StorageTk::findLongestMountedPrefix("", file).first);
}
{
std::stringstream file;
ASSERT_FALSE(StorageTk::findLongestMountedPrefix("relative", file).first);
}
{
std::stringstream file;
file.setstate(std::stringstream::failbit);
ASSERT_FALSE(StorageTk::findLongestMountedPrefix("/", file).first);
}
{
std::stringstream file;
auto res = StorageTk::findLongestMountedPrefix("/test", file);
ASSERT_FALSE(res.first);
}
{
std::stringstream file(inputFile);
auto res = StorageTk::findLongestMountedPrefix("/test/foo1", file);
ASSERT_TRUE(res.first);
ASSERT_EQ(res.second, (Mount{"/dev/root", "/", "tmpfs"}));
}
{
std::stringstream file(inputFile);
auto res = StorageTk::findLongestMountedPrefix("/test/foo/bar/baz", file);
ASSERT_TRUE(res.first);
ASSERT_EQ(res.second, (Mount{"/dev/sda1", "/test/foo/bar", "xfs"}));
}
{
std::stringstream file(inputFile);
auto res = StorageTk::findLongestMountedPrefix("/test/foo", file);
ASSERT_TRUE(res.first);
ASSERT_EQ(res.second, (Mount{"/dev/sda1", "/test/foo", "ext4"}));
}
{
std::stringstream file(inputFile);
auto res = StorageTk::findLongestMountedPrefix("/test/" FULL_SET_RAW, file);
ASSERT_TRUE(res.first);
ASSERT_EQ(res.second, (Mount{"/dev/sda1", "/test/" FULL_SET_RAW, "ext4"}));
}
{
std::stringstream file(inputFile);
auto res = StorageTk::findLongestMountedPrefix("/test/" FULL_SET_RAW "/sub", file);
ASSERT_TRUE(res.first);
ASSERT_EQ(res.second, (Mount{"/dev/sda1", "/test/" FULL_SET_RAW, "ext4"}));
}
}

View File

@@ -0,0 +1,15 @@
#include <common/toolkit/StringTk.h>
#include <list>
#include <map>
#include <string>
#include <vector>
#include <gtest/gtest.h>
TEST(StringTk, implode)
{
EXPECT_EQ(std::string(""), StringTk::implode(",", std::vector<int>()));
EXPECT_EQ(std::string("1"), StringTk::implode(",", std::vector<int>({1})));
EXPECT_EQ(std::string("1,2,3"), StringTk::implode(",", std::vector<int>({1,2,3})));
}

View File

@@ -0,0 +1,40 @@
#include <common/storage/striping/StripePattern.h>
#include <common/storage/striping/Raid0Pattern.h>
#include <vector>
#include <gtest/gtest.h>
class Raid0PatternTest : public testing::TestWithParam<unsigned>
{
};
INSTANTIATE_TEST_CASE_P(Name, Raid0PatternTest,
::testing::Values(
64*1024,
1024*1024*1024
)
);
TEST_P(Raid0PatternTest, chunkSizes)
{
const auto chunkSize = GetParam();
const auto targetPattern = std::vector<uint16_t>({0,1,2,3});
const Raid0Pattern p(chunkSize, targetPattern);
ASSERT_EQ(p.getChunkSize(), chunkSize);
for(size_t i=0; i<10*targetPattern.size(); ++i) {
const auto chunkPos = int64_t(i) * chunkSize;
const auto chunkEnd = chunkPos + chunkSize -1;
ASSERT_EQ(p.getStripeTargetIndex(chunkPos), i % targetPattern.size());
ASSERT_EQ(p.getStripeTargetIndex(chunkEnd), i % targetPattern.size());
ASSERT_EQ(p.getChunkStart(chunkPos), chunkPos);
ASSERT_EQ(p.getChunkStart(chunkEnd), chunkPos);
}
}

View File

@@ -0,0 +1,18 @@
#include <common/nodes/TargetCapacityPools.h>
#include <gtest/gtest.h>
TEST(TargetCapacityPools, interdomainWithEmptyGroups)
{
TargetCapacityPools pools(false, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0});
pools.addOrUpdate(1, NumNodeID(1), CapacityPool_NORMAL);
// moves target from LOW to NORMAL, must remove the NORMAL group from chooser
pools.addOrUpdate(1, NumNodeID(1), CapacityPool_LOW);
std::vector<uint16_t> chosen;
pools.chooseTargetsInterdomain(4, 1, &chosen);
EXPECT_EQ(chosen.size(), 1u);
ASSERT_EQ(chosen[0], 1);
}

View File

@@ -0,0 +1,68 @@
#include <common/components/TimerQueue.h>
#include <common/threading/Barrier.h>
#include <gtest/gtest.h>
class TestTimerQueue : public ::testing::Test {
protected:
std::unique_ptr<TimerQueue> queue;
void SetUp() override
{
queue.reset(new TimerQueue(0, 20));
queue->start();
}
};
namespace {
struct EnqueueCancelFn
{
AtomicSizeT* count;
void operator()()
{
count->increase();
}
};
}
TEST_F(TestTimerQueue, enqueueCancel)
{
AtomicSizeT count(0);
auto handle = queue->enqueue(std::chrono::milliseconds(100), EnqueueCancelFn{&count});
handle.cancel();
sleep(1);
ASSERT_EQ(count.read(), 0u);
}
namespace {
struct EnqueueManyLongFn
{
void operator()(AtomicSizeT& count)
{
sleep(1);
count.increase();
}
};
}
TEST_F(TestTimerQueue, enqueueManyLong)
{
AtomicSizeT count(0);
EnqueueManyLongFn fn;
for (int i = 0; i < 42; i++)
queue->enqueue(
std::chrono::milliseconds(10),
std::bind(&EnqueueManyLongFn::operator(), &fn, std::ref(count)));
Time begin;
while (count.read() != 42)
sleep(1);
Time end;
ASSERT_LT(end.elapsedSinceMS(&begin), 20 * 1000u);
}

33
common/tests/TestUiTk.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include <common/toolkit/UiTk.h>
#include <sstream>
#include <gtest/gtest.h>
bool testQuestion(const std::string& answer, boost::optional<bool> defaultAnswer=boost::none)
{
std::stringstream input(answer);
return uitk::userYNQuestion("Test", defaultAnswer, input);
}
TEST(UiTk, userYNQuestion)
{
// basic input
ASSERT_TRUE( testQuestion("Y"));
ASSERT_FALSE(testQuestion("N"));
ASSERT_TRUE( testQuestion("y"));
ASSERT_FALSE(testQuestion("n"));
ASSERT_TRUE( testQuestion("Yes"));
ASSERT_FALSE(testQuestion("No"));
// default answers
ASSERT_TRUE( testQuestion("", true));
ASSERT_FALSE(testQuestion("", false));
ASSERT_TRUE( testQuestion("Y", false));
ASSERT_TRUE( testQuestion("Y", false));
ASSERT_FALSE(testQuestion("N", true));
ASSERT_FALSE(testQuestion("N", true));
}

151
common/tests/TestUnitTk.cpp Normal file
View File

@@ -0,0 +1,151 @@
#include <common/toolkit/UnitTk.h>
#include <gtest/gtest.h>
TEST(UnitTk, gigabyteToByte)
{
double gbValue = 1.0;
int64_t byteValueExpected = 1073741824LL;
int64_t byteValueCalc = UnitTk::gibibyteToByte(gbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
gbValue = 10.0;
byteValueExpected = 10737418240LL;
byteValueCalc = UnitTk::gibibyteToByte(gbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
gbValue = 10.598;
byteValueExpected = 11379515850LL;
byteValueCalc = UnitTk::gibibyteToByte(gbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
}
TEST(UnitTk, megabyteToByte)
{
double mbValue = 1.0;
int64_t byteValueExpected = 1048576LL;
int64_t byteValueCalc = UnitTk::mebibyteToByte(mbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
mbValue = 10.0;
byteValueExpected = 10485760LL;
byteValueCalc = UnitTk::mebibyteToByte(mbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
mbValue = 10.598;
byteValueExpected = 11112808LL;
byteValueCalc = UnitTk::mebibyteToByte(mbValue);;
ASSERT_EQ(byteValueExpected, byteValueCalc);
}
TEST(UnitTk, kilobyteToByte)
{
double kbValue = 1.0;
int64_t byteValueExpected = 1024LL;
int64_t byteValueCalc = UnitTk::kibibyteToByte(kbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
kbValue = 10.0;
byteValueExpected = 10240LL;
byteValueCalc = UnitTk::kibibyteToByte(kbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
kbValue = 10.598;
byteValueExpected = 10852LL;
byteValueCalc = UnitTk::kibibyteToByte(kbValue);
ASSERT_EQ(byteValueExpected, byteValueCalc);
}
TEST(UnitTk, byteToXbyte)
{
std::string unit;
int64_t value = 2048LL;
double valueExpected = 2.0;
double valueCalc = UnitTk::byteToXbyte(value, &unit);
ASSERT_EQ(valueExpected, valueCalc);
ASSERT_EQ(unit.compare("KiB"), 0);
value = 10240LL;
valueExpected = 10.0;
valueCalc = UnitTk::byteToXbyte(value, &unit);
ASSERT_EQ(valueExpected, valueCalc);
ASSERT_EQ(unit.compare("KiB"), 0);
value = 10843LL;
valueExpected = 10.6;
valueCalc = UnitTk::byteToXbyte(value, &unit, true);
ASSERT_EQ(valueExpected, valueCalc);
ASSERT_EQ(unit.compare("KiB"), 0);
value = 1073741824LL;
valueExpected = 1024.0;
valueCalc = UnitTk::byteToXbyte(value, &unit);
ASSERT_EQ(valueExpected, valueCalc);
ASSERT_EQ(unit.compare("MiB"), 0);
value = 10737418240LL;
valueExpected = 10.0;
valueCalc = UnitTk::byteToXbyte(value, &unit);
ASSERT_EQ(valueExpected, valueCalc);
ASSERT_EQ(unit.compare("GiB"), 0);
value = 11446087843LL;
valueExpected = 10.7;
valueCalc = UnitTk::byteToXbyte(value, &unit, true);
ASSERT_EQ(valueExpected, valueCalc);
ASSERT_EQ(unit.compare("GiB"), 0);
}
TEST(UnitTk, xbyteToByte)
{
std::string unit = "KiB";
double value = 1.0;
int64_t byteValueExpected = 1024LL;
int64_t byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
value = 10.0;
byteValueExpected = 10240LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
value = 10.598;
byteValueExpected = 10852LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
unit = "MiB";
value = 1.0;
byteValueExpected = 1048576LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
value = 10.0;
byteValueExpected = 10485760LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
value = 10.598;
byteValueExpected = 11112808LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
unit = "GiB";
value = 1.0;
byteValueExpected = 1073741824LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
value = 10.0;
byteValueExpected = 10737418240LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
value = 10.598;
byteValueExpected = 11379515850LL;
byteValueCalc = UnitTk::xbyteToByte(value, unit);
ASSERT_EQ(byteValueExpected, byteValueCalc);
}