PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/entityx/Entity_test.cc

https://gitlab.com/sortofsleepy/noise-ink-lettering
C++ | 606 lines | 470 code | 107 blank | 29 comment | 90 complexity | 712ef5d62b18c2bc88c0986463586e32 MD5 | raw file
  1. /*
  2. * Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
  3. * All rights reserved.
  4. *
  5. * This software is licensed as described in the file COPYING, which
  6. * you should have received as part of this distribution.
  7. *
  8. * Author: Alec Thomas <alec@swapoff.org>
  9. */
  10. #define CATCH_CONFIG_MAIN
  11. #include <algorithm>
  12. #include <iterator>
  13. #include <string>
  14. #include <utility>
  15. #include <vector>
  16. #include <set>
  17. #include <map>
  18. #include "entityx/3rdparty/catch.hpp"
  19. #include "entityx/entityx.h"
  20. // using namespace std;
  21. using namespace entityx;
  22. using std::ostream;
  23. using std::vector;
  24. using std::set;
  25. using std::map;
  26. using std::pair;
  27. using std::string;
  28. template <typename T>
  29. int size(const T &t) {
  30. int n = 0;
  31. for (auto i : t) {
  32. ++n;
  33. (void)i; // Unused on purpose, suppress warning
  34. }
  35. return n;
  36. }
  37. struct Position {
  38. Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
  39. bool operator==(const Position &other) const {
  40. return x == other.x && y == other.y;
  41. }
  42. float x, y;
  43. };
  44. ostream &operator<<(ostream &out, const Position &position) {
  45. out << "Position(" << position.x << ", " << position.y << ")";
  46. return out;
  47. }
  48. struct Direction {
  49. Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
  50. bool operator==(const Direction &other) const {
  51. return x == other.x && y == other.y;
  52. }
  53. float x, y;
  54. };
  55. ostream &operator<<(ostream &out, const Direction &direction) {
  56. out << "Direction(" << direction.x << ", " << direction.y << ")";
  57. return out;
  58. }
  59. struct Tag : Component<Tag> {
  60. explicit Tag(string tag) : tag(tag) {}
  61. bool operator==(const Tag &other) const { return tag == other.tag; }
  62. string tag;
  63. };
  64. ostream &operator<<(ostream &out, const Tag &tag) {
  65. out << "Tag(" << tag.tag << ")";
  66. return out;
  67. }
  68. struct EntityManagerFixture {
  69. EntityManagerFixture() : em(ev) {}
  70. EventManager ev;
  71. EntityManager em;
  72. };
  73. TEST_CASE_METHOD(EntityManagerFixture, "TestCreateEntity") {
  74. REQUIRE(em.size() == 0UL);
  75. Entity e2;
  76. REQUIRE(!(e2.valid()));
  77. Entity e = em.create();
  78. REQUIRE(e.valid());
  79. REQUIRE(em.size() == 1UL);
  80. e2 = e;
  81. REQUIRE(e2.valid());
  82. }
  83. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAsBoolean") {
  84. REQUIRE(em.size() == 0UL);
  85. Entity e = em.create();
  86. REQUIRE(e.valid());
  87. REQUIRE(em.size() == 1UL);
  88. REQUIRE(!(!e));
  89. e.destroy();
  90. REQUIRE(em.size() == 0UL);
  91. REQUIRE(!e);
  92. Entity e2; // Not initialized
  93. REQUIRE(!e2);
  94. }
  95. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityReuse") {
  96. Entity e1 = em.create();
  97. Entity e2 = e1;
  98. auto id = e1.id();
  99. REQUIRE(e1.valid());
  100. REQUIRE(e2.valid());
  101. e1.destroy();
  102. REQUIRE(!e1.valid());
  103. REQUIRE(!e2.valid());
  104. Entity e3 = em.create();
  105. // It is assumed that the allocation will reuse the same entity id, though
  106. // the version will change.
  107. auto new_id = e3.id();
  108. REQUIRE(new_id != id);
  109. REQUIRE((new_id.id() & 0xffffffffUL) == (id.id() & 0xffffffffUL));
  110. }
  111. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentConstruction") {
  112. auto e = em.create();
  113. auto p = e.assign<Position>(1, 2);
  114. auto cp = e.component<Position>();
  115. REQUIRE(p == cp);
  116. REQUIRE(1.0 == Approx(cp->x));
  117. REQUIRE(2.0 == Approx(cp->y));
  118. }
  119. TEST_CASE_METHOD(EntityManagerFixture, "TestDestroyEntity") {
  120. Entity e = em.create();
  121. Entity f = em.create();
  122. e.assign<Position>();
  123. f.assign<Position>();
  124. e.assign<Direction>();
  125. f.assign<Direction>();
  126. REQUIRE(e.valid());
  127. REQUIRE(f.valid());
  128. REQUIRE(static_cast<bool>(e.component<Position>()));
  129. REQUIRE(static_cast<bool>(e.component<Direction>()));
  130. REQUIRE(static_cast<bool>(f.component<Position>()));
  131. REQUIRE(static_cast<bool>(f.component<Direction>()));
  132. e.destroy();
  133. REQUIRE(!(e.valid()));
  134. REQUIRE(f.valid());
  135. REQUIRE(static_cast<bool>(f.component<Position>()));
  136. REQUIRE(static_cast<bool>(f.component<Direction>()));
  137. }
  138. TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponent") {
  139. Entity e = em.create();
  140. Entity f = em.create();
  141. Entity g = em.create();
  142. e.assign<Position>();
  143. e.assign<Direction>();
  144. f.assign<Position>();
  145. g.assign<Position>();
  146. REQUIRE(3 == size(em.entities_with_components<Position>()));
  147. REQUIRE(1 == size(em.entities_with_components<Direction>()));
  148. }
  149. TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithIntersectionOfComponents") {
  150. vector<Entity> entities;
  151. for (int i = 0; i < 150; ++i) {
  152. Entity e = em.create();
  153. entities.push_back(e);
  154. if (i % 2 == 0) e.assign<Position>();
  155. if (i % 3 == 0) e.assign<Direction>();
  156. }
  157. REQUIRE(50 == size(em.entities_with_components<Direction>()));
  158. REQUIRE(75 == size(em.entities_with_components<Position>()));
  159. REQUIRE(25 == size(em.entities_with_components<Direction, Position>()));
  160. }
  161. TEST_CASE_METHOD(EntityManagerFixture, "TestGetEntitiesWithComponentAndUnpacking") {
  162. vector<Entity::Id> entities;
  163. Entity e = em.create();
  164. Entity f = em.create();
  165. Entity g = em.create();
  166. std::vector<std::pair<ComponentHandle<Position>, ComponentHandle<Direction>>> position_directions;
  167. position_directions.push_back(std::make_pair(
  168. e.assign<Position>(1.0f, 2.0f), e.assign<Direction>(3.0f, 4.0f)));
  169. position_directions.push_back(std::make_pair(
  170. f.assign<Position>(7.0f, 8.0f), f.assign<Direction>(9.0f, 10.0f)));
  171. auto thetag = f.assign<Tag>("tag");
  172. g.assign<Position>(5.0f, 6.0f);
  173. int i = 0;
  174. ComponentHandle<Position> position;
  175. REQUIRE(3 == size(em.entities_with_components(position)));
  176. ComponentHandle<Direction> direction;
  177. for (auto unused_entity : em.entities_with_components(position, direction)) {
  178. (void)unused_entity;
  179. REQUIRE(position);
  180. REQUIRE(direction);
  181. auto pd = position_directions.at(i);
  182. REQUIRE(position == pd.first);
  183. REQUIRE(direction == pd.second);
  184. ++i;
  185. }
  186. REQUIRE(2 == i);
  187. ComponentHandle<Tag> tag;
  188. i = 0;
  189. for (auto unused_entity :
  190. em.entities_with_components(position, direction, tag)) {
  191. (void)unused_entity;
  192. REQUIRE(static_cast<bool>(position));
  193. REQUIRE(static_cast<bool>(direction));
  194. REQUIRE(static_cast<bool>(tag));
  195. auto pd = position_directions.at(1);
  196. REQUIRE(position == pd.first);
  197. REQUIRE(direction == pd.second);
  198. REQUIRE(tag == thetag);
  199. i++;
  200. }
  201. REQUIRE(1 == i);
  202. }
  203. TEST_CASE_METHOD(EntityManagerFixture, "TestIterateAllEntitiesSkipsDestroyed") {
  204. Entity a = em.create();
  205. Entity b = em.create();
  206. Entity c = em.create();
  207. b.destroy();
  208. auto it = em.entities_for_debugging().begin();
  209. REQUIRE(a.id() == (*it).id());
  210. ++it;
  211. REQUIRE(c.id() == (*it).id());
  212. }
  213. TEST_CASE_METHOD(EntityManagerFixture, "TestUnpack") {
  214. Entity e = em.create();
  215. auto p = e.assign<Position>(1.0, 2.0);
  216. auto d = e.assign<Direction>(3.0, 4.0);
  217. auto t = e.assign<Tag>("tag");
  218. ComponentHandle<Position> up;
  219. ComponentHandle<Direction> ud;
  220. ComponentHandle<Tag> ut;
  221. e.unpack(up);
  222. REQUIRE(p == up);
  223. e.unpack(up, ud);
  224. REQUIRE(p == up);
  225. REQUIRE(d == ud);
  226. e.unpack(up, ud, ut);
  227. REQUIRE(p == up);
  228. REQUIRE(d == ud);
  229. REQUIRE(t == ut);
  230. }
  231. // gcc 4.7.2 does not allow this struct to be declared locally inside the
  232. // TEST_CASE_METHOD.EntityManagerFixture, " //" TEST_CASE_METHOD(EntityManagerFixture, "TestUnpackNullMissing") {
  233. // Entity e = em.create();
  234. // auto p = e.assign<Position>();
  235. // std::shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef),
  236. // NullDeleter());
  237. // std::shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef),
  238. // NullDeleter());
  239. // e.unpack<Position, Direction>(up, ud);
  240. // REQUIRE(p == up);
  241. // REQUIRE(std::shared_ptr<Direction>() == ud);
  242. // }
  243. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentIdsDiffer") {
  244. REQUIRE(EntityManager::component_family<Position>() != EntityManager::component_family<Direction>());
  245. }
  246. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityCreatedEvent") {
  247. struct EntityCreatedEventReceiver
  248. : public Receiver<EntityCreatedEventReceiver> {
  249. void receive(const EntityCreatedEvent &event) {
  250. created.push_back(event.entity);
  251. }
  252. vector<Entity> created;
  253. };
  254. EntityCreatedEventReceiver receiver;
  255. ev.subscribe<EntityCreatedEvent>(receiver);
  256. REQUIRE(0UL == receiver.created.size());
  257. for (int i = 0; i < 10; ++i) {
  258. em.create();
  259. }
  260. REQUIRE(10UL == receiver.created.size());
  261. }
  262. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyedEvent") {
  263. struct EntityDestroyedEventReceiver
  264. : public Receiver<EntityDestroyedEventReceiver> {
  265. void receive(const EntityDestroyedEvent &event) {
  266. destroyed.push_back(event.entity);
  267. }
  268. vector<Entity> destroyed;
  269. };
  270. EntityDestroyedEventReceiver receiver;
  271. ev.subscribe<EntityDestroyedEvent>(receiver);
  272. REQUIRE(0UL == receiver.destroyed.size());
  273. vector<Entity> entities;
  274. for (int i = 0; i < 10; ++i) {
  275. entities.push_back(em.create());
  276. }
  277. REQUIRE(0UL == receiver.destroyed.size());
  278. for (auto e : entities) {
  279. e.destroy();
  280. }
  281. REQUIRE(10UL == receiver.destroyed.size());
  282. REQUIRE(entities == receiver.destroyed);
  283. }
  284. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAddedEvent") {
  285. struct ComponentAddedEventReceiver
  286. : public Receiver<ComponentAddedEventReceiver> {
  287. ComponentAddedEventReceiver()
  288. : position_events(0), direction_events(0) {}
  289. void receive(const ComponentAddedEvent<Position> &event) {
  290. auto p = event.component;
  291. float n = static_cast<float>(position_events);
  292. REQUIRE(p->x == n);
  293. REQUIRE(p->y == n);
  294. position_events++;
  295. }
  296. void receive(const ComponentAddedEvent<Direction> &event) {
  297. auto p = event.component;
  298. float n = static_cast<float>(direction_events);
  299. REQUIRE(p->x == -n);
  300. REQUIRE(p->y == -n);
  301. direction_events++;
  302. }
  303. int position_events;
  304. int direction_events;
  305. };
  306. ComponentAddedEventReceiver receiver;
  307. ev.subscribe<ComponentAddedEvent<Position>>(receiver);
  308. ev.subscribe<ComponentAddedEvent<Direction>>(receiver);
  309. REQUIRE(ComponentAddedEvent<Position>::family() !=
  310. ComponentAddedEvent<Direction>::family());
  311. REQUIRE(0 == receiver.position_events);
  312. REQUIRE(0 == receiver.direction_events);
  313. for (int i = 0; i < 10; ++i) {
  314. Entity e = em.create();
  315. e.assign<Position>(static_cast<float>(i), static_cast<float>(i));
  316. e.assign<Direction>(static_cast<float>(-i), static_cast<float>(-i));
  317. }
  318. REQUIRE(10 == receiver.position_events);
  319. REQUIRE(10 == receiver.direction_events);
  320. }
  321. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentRemovedEvent") {
  322. struct ComponentRemovedReceiver : public Receiver<ComponentRemovedReceiver> {
  323. void receive(const ComponentRemovedEvent<Direction> &event) {
  324. removed = event.component;
  325. }
  326. ComponentHandle<Direction> removed;
  327. };
  328. ComponentRemovedReceiver receiver;
  329. ev.subscribe<ComponentRemovedEvent<Direction>>(receiver);
  330. REQUIRE(!(receiver.removed));
  331. Entity e = em.create();
  332. ComponentHandle<Direction> p = e.assign<Direction>(1.0, 2.0);
  333. e.remove<Direction>();
  334. REQUIRE(receiver.removed == p);
  335. REQUIRE(!(e.component<Direction>()));
  336. }
  337. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAssignment") {
  338. Entity a, b;
  339. a = em.create();
  340. REQUIRE(a != b);
  341. b = a;
  342. REQUIRE(a == b);
  343. a.invalidate();
  344. REQUIRE(a != b);
  345. }
  346. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyAll") {
  347. Entity a = em.create(), b = em.create();
  348. em.reset();
  349. REQUIRE(!(a.valid()));
  350. REQUIRE(!(b.valid()));
  351. }
  352. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityDestroyHole") {
  353. std::vector<Entity> entities;
  354. auto count = [this]()->int {
  355. auto e = em.entities_with_components<Position>();
  356. return std::count_if(e.begin(), e.end(),
  357. [](const Entity &) { return true; });
  358. };
  359. for (int i = 0; i < 5000; i++) {
  360. auto e = em.create();
  361. e.assign<Position>();
  362. entities.push_back(e);
  363. }
  364. REQUIRE(count() == 5000);
  365. entities[2500].destroy();
  366. REQUIRE(count() == 4999);
  367. }
  368. // TODO(alec): Disable this on OSX - it doesn't seem to be possible to catch it?!?
  369. // TEST_CASE_METHOD(EntityManagerFixture, "DeleteComponentThrowsBadAlloc") {
  370. // Position *position = new Position();
  371. // REQUIRE_THROWS_AS(delete position, std::bad_alloc);
  372. // }
  373. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenEntityDestroyed") {
  374. Entity a = em.create();
  375. ComponentHandle<Position> position = a.assign<Position>(1, 2);
  376. REQUIRE(position);
  377. REQUIRE(position->x == 1);
  378. REQUIRE(position->y == 2);
  379. a.destroy();
  380. REQUIRE(!position);
  381. }
  382. struct CopyVerifier : Component<CopyVerifier> {
  383. CopyVerifier() : copied(false) {}
  384. CopyVerifier(const CopyVerifier &other) {
  385. copied = other.copied + 1;
  386. }
  387. int copied;
  388. };
  389. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentAssignmentFromCopy") {
  390. Entity a = em.create();
  391. CopyVerifier original;
  392. ComponentHandle<CopyVerifier> copy = a.assign_from_copy(original);
  393. REQUIRE(copy);
  394. REQUIRE(copy->copied == 1);
  395. a.destroy();
  396. REQUIRE(!copy);
  397. }
  398. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentHandleInvalidatedWhenComponentDestroyed") {
  399. Entity a = em.create();
  400. ComponentHandle<Position> position = a.assign<Position>(1, 2);
  401. REQUIRE(position);
  402. REQUIRE(position->x == 1);
  403. REQUIRE(position->y == 2);
  404. a.remove<Position>();
  405. REQUIRE(!position);
  406. }
  407. TEST_CASE_METHOD(EntityManagerFixture, "TestDeleteEntityWithNoComponents") {
  408. Entity a = em.create();
  409. a.assign<Position>(1, 2);
  410. Entity b = em.create();
  411. b.destroy();
  412. }
  413. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdSet") {
  414. Entity a = em.create();
  415. Entity b = em.create();
  416. Entity c = em.create();
  417. set<Entity> entitySet;
  418. REQUIRE(entitySet.insert(a).second);
  419. REQUIRE(entitySet.insert(b).second);
  420. REQUIRE(entitySet.insert(c).second);
  421. }
  422. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityInStdMap") {
  423. Entity a = em.create();
  424. Entity b = em.create();
  425. Entity c = em.create();
  426. map<Entity, int> entityMap;
  427. REQUIRE(entityMap.insert(pair<Entity, int>(a, 1)).second);
  428. REQUIRE(entityMap.insert(pair<Entity, int>(b, 2)).second);
  429. REQUIRE(entityMap.insert(pair<Entity, int>(c, 3)).second);
  430. REQUIRE(entityMap[a] == 1);
  431. REQUIRE(entityMap[b] == 2);
  432. REQUIRE(entityMap[c] == 3);
  433. }
  434. TEST_CASE_METHOD(EntityManagerFixture, "TestEntityComponentsFromTuple") {
  435. Entity e = em.create();
  436. e.assign<Position>(1, 2);
  437. e.assign<Direction>(3, 4);
  438. std::tuple<ComponentHandle<Position>, ComponentHandle<Direction>> components = e.components<Position, Direction>();
  439. REQUIRE(std::get<0>(components)->x == 1);
  440. REQUIRE(std::get<0>(components)->y == 2);
  441. REQUIRE(std::get<1>(components)->x == 3);
  442. REQUIRE(std::get<1>(components)->y == 4);
  443. }
  444. TEST_CASE("TestComponentDestructorCalledWhenManagerDestroyed") {
  445. struct Freed {
  446. explicit Freed(bool &yes) : yes(yes) {}
  447. ~Freed() { yes = true; }
  448. bool &yes;
  449. };
  450. struct Test : Component<Test> {
  451. explicit Test(bool &yes) : freed(yes) {}
  452. Freed freed;
  453. };
  454. bool freed = false;
  455. {
  456. EntityX e;
  457. auto test = e.entities.create();
  458. test.assign<Test>(freed);
  459. }
  460. REQUIRE(freed == true);
  461. }
  462. TEST_CASE("TestComponentDestructorCalledWhenEntityDestroyed") {
  463. struct Freed {
  464. explicit Freed(bool &yes) : yes(yes) {}
  465. ~Freed() { yes = true; }
  466. bool &yes;
  467. };
  468. struct Test : Component<Test> {
  469. explicit Test(bool &yes) : freed(yes) {}
  470. Freed freed;
  471. };
  472. bool freed = false;
  473. EntityX e;
  474. auto test = e.entities.create();
  475. test.assign<Test>(freed);
  476. REQUIRE(freed == false);
  477. test.destroy();
  478. REQUIRE(freed == true);
  479. }
  480. TEST_CASE_METHOD(EntityManagerFixture, "TestComponentsRemovedFromReusedEntities") {
  481. Entity a = em.create();
  482. Entity::Id aid = a.id();
  483. a.assign<Position>(1, 2);
  484. a.destroy();
  485. Entity b = em.create();
  486. Entity::Id bid = b.id();
  487. REQUIRE(aid.index() == bid.index());
  488. REQUIRE(!b.has_component<Position>());
  489. b.assign<Position>(3, 4);
  490. }
  491. TEST_CASE_METHOD(EntityManagerFixture, "TestConstComponentsNotInstantiatedTwice") {
  492. Entity a = em.create();
  493. a.assign<Position>(1, 2);
  494. const Entity b = a;
  495. REQUIRE(a.component<Position>().valid());
  496. REQUIRE(b.component<const Position>().valid());
  497. REQUIRE(b.component<const Position>()->x == 1);
  498. REQUIRE(b.component<const Position>()->y == 2);
  499. }