catch-entityconverter.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. /*
  2. * =====================================================================================
  3. *
  4. * Filename: catch-entityconverter.cpp
  5. *
  6. * Description: Unit tests for EntityConverter class
  7. *
  8. * Version: 1.0
  9. * Created: 07/14/2017 20:36:31 PM
  10. * Revision: none
  11. * Compiler: gcc
  12. *
  13. * Author: surkeh@protonmail.com
  14. *
  15. * =====================================================================================
  16. */
  17. #include <cmath>
  18. #include <queue>
  19. #include <vector>
  20. #include <sstream>
  21. #include "EntityConverter.hpp"
  22. #define ENTITY_FILENAME "r2xonotic.rem"
  23. #define DELTA 0.00001
  24. TEST_CASE ("r2x: Unsupported entity types cause return of empty vector", "[EntityConverter]") {
  25. // Instantiate object
  26. EntityConverter ec (ENTITY_FILENAME);
  27. // Mock up entity
  28. std::vector<std::string> entity;
  29. entity.push_back (" type NotAType");
  30. // Mock up entity queue
  31. std::queue<std::vector<std::string>> q;
  32. q.push (entity);
  33. // Match related entities (none)
  34. ec.extractMapInfo (q);
  35. // Convert a single entity
  36. std::vector<std::string> converted = ec.convert (entity);
  37. REQUIRE (converted.size() == 0);
  38. }
  39. TEST_CASE ("r2x: a single Pickup entity can be converted", "[EntityConverter]") {
  40. // Instantiate object
  41. EntityConverter ec (ENTITY_FILENAME);
  42. // Mock up entity
  43. std::vector<std::string> entity;
  44. entity.push_back (" type Pickup");
  45. entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  46. entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000");
  47. entity.push_back (" UInt8 pickupType 2");
  48. // Mock up entity queue
  49. std::queue<std::vector<std::string>> q;
  50. q.push (entity);
  51. // Match related entities (none)
  52. ec.extractMapInfo (q);
  53. // Convert a single entity
  54. std::vector<std::string> converted = ec.convert (entity);
  55. REQUIRE (converted[0] == "\"classname\" \"weapon_grenadelauncher\"\n");
  56. // The z (vertical) is offset by +2
  57. std::istringstream iss (converted[1]);
  58. std::string attribute;
  59. std::string coords[2];
  60. float offsetCoord;
  61. iss >> attribute >> coords[0] >> coords[1] >> offsetCoord;
  62. REQUIRE (attribute == "\"origin\"");
  63. REQUIRE (coords[0] == "\"-216.00000");
  64. REQUIRE (coords[1] == "-1488.000488");
  65. REQUIRE (fabs(-130.00000 - offsetCoord) <= DELTA);
  66. }
  67. TEST_CASE ("r2x: a single PlayerSpawn (race) entity can be converted", "[EntityConverter]") {
  68. // Instantiate object
  69. EntityConverter ec (ENTITY_FILENAME);
  70. // Mock up WorldSpawn entity
  71. // (needed for mode info)
  72. std::vector<std::string> worldspawn;
  73. worldspawn.push_back (" type WorldSpawn");
  74. worldspawn.push_back (" Bool8 modeCTF 0");
  75. worldspawn.push_back (" Bool8 modeFFA 0");
  76. worldspawn.push_back (" Bool8 modeTDM 0");
  77. worldspawn.push_back (" Bool8 mode1v1 0");
  78. // Mock up entity
  79. std::vector<std::string> entity;
  80. entity.push_back (" type PlayerSpawn");
  81. entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  82. entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000");
  83. entity.push_back (" Bool8 teamA 0");
  84. entity.push_back (" Bool8 teamB 0");
  85. entity.push_back (" Bool8 modeCTF 0");
  86. entity.push_back (" Bool8 modeFFA 0");
  87. entity.push_back (" Bool8 modeTDM 0");
  88. entity.push_back (" Bool8 mode1v1 0");
  89. // Mock up entity queue
  90. std::queue<std::vector<std::string>> q;
  91. q.push (worldspawn);
  92. q.push (entity);
  93. // Match related entities (none)
  94. ec.extractMapInfo (q);
  95. // Convert a single entity (worldspawn conversion returns empty vector
  96. // BUT sets the supported game modes for entities in that worldspawn
  97. std::vector<std::string> unused = ec.convert(worldspawn);
  98. std::vector<std::string> converted = ec.convert(entity);
  99. REQUIRE (converted[0] == "\"classname\" \"info_player_start\"\n");
  100. // The z (vertical) is offset by +32
  101. std::istringstream iss (converted[1]);
  102. std::string attribute;
  103. std::string coords[2];
  104. float offsetCoord;
  105. iss >> attribute >> coords[0] >> coords[1] >> offsetCoord;
  106. REQUIRE (attribute == "\"origin\"");
  107. REQUIRE (coords[0] == "\"-216.00000");
  108. REQUIRE (coords[1] == "-1488.000488");
  109. REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA);
  110. SECTION( "Converted angles are valid (Different coordinate system handedness)") {
  111. std::istringstream angleLineStream(converted[2]);
  112. std::string angleAttribute;
  113. std::string a;
  114. float angle;
  115. angleLineStream >> attribute >> a;
  116. a.erase(a.begin()); //removing preceding quote is necessary
  117. std::stringstream angleStream(a);
  118. angleStream >> angle;
  119. REQUIRE (attribute == "\"angle\"");
  120. REQUIRE (fabs (-90.0 - angle) <= DELTA);
  121. }
  122. SECTION( "Encountering a new worldspawn reenables all modes") {
  123. std::vector<std::string> basicWorldspawn;
  124. basicWorldspawn.push_back (" type WorldSpawn");
  125. std::vector<std::string> e;
  126. e.push_back (" type PlayerSpawn");
  127. e.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  128. e.push_back (" Vector3 angles 180.00000 0.00000 0.00000");
  129. std::vector<std::string> u = ec.convert(basicWorldspawn);
  130. std::vector<std::string> c = ec.convert(e);
  131. REQUIRE (c[0] == "\"classname\" \"info_player_deathmatch\"\n");
  132. }
  133. }
  134. TEST_CASE ("r2x: a single PlayerSpawn (teamA) entity can be converted", "[EntityConverter]") {
  135. // Instantiate object
  136. EntityConverter ec (ENTITY_FILENAME);
  137. // Mock up entity
  138. std::vector<std::string> entity;
  139. entity.push_back (" type PlayerSpawn");
  140. entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  141. entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000");
  142. entity.push_back (" Bool8 teamB 0");
  143. entity.push_back (" Bool8 modeRace 0");
  144. // Mock up entity queue
  145. std::queue<std::vector<std::string>> q;
  146. q.push (entity);
  147. // Match related entities (none)
  148. ec.extractMapInfo (q);
  149. // Convert a single entity
  150. std::vector<std::string> converted = ec.convert(entity);
  151. REQUIRE (converted[0] == "\"classname\" \"info_player_team1\"\n");
  152. // The z (vertical) is offset by +32
  153. std::istringstream iss (converted[1]);
  154. std::string attribute;
  155. std::string coords[2];
  156. float offsetCoord;
  157. iss >> attribute >> coords[0] >> coords[1] >> offsetCoord;
  158. REQUIRE (attribute == "\"origin\"");
  159. REQUIRE (coords[0] == "\"-216.00000");
  160. REQUIRE (coords[1] == "-1488.000488");
  161. REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA);
  162. }
  163. TEST_CASE ("r2x: a single PlayerSpawn (non-team) entity can be converted", "[EntityConverter]") {
  164. // Instantiate object
  165. EntityConverter ec (ENTITY_FILENAME);
  166. // Mock up entity
  167. std::vector<std::string> entity;
  168. entity.push_back (" type PlayerSpawn");
  169. entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  170. entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000");
  171. // Mock up entity queue
  172. std::queue<std::vector<std::string>> q;
  173. q.push (entity);
  174. // Match related entities (none)
  175. ec.extractMapInfo (q);
  176. // Convert a single entity
  177. std::vector<std::string> converted = ec.convert(entity);
  178. REQUIRE (converted[0] == "\"classname\" \"info_player_deathmatch\"\n");
  179. // The z (vertical) is offset by +32
  180. std::istringstream iss (converted[1]);
  181. std::string attribute;
  182. std::string coords[2];
  183. float offsetCoord;
  184. iss >> attribute >> coords[0] >> coords[1] >> offsetCoord;
  185. REQUIRE (attribute == "\"origin\"");
  186. REQUIRE (coords[0] == "\"-216.00000");
  187. REQUIRE (coords[1] == "-1488.000488");
  188. REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA);
  189. }
  190. TEST_CASE ("r2x: a single RaceStart entity can be converted", "[EntityConverter]") {
  191. // Instantiate object
  192. EntityConverter ec (ENTITY_FILENAME);
  193. // Mock up entity
  194. std::vector<std::string> entity;
  195. entity.push_back (" type RaceStart");
  196. // Mock up entity queue
  197. std::queue<std::vector<std::string>> q;
  198. q.push (entity);
  199. // Match related entities (none)
  200. ec.extractMapInfo (q);
  201. // Convert a single entity
  202. std::vector<std::string> converted = ec.convert(entity);
  203. REQUIRE (converted[0] == "\"classname\" \"trigger_race_checkpoint\"\n");
  204. REQUIRE (converted[1] == "\"targetname\" \"cp1\"\n");
  205. REQUIRE (converted[2] == "\"cnt\" \"0\"\n");
  206. }
  207. TEST_CASE ("r2x: a single RaceFinish entity can be converted", "[EntityConverter]") {
  208. // Instantiate object
  209. EntityConverter ec (ENTITY_FILENAME);
  210. // Mock up entity
  211. std::vector<std::string> entity;
  212. entity.push_back (" type RaceFinish");
  213. // Mock up entity queue
  214. std::queue<std::vector<std::string>> q;
  215. q.push (entity);
  216. // Match related entities (none)
  217. ec.extractMapInfo (q);
  218. // Convert a single entity
  219. std::vector<std::string> converted = ec.convert(entity);
  220. REQUIRE (converted[0] == "\"classname\" \"trigger_race_checkpoint\"\n");
  221. REQUIRE (converted[1] == "\"targetname\" \"finish\"\n");
  222. REQUIRE (converted[2] == "\"cnt\" \"1\"\n");
  223. }
  224. TEST_CASE ("r2x: a single Teleporter and related Target can be converted", "[EntityConverter]") {
  225. // Instantiate object
  226. EntityConverter ec (ENTITY_FILENAME);
  227. // Mock up Teleporter entity
  228. std::vector<std::string> entity;
  229. entity.push_back (" type Teleporter");
  230. entity.push_back (" String32 target tp1");
  231. // Mock up Target entity
  232. std::vector<std::string> entity2;
  233. entity2.push_back (" type Target");
  234. entity2.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  235. entity2.push_back (" String32 name tp1");
  236. // Mock up entity queue
  237. std::queue<std::vector<std::string>> q;
  238. q.push (entity);
  239. q.push (entity2);
  240. // Match related entities (one pair)
  241. ec.extractMapInfo (q);
  242. // Convert two entities
  243. std::vector<std::string> converted = ec.convert(entity);
  244. REQUIRE (converted[0] == "\"classname\" \"trigger_teleport\"\n");
  245. REQUIRE (converted[1] == "\"target\" \"tp1\"\n");
  246. std::vector<std::string> converted2 = ec.convert(entity2);
  247. REQUIRE (converted2[0] == "\"classname\" \"misc_teleporter_dest\"\n");
  248. REQUIRE (converted2[2] == "\"targetname\" \"tp1\"\n");
  249. //
  250. // The z (vertical) is offset by +32
  251. std::istringstream iss (converted2[1]);
  252. std::string attribute;
  253. std::string coords[2];
  254. float offsetCoord;
  255. iss >> attribute >> coords[0] >> coords[1] >> offsetCoord;
  256. // next REQUIRE fails without busy wait
  257. for( int i = 0; i < 10000000; i++)
  258. int x = i;
  259. REQUIRE (attribute == "\"origin\"");
  260. REQUIRE (coords[0] == "\"-216.00000");
  261. REQUIRE (coords[1] == "-1488.000488");
  262. REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA);
  263. SECTION( "When angle unspecified, defaults to 0.0 (converted to 90.0)") {
  264. std::istringstream angleStream(converted2[3]);
  265. std::string a;
  266. float angle;
  267. angleStream >> attribute >> a;
  268. a.erase(a.begin()); //removing preceding quote is necessary
  269. std::stringstream out(a);
  270. out >> angle;
  271. REQUIRE (fabs(90.0 - angle) <= DELTA);
  272. }
  273. }
  274. TEST_CASE ("r2x: a single JumpPad and related Target can be converted", "[EntityConverter]") {
  275. // Instantiate object
  276. EntityConverter ec (ENTITY_FILENAME);
  277. // Mock up JumpPad entity
  278. std::vector<std::string> entity;
  279. entity.push_back (" type JumpPad");
  280. entity.push_back (" String32 target jp1");
  281. // Mock up Target entity
  282. std::vector<std::string> entity2;
  283. entity2.push_back (" type Target");
  284. entity2.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  285. entity2.push_back (" String32 name jp1");
  286. // Mock up entity queue
  287. std::queue<std::vector<std::string>> q;
  288. q.push (entity);
  289. q.push (entity2);
  290. // Match related entities (one pair)
  291. ec.extractMapInfo (q);
  292. // Convert two entities
  293. std::vector<std::string> converted = ec.convert(entity);
  294. REQUIRE (converted[0] == "\"classname\" \"trigger_push\"\n");
  295. REQUIRE (converted[1] == "\"target\" \"jp1\"\n");
  296. std::vector<std::string> converted2 = ec.convert(entity2);
  297. REQUIRE (converted2[0] == "\"classname\" \"target_position\"\n");
  298. REQUIRE (converted2[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n");
  299. REQUIRE (converted2[2] == "\"targetname\" \"jp1\"\n");
  300. }
  301. TEST_CASE ("r2x: a single PointLight entity can be converted", "[EntityConverter]") {
  302. // Instantiate object
  303. EntityConverter ec (ENTITY_FILENAME);
  304. // Mock up entity
  305. std::vector<std::string> entity;
  306. entity.push_back (" type PointLight");
  307. entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  308. entity.push_back (" ColourXRGB32 color ffffc400");
  309. entity.push_back (" Float intensity 1.500000");
  310. entity.push_back (" Float nearAttenuation 32.000000");
  311. entity.push_back (" Float farAttenuation 160.000000");
  312. // Mock up entity queue
  313. std::queue<std::vector<std::string>> q;
  314. q.push (entity);
  315. // Match related entities (none)
  316. ec.extractMapInfo (q);
  317. // Convert a single entity
  318. std::vector<std::string> converted = ec.convert(entity);
  319. REQUIRE (converted[0] == "\"classname\" \"light\"\n");
  320. REQUIRE (converted[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n");
  321. REQUIRE (converted[2] == "\"light\" \"75\"\n");
  322. INFO (converted[3]);
  323. std::istringstream iss (converted[3]);
  324. std::string attribute;
  325. std::string r;
  326. float red;
  327. float green;
  328. float blue;
  329. iss >> attribute >> r >> green >> blue;
  330. r.erase(r.begin()); //removing preceding quote is necessary
  331. std::stringstream redStream(r);
  332. redStream >> red;
  333. REQUIRE (attribute == "\"_color\"");
  334. REQUIRE (fabs (1.0 - red) <= DELTA);
  335. REQUIRE (fabs (0.768627 - green) <= DELTA);
  336. REQUIRE (fabs (0.0 - blue) <= DELTA);
  337. }
  338. TEST_CASE ("r2x: PointLight defaults to white light of typical intensity", "[EntityConverter]") {
  339. // Instantiate object
  340. EntityConverter ec (ENTITY_FILENAME);
  341. // Mock up entity
  342. std::vector<std::string> entity;
  343. entity.push_back (" type PointLight");
  344. entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488");
  345. // Mock up entity queue
  346. std::queue<std::vector<std::string>> q;
  347. q.push (entity);
  348. // Match related entities (none)
  349. ec.extractMapInfo (q);
  350. // Convert a single entity
  351. std::vector<std::string> converted = ec.convert(entity);
  352. REQUIRE (converted[0] == "\"classname\" \"light\"\n");
  353. REQUIRE (converted[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n");
  354. REQUIRE (converted[2] == "\"light\" \"50\"\n");
  355. INFO (converted[3]);
  356. std::istringstream iss (converted[3]);
  357. std::string attribute;
  358. std::string r;
  359. float red;
  360. float green;
  361. float blue;
  362. iss >> attribute >> r >> green >> blue;
  363. r.erase (r.begin()); //removing preceding quote is necessary
  364. std::stringstream redStream (r);
  365. redStream >> red;
  366. REQUIRE (attribute == "\"_color\"");
  367. REQUIRE (fabs (0.0 - red) <= DELTA);
  368. REQUIRE (fabs (0.0 - green) <= DELTA);
  369. REQUIRE (fabs (0.0 - blue) <= DELTA);
  370. }