/* * ===================================================================================== * * Filename: catch-entityconverter.cpp * * Description: Unit tests for EntityConverter class * * Version: 1.0 * Created: 07/14/2017 20:36:31 PM * Revision: none * Compiler: gcc * * Author: surkeh@protonmail.com * * ===================================================================================== */ #include #include #include #include #include "EntityConverter.hpp" #define ENTITY_FILENAME "r2xonotic.ent" #define DELTA 0.00001 TEST_CASE ("r2x: Unsupported entity types cause return of empty vector", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type NotAType"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert (entity); REQUIRE (converted.size() == 0); } TEST_CASE ("r2x: a single Pickup entity can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type Pickup"); entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000"); entity.push_back (" UInt8 pickupType 2"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert (entity); REQUIRE (converted[0] == "\"classname\" \"weapon_grenadelauncher\"\n"); // The z (vertical) is offset by +2 std::istringstream iss (converted[1]); std::string attribute; std::string coords[2]; float offsetCoord; iss >> attribute >> coords[0] >> coords[1] >> offsetCoord; REQUIRE (attribute == "\"origin\""); REQUIRE (coords[0] == "\"-216.00000"); REQUIRE (coords[1] == "-1488.000488"); REQUIRE (fabs(-130.00000 - offsetCoord) <= DELTA); } TEST_CASE ("r2x: a single PlayerSpawn (race) entity can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up WorldSpawn entity // (needed for mode info) std::vector worldspawn; worldspawn.push_back (" type WorldSpawn"); worldspawn.push_back (" Bool8 modeCTF 0"); worldspawn.push_back (" Bool8 modeFFA 0"); worldspawn.push_back (" Bool8 modeTDM 0"); worldspawn.push_back (" Bool8 mode1v1 0"); // Mock up entity std::vector entity; entity.push_back (" type PlayerSpawn"); entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000"); entity.push_back (" Bool8 teamA 0"); entity.push_back (" Bool8 teamB 0"); entity.push_back (" Bool8 modeCTF 0"); entity.push_back (" Bool8 modeFFA 0"); entity.push_back (" Bool8 modeTDM 0"); entity.push_back (" Bool8 mode1v1 0"); // Mock up entity queue std::queue> q; q.push (worldspawn); q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity (worldspawn conversion returns empty vector // BUT sets the supported game modes for entities in that worldspawn std::vector unused = ec.convert(worldspawn); std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"info_player_race\"\n"); REQUIRE (converted[1] == "\"target\" \"cp1\"\n"); REQUIRE (converted[2] == "\"race_place\" \"-1\"\n"); // The z (vertical) is offset by +32 std::istringstream iss (converted[3]); std::string attribute; std::string coords[2]; float offsetCoord; iss >> attribute >> coords[0] >> coords[1] >> offsetCoord; REQUIRE (attribute == "\"origin\""); REQUIRE (coords[0] == "\"-216.00000"); REQUIRE (coords[1] == "-1488.000488"); REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA); SECTION( "Converted angles are valid (Different coordinate system handedness)") { std::istringstream angleLineStream(converted[4]); std::string angleAttribute; std::string a; float angle; angleLineStream >> attribute >> a; a.erase(a.begin()); //removing preceding quote is necessary std::stringstream angleStream(a); angleStream >> angle; REQUIRE (attribute == "\"angle\""); REQUIRE (fabs (-90.0 - angle) <= DELTA); } SECTION( "Encountering a new worldspawn reenables all modes") { std::vector basicWorldspawn; basicWorldspawn.push_back (" type WorldSpawn"); std::vector e; e.push_back (" type PlayerSpawn"); e.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); e.push_back (" Vector3 angles 180.00000 0.00000 0.00000"); std::vector u = ec.convert(basicWorldspawn); std::vector c = ec.convert(e); REQUIRE (c[0] == "\"classname\" \"info_player_deathmatch\"\n"); } } TEST_CASE ("r2x: a single PlayerSpawn (teamA) entity can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type PlayerSpawn"); entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000"); entity.push_back (" Bool8 teamB 0"); entity.push_back (" Bool8 modeRace 0"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"info_player_team1\"\n"); // The z (vertical) is offset by +32 std::istringstream iss (converted[1]); std::string attribute; std::string coords[2]; float offsetCoord; iss >> attribute >> coords[0] >> coords[1] >> offsetCoord; REQUIRE (attribute == "\"origin\""); REQUIRE (coords[0] == "\"-216.00000"); REQUIRE (coords[1] == "-1488.000488"); REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA); } TEST_CASE ("r2x: a single PlayerSpawn (non-team) entity can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type PlayerSpawn"); entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); entity.push_back (" Vector3 angles 180.00000 0.00000 0.00000"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"info_player_deathmatch\"\n"); // The z (vertical) is offset by +32 std::istringstream iss (converted[1]); std::string attribute; std::string coords[2]; float offsetCoord; iss >> attribute >> coords[0] >> coords[1] >> offsetCoord; REQUIRE (attribute == "\"origin\""); REQUIRE (coords[0] == "\"-216.00000"); REQUIRE (coords[1] == "-1488.000488"); REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA); } TEST_CASE ("r2x: a single RaceStart entity can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type RaceStart"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"trigger_race_checkpoint\"\n"); REQUIRE (converted[1] == "\"targetname\" \"cp1\"\n"); REQUIRE (converted[2] == "\"cnt\" \"1\"\n"); } TEST_CASE ("r2x: a single RaceFinish entity can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type RaceFinish"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"trigger_race_checkpoint\"\n"); REQUIRE (converted[1] == "\"targetname\" \"finish\"\n"); REQUIRE (converted[2] == "\"cnt\" \"0\"\n"); } TEST_CASE ("r2x: a single Teleporter and related Target can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up Teleporter entity std::vector entity; entity.push_back (" type Teleporter"); entity.push_back (" String32 target tp1"); // Mock up Target entity std::vector entity2; entity2.push_back (" type Target"); entity2.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); entity2.push_back (" String32 name tp1"); // Mock up entity queue std::queue> q; q.push (entity); q.push (entity2); // Match related entities (one pair) ec.extractMapInfo (q); // Convert two entities std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"trigger_teleport\"\n"); REQUIRE (converted[1] == "\"target\" \"tp1\"\n"); std::vector converted2 = ec.convert(entity2); REQUIRE (converted2[0] == "\"classname\" \"misc_teleporter_dest\"\n"); REQUIRE (converted2[2] == "\"targetname\" \"tp1\"\n"); // // The z (vertical) is offset by +32 std::istringstream iss (converted2[1]); std::string attribute; std::string coords[2]; float offsetCoord; iss >> attribute >> coords[0] >> coords[1] >> offsetCoord; // next REQUIRE fails without busy wait for( int i = 0; i < 10000000; i++) int x = i; REQUIRE (attribute == "\"origin\""); REQUIRE (coords[0] == "\"-216.00000"); REQUIRE (coords[1] == "-1488.000488"); REQUIRE (fabs(-100.00000 - offsetCoord) <= DELTA); SECTION( "When angle unspecified, defaults to 0.0 (converted to 90.0)") { std::istringstream angleStream(converted2[3]); std::string a; float angle; angleStream >> attribute >> a; a.erase(a.begin()); //removing preceding quote is necessary std::stringstream out(a); out >> angle; REQUIRE (fabs(90.0 - angle) <= DELTA); } } TEST_CASE ("r2x: a single JumpPad and related Target can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up JumpPad entity std::vector entity; entity.push_back (" type JumpPad"); entity.push_back (" String32 target jp1"); // Mock up Target entity std::vector entity2; entity2.push_back (" type Target"); entity2.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); entity2.push_back (" String32 name jp1"); // Mock up entity queue std::queue> q; q.push (entity); q.push (entity2); // Match related entities (one pair) ec.extractMapInfo (q); // Convert two entities std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"trigger_push\"\n"); REQUIRE (converted[1] == "\"target\" \"jp1\"\n"); std::vector converted2 = ec.convert(entity2); REQUIRE (converted2[0] == "\"classname\" \"target_position\"\n"); REQUIRE (converted2[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n"); REQUIRE (converted2[2] == "\"targetname\" \"jp1\"\n"); } TEST_CASE ("r2x: a single PointLight entity can be converted", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type PointLight"); entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); entity.push_back (" ColourXRGB32 color ffffc400"); entity.push_back (" Float intensity 1.500000"); entity.push_back (" Float nearAttenuation 32.000000"); entity.push_back (" Float farAttenuation 160.000000"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"light\"\n"); REQUIRE (converted[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n"); REQUIRE (converted[2] == "\"light\" \"75\"\n"); INFO (converted[3]); std::istringstream iss (converted[3]); std::string attribute; std::string r; float red; float green; float blue; iss >> attribute >> r >> green >> blue; r.erase(r.begin()); //removing preceding quote is necessary std::stringstream redStream(r); redStream >> red; REQUIRE (attribute == "\"_color\""); REQUIRE (fabs (1.0 - red) <= DELTA); REQUIRE (fabs (0.768627 - green) <= DELTA); REQUIRE (fabs (0.0 - blue) <= DELTA); } TEST_CASE ("r2x: PointLight defaults to white light of typical intensity", "[EntityConverter]") { // Instantiate object EntityConverter ec (ENTITY_FILENAME); // Mock up entity std::vector entity; entity.push_back (" type PointLight"); entity.push_back (" Vector3 position -216.00000 -132.00000 -1488.000488"); // Mock up entity queue std::queue> q; q.push (entity); // Match related entities (none) ec.extractMapInfo (q); // Convert a single entity std::vector converted = ec.convert(entity); REQUIRE (converted[0] == "\"classname\" \"light\"\n"); REQUIRE (converted[1] == "\"origin\" \"-216.00000 -1488.000488 -132.00000\"\n"); REQUIRE (converted[2] == "\"light\" \"50\"\n"); INFO (converted[3]); std::istringstream iss (converted[3]); std::string attribute; std::string r; float red; float green; float blue; iss >> attribute >> r >> green >> blue; r.erase (r.begin()); //removing preceding quote is necessary std::stringstream redStream (r); redStream >> red; REQUIRE (attribute == "\"_color\""); REQUIRE (fabs (0.0 - red) <= DELTA); REQUIRE (fabs (0.0 - green) <= DELTA); REQUIRE (fabs (0.0 - blue) <= DELTA); }