/libs/entityx/Entity_test.cc
C++ | 606 lines | 470 code | 107 blank | 29 comment | 90 complexity | 712ef5d62b18c2bc88c0986463586e32 MD5 | raw file
- /*
- * 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, "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, "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);
- }