Files
DarkRP2D/include/entityx/Entity_test.cc
2017-10-22 14:26:55 +02:00

681 lines
18 KiB
C++

/*
* Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
* All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
*
* Author: Alec Thomas <alec@swapoff.org>
*/
#define CATCH_CONFIG_MAIN
#include <algorithm>
#include <iterator>
#include <string>
#include <utility>
#include <vector>
#include <set>
#include <map>
#include "entityx/3rdparty/catch.hpp"
#include "entityx/entityx.h"
// using namespace std;
using namespace entityx;
using std::ostream;
using std::vector;
using std::set;
using std::map;
using std::pair;
using std::string;
template <typename T>
int size(const T &t) {
int n = 0;
for (auto i : t) {
++n;
(void)i; // Unused on purpose, suppress warning
}
return n;
}
struct Position {
Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
bool operator==(const Position &other) const {
return x == other.x && y == other.y;
}
float x, y;
};
ostream &operator<<(ostream &out, const Position &position) {
out << "Position(" << position.x << ", " << position.y << ")";
return out;
}
struct Direction {
Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
bool operator==(const Direction &other) const {
return x == other.x && y == other.y;
}
float x, y;
};
ostream &operator<<(ostream &out, const Direction &direction) {
out << "Direction(" << direction.x << ", " << direction.y << ")";
return out;
}
struct Tag : Component<Tag> {
explicit Tag(string tag) : tag(tag) {}
bool operator==(const Tag &other) const { return tag == other.tag; }
string tag;
};
ostream &operator<<(ostream &out, const Tag &tag) {
out << "Tag(" << tag.tag << ")";
return out;
}
struct EntityManagerFixture {
EntityManagerFixture() : em(ev) {}
EventManager ev;
EntityManager em;
};
TEST_CASE_METHOD(EntityManagerFixture, "TestCreateEntity") {
REQUIRE(em.size() == 0UL);
Entity e2;
REQUIRE(!(e2.valid()));
Entity e = em.create();
REQUIRE(e.valid());
REQUIRE(em.size() == 1UL);
e2 = e;
REQUIRE(e2.valid());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAsBoolean") {
REQUIRE(em.size() == 0UL);
Entity e = em.create();
REQUIRE(e.valid());
REQUIRE(em.size() == 1UL);
REQUIRE(!(!e));
e.destroy();
REQUIRE(em.size() == 0UL);
REQUIRE(!e);
Entity e2; // Not initialized
REQUIRE(!e2);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityReuse") {
Entity e1 = em.create();
Entity e2 = e1;
auto id = e1.id();
REQUIRE(e1.valid());
REQUIRE(e2.valid());
e1.destroy();
REQUIRE(!e1.valid());
REQUIRE(!e2.valid());
Entity e3 = em.create();
// It is assumed that the allocation will reuse the same entity id, though
// the version will change.
auto new_id = e3.id();
REQUIRE(new_id != id);
REQUIRE((new_id.id() & 0xffffffffUL) == (id.id() & 0xffffffffUL));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentConstruction") {
auto e = em.create();
auto p = e.assign<Position>(1, 2);
auto cp = e.component<Position>();
REQUIRE(p == cp);
REQUIRE(1.0 == Approx(cp->x));
REQUIRE(2.0 == Approx(cp->y));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestDestroyEntity") {
Entity e = em.create();
Entity f = em.create();
e.assign<Position>();
f.assign<Position>();
e.assign<Direction>();
f.assign<Direction>();
REQUIRE(e.valid());
REQUIRE(f.valid());
REQUIRE(static_cast<bool>(e.component<Position>()));
REQUIRE(static_cast<bool>(e.component<Direction>()));
REQUIRE(static_cast<bool>(f.component<Position>()));
REQUIRE(static_cast<bool>(f.component<Direction>()));
e.destroy();
REQUIRE(!(e.valid()));
REQUIRE(f.valid());
REQUIRE(static_cast<bool>(f.component<Position>()));
REQUIRE(static_cast<bool>(f.component<Direction>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponent") {
Entity e = em.create();
Entity f = em.create();
Entity g = em.create();
e.assign<Position>();
e.assign<Direction>();
f.assign<Position>();
g.assign<Position>();
REQUIRE(3 == size(em.entities_with_components<Position>()));
REQUIRE(1 == size(em.entities_with_components<Direction>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithIntersectionOfComponents") {
vector<Entity> entities;
for (int i = 0; i < 150; ++i) {
Entity e = em.create();
entities.push_back(e);
if (i % 2 == 0) e.assign<Position>();
if (i % 3 == 0) e.assign<Direction>();
}
REQUIRE(50 == size(em.entities_with_components<Direction>()));
REQUIRE(75 == size(em.entities_with_components<Position>()));
REQUIRE(25 == size(em.entities_with_components<Direction, Position>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponentAndUnpacking") {
vector<Entity::Id> entities;
Entity e = em.create();
Entity f = em.create();
Entity g = em.create();
std::vector<std::pair<ComponentHandle<Position>, ComponentHandle<Direction>>> position_directions;
position_directions.push_back(std::make_pair(
e.assign<Position>(1.0f, 2.0f), e.assign<Direction>(3.0f, 4.0f)));
position_directions.push_back(std::make_pair(
f.assign<Position>(7.0f, 8.0f), f.assign<Direction>(9.0f, 10.0f)));
auto thetag = f.assign<Tag>("tag");
g.assign<Position>(5.0f, 6.0f);
int i = 0;
ComponentHandle<Position> position;
REQUIRE(3 == size(em.entities_with_components(position)));
ComponentHandle<Direction> direction;
for (auto unused_entity : em.entities_with_components(position, direction)) {
(void)unused_entity;
REQUIRE(position);
REQUIRE(direction);
auto pd = position_directions.at(i);
REQUIRE(position == pd.first);
REQUIRE(direction == pd.second);
++i;
}
REQUIRE(2 == i);
ComponentHandle<Tag> tag;
i = 0;
for (auto unused_entity :
em.entities_with_components(position, direction, tag)) {
(void)unused_entity;
REQUIRE(static_cast<bool>(position));
REQUIRE(static_cast<bool>(direction));
REQUIRE(static_cast<bool>(tag));
auto pd = position_directions.at(1);
REQUIRE(position == pd.first);
REQUIRE(direction == pd.second);
REQUIRE(tag == thetag);
i++;
}
REQUIRE(1 == i);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestIterateAllEntitiesSkipsDestroyed") {
Entity a = em.create();
Entity b = em.create();
Entity c = em.create();
b.destroy();
auto it = em.entities_for_debugging().begin();
REQUIRE(a.id() == (*it).id());
++it;
REQUIRE(c.id() == (*it).id());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestUnpack") {
Entity e = em.create();
auto p = e.assign<Position>(1.0, 2.0);
auto d = e.assign<Direction>(3.0, 4.0);
auto t = e.assign<Tag>("tag");
ComponentHandle<Position> up;
ComponentHandle<Direction> ud;
ComponentHandle<Tag> ut;
e.unpack(up);
REQUIRE(p == up);
e.unpack(up, ud);
REQUIRE(p == up);
REQUIRE(d == ud);
e.unpack(up, ud, ut);
REQUIRE(p == up);
REQUIRE(d == ud);
REQUIRE(t == ut);
}
// gcc 4.7.2 does not allow this struct to be declared locally inside the
// TEST_CASE_METHOD.EntityManagerFixture, " //" TEST_CASE_METHOD(EntityManagerFixture, "TestUnpackNullMissing") {
// Entity e = em.create();
// auto p = e.assign<Position>();
// std::shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef),
// NullDeleter());
// std::shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef),
// NullDeleter());
// e.unpack<Position, Direction>(up, ud);
// REQUIRE(p == up);
// REQUIRE(std::shared_ptr<Direction>() == ud);
// }
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentIdsDiffer") {
REQUIRE(EntityManager::component_family<Position>() != EntityManager::component_family<Direction>());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityCreatedEvent") {
struct EntityCreatedEventReceiver
: public Receiver<EntityCreatedEventReceiver> {
void receive(const EntityCreatedEvent &event) {
created.push_back(event.entity);
}
vector<Entity> created;
};
EntityCreatedEventReceiver receiver;
ev.subscribe<EntityCreatedEvent>(receiver);
REQUIRE(0UL == receiver.created.size());
for (int i = 0; i < 10; ++i) {
em.create();
}
REQUIRE(10UL == receiver.created.size());
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyedEvent") {
struct EntityDestroyedEventReceiver
: public Receiver<EntityDestroyedEventReceiver> {
void receive(const EntityDestroyedEvent &event) {
destroyed.push_back(event.entity);
}
vector<Entity> destroyed;
};
EntityDestroyedEventReceiver receiver;
ev.subscribe<EntityDestroyedEvent>(receiver);
REQUIRE(0UL == receiver.destroyed.size());
vector<Entity> entities;
for (int i = 0; i < 10; ++i) {
entities.push_back(em.create());
}
REQUIRE(0UL == receiver.destroyed.size());
for (auto e : entities) {
e.destroy();
}
REQUIRE(10UL == receiver.destroyed.size());
REQUIRE(entities == receiver.destroyed);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAddedEvent") {
struct ComponentAddedEventReceiver
: public Receiver<ComponentAddedEventReceiver> {
ComponentAddedEventReceiver()
: position_events(0), direction_events(0) {}
void receive(const ComponentAddedEvent<Position> &event) {
auto p = event.component;
float n = static_cast<float>(position_events);
REQUIRE(p->x == n);
REQUIRE(p->y == n);
position_events++;
}
void receive(const ComponentAddedEvent<Direction> &event) {
auto p = event.component;
float n = static_cast<float>(direction_events);
REQUIRE(p->x == -n);
REQUIRE(p->y == -n);
direction_events++;
}
int position_events;
int direction_events;
};
ComponentAddedEventReceiver receiver;
ev.subscribe<ComponentAddedEvent<Position>>(receiver);
ev.subscribe<ComponentAddedEvent<Direction>>(receiver);
REQUIRE(ComponentAddedEvent<Position>::family() !=
ComponentAddedEvent<Direction>::family());
REQUIRE(0 == receiver.position_events);
REQUIRE(0 == receiver.direction_events);
for (int i = 0; i < 10; ++i) {
Entity e = em.create();
e.assign<Position>(static_cast<float>(i), static_cast<float>(i));
e.assign<Direction>(static_cast<float>(-i), static_cast<float>(-i));
}
REQUIRE(10 == receiver.position_events);
REQUIRE(10 == receiver.direction_events);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentRemovedEvent") {
struct ComponentRemovedReceiver : public Receiver<ComponentRemovedReceiver> {
void receive(const ComponentRemovedEvent<Direction> &event) {
removed = event.component;
}
ComponentHandle<Direction> removed;
};
ComponentRemovedReceiver receiver;
ev.subscribe<ComponentRemovedEvent<Direction>>(receiver);
REQUIRE(!(receiver.removed));
Entity e = em.create();
ComponentHandle<Direction> p = e.assign<Direction>(1.0, 2.0);
e.remove<Direction>();
REQUIRE(receiver.removed == p);
REQUIRE(!(e.component<Direction>()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentRemovedEventOnEntityDestroyed") {
struct ComponentRemovedReceiver : public Receiver<ComponentRemovedReceiver> {
void receive(const ComponentRemovedEvent<Direction> &event) {
removed = true;
}
bool removed = false;
};
ComponentRemovedReceiver receiver;
ev.subscribe<ComponentRemovedEvent<Direction>>(receiver);
REQUIRE(!(receiver.removed));
Entity e = em.create();
e.assign<Direction>(1.0, 2.0);
e.destroy();
REQUIRE(receiver.removed);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAssignment") {
Entity a, b;
a = em.create();
REQUIRE(a != b);
b = a;
REQUIRE(a == b);
a.invalidate();
REQUIRE(a != b);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyAll") {
Entity a = em.create(), b = em.create();
em.reset();
REQUIRE(!(a.valid()));
REQUIRE(!(b.valid()));
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyHole") {
std::vector<Entity> entities;
auto count = [this]()->int {
auto e = em.entities_with_components<Position>();
return std::count_if(e.begin(), e.end(),
[](const Entity &) { return true; });
};
for (int i = 0; i < 5000; i++) {
auto e = em.create();
e.assign<Position>();
entities.push_back(e);
}
REQUIRE(count() == 5000);
entities[2500].destroy();
REQUIRE(count() == 4999);
}
// TODO(alec): Disable this on OSX - it doesn't seem to be possible to catch it?!?
// TEST_CASE_METHOD(EntityManagerFixture, "DeleteComponentThrowsBadAlloc") {
// Position *position = new Position();
// REQUIRE_THROWS_AS(delete position, std::bad_alloc);
// }
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenEntityDestroyed") {
Entity a = em.create();
ComponentHandle<Position> position = a.assign<Position>(1, 2);
REQUIRE(position);
REQUIRE(position->x == 1);
REQUIRE(position->y == 2);
a.destroy();
REQUIRE(!position);
}
struct CopyVerifier : Component<CopyVerifier> {
CopyVerifier() : copied(false) {}
CopyVerifier(const CopyVerifier &other) {
copied = other.copied + 1;
}
int copied;
};
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAssignmentFromCopy") {
Entity a = em.create();
CopyVerifier original;
ComponentHandle<CopyVerifier> copy = a.assign_from_copy(original);
REQUIRE(copy);
REQUIRE(copy->copied == 1);
a.destroy();
REQUIRE(!copy);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityCreateFromCopy") {
Entity a = em.create();
a.assign<CopyVerifier>();
ComponentHandle<CopyVerifier> original = a.component<CopyVerifier>();
ComponentHandle<Position> aPosition = a.assign<Position>(1, 2);
Entity b = em.create_from_copy(a);
ComponentHandle<CopyVerifier> copy = b.component<CopyVerifier>();
ComponentHandle<Position> bPosition = b.component<Position>();
REQUIRE(original);
REQUIRE(original->copied == false);
REQUIRE(copy);
REQUIRE(copy->copied == 1);
REQUIRE(aPosition->x == bPosition->x);
REQUIRE(aPosition->y == bPosition->y);
REQUIRE(aPosition.get() != bPosition.get());
REQUIRE(a.component_mask() == b.component_mask());
REQUIRE(a != b);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenComponentDestroyed") {
Entity a = em.create();
ComponentHandle<Position> position = a.assign<Position>(1, 2);
REQUIRE(position);
REQUIRE(position->x == 1);
REQUIRE(position->y == 2);
a.remove<Position>();
REQUIRE(!position);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestDeleteEntityWithNoComponents") {
Entity a = em.create();
a.assign<Position>(1, 2);
Entity b = em.create();
b.destroy();
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdSet") {
Entity a = em.create();
Entity b = em.create();
Entity c = em.create();
set<Entity> entitySet;
REQUIRE(entitySet.insert(a).second);
REQUIRE(entitySet.insert(b).second);
REQUIRE(entitySet.insert(c).second);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdMap") {
Entity a = em.create();
Entity b = em.create();
Entity c = em.create();
map<Entity, int> entityMap;
REQUIRE(entityMap.insert(pair<Entity, int>(a, 1)).second);
REQUIRE(entityMap.insert(pair<Entity, int>(b, 2)).second);
REQUIRE(entityMap.insert(pair<Entity, int>(c, 3)).second);
REQUIRE(entityMap[a] == 1);
REQUIRE(entityMap[b] == 2);
REQUIRE(entityMap[c] == 3);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityComponentsFromTuple") {
Entity e = em.create();
e.assign<Position>(1, 2);
e.assign<Direction>(3, 4);
std::tuple<ComponentHandle<Position>, ComponentHandle<Direction>> components = e.components<Position, Direction>();
REQUIRE(std::get<0>(components)->x == 1);
REQUIRE(std::get<0>(components)->y == 2);
REQUIRE(std::get<1>(components)->x == 3);
REQUIRE(std::get<1>(components)->y == 4);
}
TEST_CASE("TestComponentDestructorCalledWhenManagerDestroyed") {
struct Freed {
explicit Freed(bool &yes) : yes(yes) {}
~Freed() { yes = true; }
bool &yes;
};
struct Test : Component<Test> {
explicit Test(bool &yes) : freed(yes) {}
Freed freed;
};
bool freed = false;
{
EntityX e;
auto test = e.entities.create();
test.assign<Test>(freed);
}
REQUIRE(freed == true);
}
TEST_CASE("TestComponentDestructorCalledWhenEntityDestroyed") {
struct Freed {
explicit Freed(bool &yes) : yes(yes) {}
~Freed() { yes = true; }
bool &yes;
};
struct Test : Component<Test> {
explicit Test(bool &yes) : freed(yes) {}
Freed freed;
};
bool freed = false;
EntityX e;
auto test = e.entities.create();
test.assign<Test>(freed);
REQUIRE(freed == false);
test.destroy();
REQUIRE(freed == true);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentsRemovedFromReusedEntities") {
Entity a = em.create();
Entity::Id aid = a.id();
a.assign<Position>(1, 2);
a.destroy();
Entity b = em.create();
Entity::Id bid = b.id();
REQUIRE(aid.index() == bid.index());
REQUIRE(!b.has_component<Position>());
b.assign<Position>(3, 4);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestConstComponentsNotInstantiatedTwice") {
Entity a = em.create();
a.assign<Position>(1, 2);
const Entity b = a;
REQUIRE(a.component<Position>().valid());
REQUIRE(b.component<const Position>().valid());
REQUIRE(b.component<const Position>()->x == 1);
REQUIRE(b.component<const Position>()->y == 2);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityManagerEach") {
Entity a = em.create();
a.assign<Position>(1, 2);
int count = 0;
em.each<Position>([&count](Entity entity, Position &position) {
count++;
REQUIRE(position.x == 1);
REQUIRE(position.y == 2);
});
REQUIRE(count == 1);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestViewEach") {
Entity a = em.create();
a.assign<Position>(1, 2);
int count = 0;
em.entities_with_components<Position>().each([&count](Entity entity, Position &position) {
count++;
REQUIRE(position.x == 1);
REQUIRE(position.y == 2);
});
REQUIRE(count == 1);
}
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentDereference") {
Entity a = em.create();
a.assign<Position>(10, 5);
auto& positionRef = *a.component<Position>();
REQUIRE(positionRef.x == 10);
REQUIRE(positionRef.y == 5);
positionRef.y = 20;
REQUIRE(a.component<Position>()->y == 20);
}