#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include
#include
#include
#include
#include
// --------------------------------------------------------
// Testcase: PassiveVector
// --------------------------------------------------------
TEST_CASE("PassiveVector" * doctest::timeout(300)) {
SUBCASE("constructor") {
tf::PassiveVector vec1;
REQUIRE(vec1.size() == 0);
REQUIRE(vec1.empty() == true);
tf::PassiveVector vec2;
REQUIRE(vec2.size() == 0);
REQUIRE(vec2.empty() == true);
REQUIRE(vec2.capacity() == 8);
}
SUBCASE("constructor_n") {
for(int N=0; N<=65536; ++N) {
tf::PassiveVector vec(N);
REQUIRE(vec.size() == N);
REQUIRE(vec.empty() == (N == 0));
REQUIRE(vec.max_size() >= vec.size());
REQUIRE(vec.capacity() >= vec.size());
}
}
SUBCASE("copy_constructor") {
for(int N=0; N<=65536; N = (N ? N << 1 : 1)) {
tf::PassiveVector vec1(N);
for(auto& item : vec1) {
item = N;
}
tf::PassiveVector vec2(vec1);
REQUIRE(vec1.size() == N);
REQUIRE(vec2.size() == N);
for(size_t i=0; i vec1(N);
for(auto& item : vec1) {
item = N;
}
tf::PassiveVector vec2(std::move(vec1));
REQUIRE(vec1.size() == 0);
REQUIRE(vec1.empty() == true);
REQUIRE(vec2.size() == N);
for(size_t i=0; i vec;
size_t pcap {0};
size_t ncap {0};
for(int n=0; n= pcap);
pcap = ncap;
}
for(int n=0; n vec;
for(int N=0; N<=65536; ++N) {
vec.push_back(N);
++size;
REQUIRE(vec.size() == size);
if(N % 4 == 0) {
vec.pop_back();
--size;
REQUIRE(vec.size() == size);
}
ncap = vec.capacity();
REQUIRE(ncap >= pcap);
pcap = ncap;
}
REQUIRE(vec.size() == size);
for(size_t i=0; i vec;
for(int n=0; n vec(N);
REQUIRE_THROWS(vec.at(N));
REQUIRE_THROWS(vec.at(N+1));
for(int n=0; n vec(N);
auto cap = vec.capacity();
REQUIRE(vec.size() == N);
vec.clear();
REQUIRE(vec.size() == 0);
REQUIRE(vec.capacity() == cap);
}
}
SUBCASE("comparison") {
for(int N=0; N<=65536; N = (N ? N << 1 : 1)) {
tf::PassiveVector vec1;
for(int i=0; i vec2(vec1);
REQUIRE(vec1 == vec2);
}
}
}
// --------------------------------------------------------
// Testcase: Pool
// --------------------------------------------------------
TEST_CASE("ObjectPool" * doctest::timeout(300)) {
struct TestObject {
TestObject(int v) : value {v} {
}
void animate(int v) {
REQUIRE(value == 0);
value = v;
}
void recycle() {
value = 0;
}
int value;
};
thread_local tf::ObjectPool TestObjectPool;
auto fork = [&] (unsigned N) {
const int M = 2048 * N;
std::atomic counter = M;
std::atomic recycle = M;
std::mutex mutex;
std::vector<:unique_ptr>> objects;
std::vector<:thread> threads;
// allocate
for(unsigned t=1; t<=N; ++t) {
threads.emplace_back([&] () {
while(1) {
if(int c = --counter; c < 0) {
break;
}
else {
auto ptr = TestObjectPool.acquire(c);
std::scoped_lock lock(mutex);
objects.push_back(std::move(ptr));
}
}
});
}
for(auto& thread : threads) {
thread.join();
}
threads.clear();
REQUIRE(objects.size() == M);
auto sum = std::accumulate(objects.begin(), objects.end(), 0,
[] (int s, const auto& v) { return s + v->value; }
);
REQUIRE(sum == (M-1)*M / 2);
// recycle
for(unsigned t=1; t<=N; ++t) {
threads.emplace_back([&] () {
while(1) {
if(int r = --recycle; r < 0) {
break;
}
else {
std::scoped_lock lock(mutex);
REQUIRE(!objects.empty());
TestObjectPool.release(std::move(objects.back()));
objects.pop_back();
}
}
});
}
for(auto& thread : threads) {
thread.join();
}
threads.clear();
REQUIRE(objects.size() == 0);
};
SUBCASE("OneThread") { fork(1); }
SUBCASE("TwoThread") { fork(2); }
SUBCASE("ThreeThreads") { fork(3); }
SUBCASE("FourThreads") { fork(4); }
SUBCASE("FiveThreads") { fork(5); }
SUBCASE("SixThreads") { fork(6); }
SUBCASE("SevenThreads") { fork(7); }
SUBCASE("EightThreads") { fork(8); }
}
// --------------------------------------------------------
// Testcase: SingularAllocator
// --------------------------------------------------------
TEST_CASE("SingularAllocator" * doctest::timeout(300)) {
tf::SingularAllocator<:string> allocator;
std::set<:string> set;
for(int i=0; i<1024; ++i) {
for(int j=0; j