#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