Files
factory-hole-core/tests/test_SharedBuffer.cpp
2026-02-09 00:53:38 +09:00

432 lines
8.6 KiB
C++

#include <doctest/doctest.h>
#include <atomic>
#include "Util/SharedBuffer.h"
#include <string>
#include <vector>
#include <thread>
struct SimpleMetaData
{
int Id{};
float Value{};
};
TEST_SUITE("SharedBuffer")
{
TEST_CASE("default construction")
{
SharedBuffer<int, SimpleMetaData> buf;
CHECK_FALSE(buf);
}
TEST_CASE("sized construction")
{
SimpleMetaData meta{ 42, 3.14f };
SharedBuffer<int, SimpleMetaData> buf(5, meta);
CHECK(buf);
CHECK(buf.GetSize() == 5);
CHECK(buf.GetMetaData()->Id == 42);
CHECK(buf.GetMetaData()->Value == doctest::Approx(3.14f));
for (uint32_t i = 0; i < buf.GetSize(); ++i)
CHECK(buf[i] == 0);
}
TEST_CASE("element access and mutation")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> buf(3, meta);
buf[0] = 10;
buf[1] = 20;
buf[2] = 30;
CHECK(buf[0] == 10);
CHECK(buf[1] == 20);
CHECK(buf[2] == 30);
}
TEST_CASE("Ptr() access")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> buf(3, meta);
buf[0] = 100;
int* ptr = buf.Ptr();
CHECK(ptr[0] == 100);
ptr[1] = 200;
CHECK(buf[1] == 200);
}
TEST_CASE("GetData() returns valid span")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> buf(4, meta);
buf[0] = 1;
buf[1] = 2;
buf[2] = 3;
buf[3] = 4;
auto span = buf.GetData();
CHECK(span.size() == 4);
CHECK(span[0] == 1);
CHECK(span[3] == 4);
}
TEST_CASE("const access")
{
SimpleMetaData meta{ 7, 1.0f };
SharedBuffer<int, SimpleMetaData> buf(2, meta);
buf[0] = 99;
const auto& cbuf = buf;
CHECK(cbuf[0] == 99);
CHECK(cbuf.GetSize() == 2);
CHECK(cbuf.Ptr()[0] == 99);
CHECK(cbuf.GetMetaData()->Id == 7);
CHECK(cbuf.GetData().size() == 2);
}
TEST_CASE("copy constructor shares data")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(3, meta);
a[0] = 42;
SharedBuffer<int, SimpleMetaData> b(a);
CHECK(b);
CHECK(b.GetSize() == 3);
CHECK(b[0] == 42);
// They share the same underlying data
CHECK(a.Ptr() == b.Ptr());
// Mutation through one is visible in the other
a[1] = 77;
CHECK(b[1] == 77);
}
TEST_CASE("copy assignment shares data")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(2, meta);
a[0] = 10;
SharedBuffer<int, SimpleMetaData> b;
b = a;
CHECK(b);
CHECK(b[0] == 10);
CHECK(a.Ptr() == b.Ptr());
}
TEST_CASE("copy assignment from non-empty to non-empty")
{
SimpleMetaData meta1{};
SimpleMetaData meta2{};
SharedBuffer<int, SimpleMetaData> a(2, meta1);
SharedBuffer<int, SimpleMetaData> b(3, meta2);
a[0] = 1;
b[0] = 2;
int* oldBPtr = b.Ptr();
b = a;
CHECK(b.GetSize() == 2);
CHECK(b[0] == 1);
CHECK(b.Ptr() == a.Ptr());
CHECK(b.Ptr() != oldBPtr);
}
TEST_CASE("self copy assignment")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(2, meta);
a[0] = 55;
a = a;
CHECK(a);
CHECK(a[0] == 55);
CHECK(a.GetSize() == 2);
}
TEST_CASE("move constructor transfers ownership")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(3, meta);
a[0] = 42;
int* origPtr = a.Ptr();
SharedBuffer<int, SimpleMetaData> b(std::move(a));
CHECK(b);
CHECK(b[0] == 42);
CHECK(b.Ptr() == origPtr);
CHECK_FALSE(a); // source is empty
}
TEST_CASE("move assignment transfers ownership")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(3, meta);
a[0] = 42;
int* origPtr = a.Ptr();
SharedBuffer<int, SimpleMetaData> b;
b = std::move(a);
CHECK(b);
CHECK(b[0] == 42);
CHECK(b.Ptr() == origPtr);
CHECK_FALSE(a);
}
TEST_CASE("self move assignment")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(2, meta);
a[0] = 55;
a = std::move(a);
CHECK(a);
CHECK(a[0] == 55);
}
TEST_CASE("reference counting - last copy cleans up")
{
SimpleMetaData meta{};
int* ptr;
{
SharedBuffer<int, SimpleMetaData> a(3, meta);
a[0] = 1;
ptr = a.Ptr();
{
SharedBuffer<int, SimpleMetaData> b(a);
CHECK(b.Ptr() == ptr);
// b goes out of scope - should NOT free since a still alive
}
// a should still be valid
CHECK(a);
CHECK(a[0] == 1);
}
// a goes out of scope here - memory freed (no way to check, but no crash)
}
TEST_CASE("multiple copies and sequential destruction")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(2, meta);
a[0] = 100;
SharedBuffer<int, SimpleMetaData> b(a);
SharedBuffer<int, SimpleMetaData> c(b);
SharedBuffer<int, SimpleMetaData> d(c);
CHECK(a.Ptr() == d.Ptr());
// Destroy in various orders
b = SharedBuffer<int, SimpleMetaData>(); // release b's ref
CHECK_FALSE(b);
CHECK(a[0] == 100); // a still valid
d = SharedBuffer<int, SimpleMetaData>(); // release d's ref
CHECK(a[0] == 100); // a still valid
CHECK(c[0] == 100); // c still valid
}
TEST_CASE("reassignment releases old buffer")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(2, meta);
SharedBuffer<int, SimpleMetaData> b(3, meta);
a[0] = 1;
b[0] = 2;
// a had sole ownership of its buffer; assigning b should free old buffer
a = b;
CHECK(a[0] == 2);
CHECK(a.GetSize() == 3);
}
TEST_CASE("works with non-trivial element types")
{
struct Meta { int x{}; };
SharedBuffer<std::string, Meta> buf(3, Meta{ 1 });
buf[0] = "hello";
buf[1] = "world";
buf[2] = "test";
CHECK(buf[0] == "hello");
CHECK(buf[1] == "world");
CHECK(buf[2] == "test");
// Copy and verify strings are shared
SharedBuffer<std::string, Meta> copy(buf);
CHECK(copy[0] == "hello");
buf[0] = "modified";
CHECK(copy[0] == "modified"); // shared data
}
TEST_CASE("copy of non-trivial types cleans up properly")
{
struct Meta { int x{}; };
{
SharedBuffer<std::string, Meta> a(2, Meta{});
a[0] = "aaa";
a[1] = "bbb";
{
SharedBuffer<std::string, Meta> b(a);
CHECK(b[0] == "aaa");
}
// b destroyed, a still valid
CHECK(a[0] == "aaa");
}
// a destroyed, strings cleaned up (no leak/crash)
}
TEST_CASE("zero-size buffer")
{
SimpleMetaData meta{ 1, 2.0f };
SharedBuffer<int, SimpleMetaData> buf(0, meta);
CHECK(buf);
CHECK(buf.GetSize() == 0);
CHECK(buf.GetMetaData()->Id == 1);
CHECK(buf.GetData().empty());
}
TEST_CASE("metadata mutation")
{
SimpleMetaData meta{ 1, 0.0f };
SharedBuffer<int, SimpleMetaData> buf(1, meta);
buf.GetMetaData()->Id = 99;
buf.GetMetaData()->Value = 1.5f;
CHECK(buf.GetMetaData()->Id == 99);
CHECK(buf.GetMetaData()->Value == doctest::Approx(1.5f));
}
TEST_CASE("metadata shared between copies")
{
SimpleMetaData meta{ 10, 0.0f };
SharedBuffer<int, SimpleMetaData> a(1, meta);
SharedBuffer<int, SimpleMetaData> b(a);
a.GetMetaData()->Id = 50;
CHECK(b.GetMetaData()->Id == 50);
}
TEST_CASE("bool conversion")
{
SharedBuffer<int, SimpleMetaData> empty;
CHECK_FALSE(empty);
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> valid(1, meta);
CHECK(valid);
SharedBuffer<int, SimpleMetaData> moved(std::move(valid));
CHECK_FALSE(valid);
CHECK(moved);
}
TEST_CASE("copy from default-constructed buffer")
{
SharedBuffer<int, SimpleMetaData> empty;
SharedBuffer<int, SimpleMetaData> copy(empty);
CHECK_FALSE(copy);
}
TEST_CASE("assign default-constructed buffer")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(2, meta);
SharedBuffer<int, SimpleMetaData> empty;
a = empty;
CHECK_FALSE(a);
}
TEST_CASE("move from default-constructed buffer")
{
SharedBuffer<int, SimpleMetaData> empty;
SharedBuffer<int, SimpleMetaData> moved(std::move(empty));
CHECK_FALSE(moved);
}
TEST_CASE("large buffer")
{
SimpleMetaData meta{};
constexpr int N = 10000;
SharedBuffer<int, SimpleMetaData> buf(N, meta);
for (int i = 0; i < N; ++i)
buf[i] = i * 2;
for (int i = 0; i < N; ++i)
CHECK(buf[i] == i * 2);
}
TEST_CASE("chain of assignments")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> a(1, meta);
SharedBuffer<int, SimpleMetaData> b(1, meta);
SharedBuffer<int, SimpleMetaData> c(1, meta);
a[0] = 1;
b[0] = 2;
c[0] = 3;
a = b;
b = c;
CHECK(a[0] == 2);
CHECK(b[0] == 3);
CHECK(c[0] == 3);
CHECK(b.Ptr() == c.Ptr());
}
TEST_CASE("concurrent read access from copies")
{
SimpleMetaData meta{};
SharedBuffer<int, SimpleMetaData> buf(100, meta);
for (uint32_t i = 0; i < 100; ++i)
buf[i] = static_cast<int>(i);
auto copy1 = buf;
auto copy2 = buf;
std::thread t1([&copy1]() {
int sum = 0;
for (uint32_t i = 0; i < copy1.GetSize(); ++i)
sum += copy1[i];
CHECK(sum == 4950);
});
std::thread t2([&copy2]() {
int sum = 0;
for (uint32_t i = 0; i < copy2.GetSize(); ++i)
sum += copy2[i];
CHECK(sum == 4950);
});
t1.join();
t2.join();
}
}