Skip to content

Commit

Permalink
Add randomized spatial index unit testing
Browse files Browse the repository at this point in the history
  • Loading branch information
appgurueu committed Sep 4, 2024
1 parent 9f74439 commit 09c62b5
Showing 1 changed file with 147 additions and 1 deletion.
148 changes: 147 additions & 1 deletion src/unittest/test_serveractiveobjectmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,

#include "activeobjectmgr.h"
#include "catch.h"
#include "irrTypes.h"
#include "irr_aabb3d.h"
#include "mock_serveractiveobject.h"
#include <algorithm>
#include <iterator>
#include <random>
#include <utility>

#include "server/activeobjectmgr.h"
#include "server/serveractiveobject.h"

class TestServerActiveObjectMgr {
server::ActiveObjectMgr saomgr;
std::vector<u16> ids;

public:

Expand All @@ -36,15 +41,30 @@ class TestServerActiveObjectMgr {
}

bool registerObject(std::unique_ptr<ServerActiveObject> obj) {
return saomgr.registerObject(std::move(obj));
auto *ptr = obj.get();
if (!saomgr.registerObject(std::move(obj)))
return false;
ids.push_back(ptr->getId());
return true;
}

void removeObject(u16 id) {
const auto it = std::find(ids.begin(), ids.end(), id);
REQUIRE(it != ids.end());
ids.erase(it);
saomgr.removeObject(id);
}

void updatePos(u16 id, const v3f &pos) {
auto *obj = saomgr.getActiveObject(id);
REQUIRE(obj != nullptr);
obj->setPos(pos);
saomgr.updatePos(pos, id); // HACK work around m_env == nullptr
}

void clear() {
saomgr.clear();
ids.clear();
}

ServerActiveObject *getActiveObject(u16 id) {
Expand All @@ -60,6 +80,82 @@ class TestServerActiveObjectMgr {
void getAddedActiveObjectsAroundPos(T&& arg) {
saomgr.getAddedActiveObjectsAroundPos(std::forward(arg));
}

// Testing

bool empty() {
return ids.empty();
}

template<class T>
u16 randomId(T &random) {
REQUIRE(!ids.empty());
std::uniform_int_distribution<u16> index(0, ids.size() - 1);
return ids[index(random)];
}

void getObjectsInsideRadiusNaive(const v3f &pos, float radius,
std::vector<ServerActiveObject *> &result)
{
for (const auto &[id, obj] : saomgr.m_active_objects.iter()) {
if (obj->getBasePosition().getDistanceFromSQ(pos) <= radius * radius) {
result.push_back(obj.get());
}
}
}

void getObjectsInAreaNaive(const aabb3f &box,
std::vector<ServerActiveObject *> &result)
{
for (const auto &[id, obj] : saomgr.m_active_objects.iter()) {
if (box.isPointInside(obj->getBasePosition())) {
result.push_back(obj.get());
}
}
}

constexpr static auto compare_by_id = [](auto *sao1, auto *sao2) -> bool {
return sao1->getId() < sao2->getId();
};

static void sortById(std::vector<ServerActiveObject *> &saos) {
std::sort(saos.begin(), saos.end(), compare_by_id);
}

void compareObjects(std::vector<ServerActiveObject *> &actual,
std::vector<ServerActiveObject *> &expected)
{
std::vector<ServerActiveObject *> unexpected, missing;
sortById(actual);
sortById(expected);

std::set_difference(actual.begin(), actual.end(),
expected.begin(), expected.end(),
std::back_inserter(unexpected), compare_by_id);

assert(unexpected.empty());

std::set_difference(expected.begin(), expected.end(),
actual.begin(), actual.end(),
std::back_inserter(missing), compare_by_id);
assert(missing.empty());
}

void compareObjectsInsideRadius(const v3f &pos, float radius)
{
std::vector<ServerActiveObject *> actual, expected;
saomgr.getObjectsInsideRadius(pos, radius, actual, nullptr);
getObjectsInsideRadiusNaive(pos, radius, expected);
compareObjects(actual, expected);
}

void compareObjectsInArea(const aabb3f &box)
{
std::vector<ServerActiveObject *> actual, expected;
saomgr.getObjectsInArea(box, actual, nullptr);
getObjectsInAreaNaive(box, expected);
compareObjects(actual, expected);
}
};


Expand Down Expand Up @@ -190,4 +286,54 @@ TEST_CASE("server active object manager")

saomgr.clear();
}

SECTION("spatial index") {
TestServerActiveObjectMgr saomgr;
std::mt19937 gen(0xABCDEF);
std::uniform_int_distribution<s32> coordinate(-1000, 1000);
const auto random_pos = [&]() {
return v3f(coordinate(gen), coordinate(gen), coordinate(gen));
};

std::uniform_int_distribution<u32> percent(0, 99);
const auto modify = [&](u32 p_insert, u32 p_delete, u32 p_update) {
const auto p = percent(gen);
if (p < p_insert) {
saomgr.registerObject(std::make_unique<MockServerActiveObject>(nullptr, random_pos()));
} else if (p < p_insert + p_delete) {
if (!saomgr.empty())
saomgr.removeObject(saomgr.randomId(gen));
} else if (p < p_insert + p_delete + p_update) {
if (!saomgr.empty())
saomgr.updatePos(saomgr.randomId(gen), random_pos());
}
};

const auto test_queries = [&]() {
std::uniform_real_distribution<f32> radius(0, 100);
saomgr.compareObjectsInsideRadius(random_pos(), radius(gen));

aabb3f box(random_pos(), random_pos());
box.repair();
saomgr.compareObjectsInArea(box);
};

// Grow: Insertion twice as likely as deletion
for (u32 i = 0; i < 3000; ++i) {
modify(50, 25, 25);
test_queries();
}

// Stagnate: Insertion and deletion equally likely
for (u32 i = 0; i < 3000; ++i) {
modify(25, 25, 50);
test_queries();
}

// Shrink: Deletion twice as likely as insertion
while (!saomgr.empty()) {
modify(25, 50, 25);
test_queries();
}
}
}

0 comments on commit 09c62b5

Please sign in to comment.