MapComparer.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * MapComparer.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "MapComparer.h"
  12. #include "../lib/ScopeGuard.h"
  13. #include "../lib/mapping/CMap.h"
  14. #define VCMI_CHECK_FIELD_EQUAL_P(field) EXPECT_EQ(actual->field, expected->field)
  15. #define VCMI_CHECK_FIELD_EQUAL(field) EXPECT_EQ(actual.field, expected.field)
  16. #define VCMI_REQUIRE_FIELD_EQUAL_P(field) ASSERT_EQ(actual->field, expected->field)
  17. #define VCMI_REQUIRE_FIELD_EQUAL(field) ASSERT_EQ(actual.field, expected.field)
  18. template <class T>
  19. void checkEqual(const T & actual, const T & expected)
  20. {
  21. EXPECT_EQ(actual, expected) ;
  22. }
  23. void checkEqual(const std::vector<bool> & actual, const std::vector<bool> & expected)
  24. {
  25. EXPECT_EQ(actual.size(), expected.size());
  26. for(auto actualIt = actual.begin(), expectedIt = expected.begin(); actualIt != actual.end() && expectedIt != expected.end(); actualIt++, expectedIt++)
  27. {
  28. checkEqual(*actualIt, *expectedIt);
  29. }
  30. }
  31. template <class Element>
  32. void checkEqual(const std::vector<Element> & actual, const std::vector<Element> & expected)
  33. {
  34. EXPECT_EQ(actual.size(), expected.size());
  35. for(auto actualIt = actual.begin(), expectedIt = expected.begin(); actualIt != actual.end() && expectedIt != expected.end(); actualIt++, expectedIt++)
  36. {
  37. checkEqual(*actualIt, *expectedIt);
  38. }
  39. }
  40. template <class Element>
  41. void checkEqual(const std::set<Element> & actual, const std::set<Element> & expected)
  42. {
  43. EXPECT_EQ(actual.size(), expected.size());
  44. for(auto elem : expected)
  45. {
  46. if(!vstd::contains(actual, elem))
  47. FAIL() << "Required element not found "+boost::to_string(elem);
  48. }
  49. }
  50. void checkEqual(const SHeroName & actual, const SHeroName & expected)
  51. {
  52. VCMI_CHECK_FIELD_EQUAL(heroId);
  53. VCMI_CHECK_FIELD_EQUAL(heroName);
  54. }
  55. void checkEqual(const PlayerInfo & actual, const PlayerInfo & expected)
  56. {
  57. VCMI_CHECK_FIELD_EQUAL(canHumanPlay);
  58. VCMI_CHECK_FIELD_EQUAL(canComputerPlay);
  59. VCMI_CHECK_FIELD_EQUAL(aiTactic);
  60. checkEqual(actual.allowedFactions, expected.allowedFactions);
  61. VCMI_CHECK_FIELD_EQUAL(isFactionRandom);
  62. VCMI_CHECK_FIELD_EQUAL(mainCustomHeroPortrait);
  63. VCMI_CHECK_FIELD_EQUAL(mainCustomHeroName);
  64. VCMI_CHECK_FIELD_EQUAL(mainCustomHeroId);
  65. checkEqual(actual.heroesNames, expected.heroesNames);
  66. VCMI_CHECK_FIELD_EQUAL(hasMainTown);
  67. VCMI_CHECK_FIELD_EQUAL(generateHeroAtMainTown);
  68. VCMI_CHECK_FIELD_EQUAL(posOfMainTown);
  69. VCMI_CHECK_FIELD_EQUAL(team);
  70. VCMI_CHECK_FIELD_EQUAL(hasRandomHero);
  71. }
  72. void checkEqual(const EventExpression & actual, const EventExpression & expected)
  73. {
  74. //todo: checkEventExpression
  75. }
  76. void checkEqual(const TriggeredEvent & actual, const TriggeredEvent & expected)
  77. {
  78. VCMI_CHECK_FIELD_EQUAL(identifier);
  79. VCMI_CHECK_FIELD_EQUAL(description);
  80. VCMI_CHECK_FIELD_EQUAL(onFulfill);
  81. VCMI_CHECK_FIELD_EQUAL(effect.type);
  82. VCMI_CHECK_FIELD_EQUAL(effect.toOtherMessage);
  83. checkEqual(actual.trigger, expected.trigger);
  84. }
  85. void checkEqual(const Rumor & actual, const Rumor & expected)
  86. {
  87. VCMI_CHECK_FIELD_EQUAL(name);
  88. VCMI_CHECK_FIELD_EQUAL(text);
  89. }
  90. void checkEqual(const DisposedHero & actual, const DisposedHero & expected)
  91. {
  92. VCMI_CHECK_FIELD_EQUAL(heroId);
  93. VCMI_CHECK_FIELD_EQUAL(portrait);
  94. VCMI_CHECK_FIELD_EQUAL(name);
  95. VCMI_CHECK_FIELD_EQUAL(players);
  96. }
  97. void checkEqual(const ObjectTemplate & actual, const ObjectTemplate & expected)
  98. {
  99. VCMI_CHECK_FIELD_EQUAL(id);
  100. VCMI_CHECK_FIELD_EQUAL(subid);
  101. VCMI_CHECK_FIELD_EQUAL(printPriority);
  102. VCMI_CHECK_FIELD_EQUAL(animationFile);
  103. //VCMI_CHECK_FIELD_EQUAL(stringID);
  104. }
  105. void checkEqual(const TerrainTile & actual, const TerrainTile & expected)
  106. {
  107. //fatal fail here on any error
  108. VCMI_REQUIRE_FIELD_EQUAL(terType);
  109. VCMI_REQUIRE_FIELD_EQUAL(terView);
  110. VCMI_REQUIRE_FIELD_EQUAL(riverType);
  111. VCMI_REQUIRE_FIELD_EQUAL(riverDir);
  112. VCMI_REQUIRE_FIELD_EQUAL(roadType);
  113. VCMI_REQUIRE_FIELD_EQUAL(roadDir);
  114. VCMI_REQUIRE_FIELD_EQUAL(extTileFlags);
  115. ASSERT_EQ(actual.blockingObjects.size(), expected.blockingObjects.size());
  116. ASSERT_EQ(actual.visitableObjects.size(), expected.visitableObjects.size());
  117. VCMI_REQUIRE_FIELD_EQUAL(visitable);
  118. VCMI_REQUIRE_FIELD_EQUAL(blocked);
  119. }
  120. //MapComparer
  121. void MapComparer::compareHeader()
  122. {
  123. //map size parameters are vital for further checks
  124. VCMI_REQUIRE_FIELD_EQUAL_P(height);
  125. VCMI_REQUIRE_FIELD_EQUAL_P(width);
  126. VCMI_REQUIRE_FIELD_EQUAL_P(twoLevel);
  127. VCMI_CHECK_FIELD_EQUAL_P(name);
  128. VCMI_CHECK_FIELD_EQUAL_P(description);
  129. VCMI_CHECK_FIELD_EQUAL_P(difficulty);
  130. VCMI_CHECK_FIELD_EQUAL_P(levelLimit);
  131. VCMI_CHECK_FIELD_EQUAL_P(victoryMessage);
  132. VCMI_CHECK_FIELD_EQUAL_P(defeatMessage);
  133. VCMI_CHECK_FIELD_EQUAL_P(victoryIconIndex);
  134. VCMI_CHECK_FIELD_EQUAL_P(defeatIconIndex);
  135. VCMI_CHECK_FIELD_EQUAL_P(howManyTeams);
  136. checkEqual(actual->players, expected->players);
  137. checkEqual(actual->allowedHeroes, expected->allowedHeroes);
  138. std::vector<TriggeredEvent> actualEvents = actual->triggeredEvents;
  139. std::vector<TriggeredEvent> expectedEvents = expected->triggeredEvents;
  140. auto sortByIdentifier = [](const TriggeredEvent & lhs, const TriggeredEvent & rhs) -> bool
  141. {
  142. return lhs.identifier < rhs.identifier;
  143. };
  144. boost::sort (actualEvents, sortByIdentifier);
  145. boost::sort (expectedEvents, sortByIdentifier);
  146. checkEqual(actualEvents, expectedEvents);
  147. }
  148. void MapComparer::compareOptions()
  149. {
  150. checkEqual(actual->rumors, expected->rumors);
  151. checkEqual(actual->disposedHeroes, expected->disposedHeroes);
  152. //todo: compareOptions predefinedHeroes
  153. checkEqual(actual->allowedAbilities, expected->allowedAbilities);
  154. checkEqual(actual->allowedArtifact, expected->allowedArtifact);
  155. checkEqual(actual->allowedSpell, expected->allowedSpell);
  156. //todo: compareOptions events
  157. }
  158. void MapComparer::compareObject(const CGObjectInstance * actual, const CGObjectInstance * expected)
  159. {
  160. EXPECT_EQ(actual->instanceName, expected->instanceName);
  161. EXPECT_EQ(typeid(actual).name(), typeid(expected).name());//todo: remove and use just comparison
  162. std::string actualFullID = boost::to_string(boost::format("%s(%d)|%s(%d) %d") % actual->typeName % actual->ID % actual->subTypeName % actual->subID % actual->tempOwner);
  163. std::string expectedFullID = boost::to_string(boost::format("%s(%d)|%s(%d) %d") % expected->typeName % expected->ID % expected->subTypeName % expected->subID % expected->tempOwner);
  164. EXPECT_EQ(actualFullID, expectedFullID);
  165. VCMI_CHECK_FIELD_EQUAL_P(pos);
  166. checkEqual(actual->appearance, expected->appearance);
  167. }
  168. void MapComparer::compareObjects()
  169. {
  170. EXPECT_EQ(actual->objects.size(), expected->objects.size());
  171. for(size_t idx = 0; idx < expected->objects.size(); idx++)
  172. {
  173. auto expectedObject = expected->objects[idx];
  174. ASSERT_EQ(idx, expectedObject->id.getNum());
  175. {
  176. auto it = expected->instanceNames.find(expectedObject->instanceName);
  177. ASSERT_NE(it, expected->instanceNames.end());
  178. }
  179. {
  180. auto it = actual->instanceNames.find(expectedObject->instanceName);
  181. ASSERT_NE(it, actual->instanceNames.end());
  182. auto actualObject = it->second;
  183. compareObject(actualObject, expectedObject);
  184. }
  185. }
  186. }
  187. void MapComparer::compareTerrain()
  188. {
  189. //assume map dimensions check passed
  190. //todo: separate check for underground
  191. for(int x = 0; x < expected->width; x++)
  192. for(int y = 0; y < expected->height; y++)
  193. {
  194. int3 coord(x,y,0);
  195. SCOPED_TRACE(coord.toString());
  196. checkEqual(actual->getTile(coord), expected->getTile(coord));
  197. }
  198. }
  199. void MapComparer::compare()
  200. {
  201. ASSERT_NE((void *) actual, (void *) expected); //should not point to the same object
  202. ASSERT_TRUE(actual != nullptr) << "Actual map is not defined";
  203. ASSERT_TRUE(expected != nullptr) << "Expected map is not defined";
  204. compareHeader();
  205. compareOptions();
  206. compareObjects();
  207. compareTerrain();
  208. }
  209. void MapComparer::operator() (const std::unique_ptr<CMap>& actual, const std::unique_ptr<CMap>& expected)
  210. {
  211. this->actual = actual.get();
  212. this->expected = expected.get();
  213. compare();
  214. }
  215. //JsonMapComparer
  216. JsonMapComparer::JsonMapComparer(bool strict_)
  217. : strict(strict_)
  218. {
  219. }
  220. vstd::ScopeGuard<JsonMapComparer::TScopeGuard> JsonMapComparer::pushName(const std::string & name)
  221. {
  222. namePath.push_back(name);
  223. return vstd::makeScopeGuard<TScopeGuard>([this](){namePath.pop_back();});
  224. }
  225. std::string JsonMapComparer::buildMessage(const std::string & message)
  226. {
  227. std::stringstream buf;
  228. for(auto & s : namePath)
  229. buf << s << "|";
  230. buf << " " << message;
  231. return buf.str();
  232. }
  233. void JsonMapComparer::addError(const std::string & message)
  234. {
  235. FAIL()<<buildMessage(message);
  236. }
  237. bool JsonMapComparer::isEmpty(const JsonNode & value)
  238. {
  239. switch (value.getType())
  240. {
  241. case JsonNode::DATA_NULL:
  242. return true;
  243. case JsonNode::DATA_BOOL:
  244. return !value.Bool();
  245. case JsonNode::DATA_FLOAT:
  246. return value.Float() == 0;
  247. case JsonNode::DATA_STRING:
  248. return value.String() == "";
  249. case JsonNode::DATA_VECTOR:
  250. return value.Vector().empty();
  251. case JsonNode::DATA_STRUCT:
  252. return value.Struct().empty();
  253. break;
  254. default:
  255. EXPECT_TRUE(false) << "Unknown Json type";
  256. return false;
  257. }
  258. }
  259. void JsonMapComparer::check(const bool condition, const std::string & message)
  260. {
  261. if(strict)
  262. ASSERT_TRUE(condition) << buildMessage(message);
  263. else
  264. EXPECT_TRUE(condition) << buildMessage(message);
  265. }
  266. void JsonMapComparer::checkEqualInteger(const si64 actual, const si64 expected)
  267. {
  268. if(actual != expected)
  269. {
  270. check(false, boost::str(boost::format("'%d' != '%d'") % actual % expected));
  271. }
  272. }
  273. void JsonMapComparer::checkEqualFloat(const double actual, const double expected)
  274. {
  275. if(std::abs(actual - expected) > 1e-6)
  276. {
  277. check(false, boost::str(boost::format("'%d' != '%d' (diff %d)") % actual % expected % (expected - actual)));
  278. }
  279. }
  280. void JsonMapComparer::checkEqualString(const std::string & actual, const std::string & expected)
  281. {
  282. if(actual != expected)
  283. {
  284. check(false, boost::str(boost::format("'%s' != '%s'") % actual % expected));
  285. }
  286. }
  287. void JsonMapComparer::checkEqualJson(const JsonMap & actual, const JsonMap & expected)
  288. {
  289. for(const auto & p : expected)
  290. checkStructField(actual, p.first, p.second);
  291. for(const auto & p : actual)
  292. checkExcessStructField(p.second, p.first, expected);
  293. }
  294. void JsonMapComparer::checkEqualJson(const JsonVector & actual, const JsonVector & expected)
  295. {
  296. check(actual.size() == expected.size(), "size mismatch");
  297. size_t sz = std::min(actual.size(), expected.size());
  298. for(size_t idx = 0; idx < sz; idx ++)
  299. {
  300. auto guard = pushName(boost::to_string(idx));
  301. checkEqualJson(actual.at(idx), expected.at(idx));
  302. }
  303. }
  304. void JsonMapComparer::checkEqualJson(const JsonNode & actual, const JsonNode & expected)
  305. {
  306. //name has been pushed before
  307. const bool validType = actual.getType() == expected.getType();
  308. if(!validType)
  309. addError("type mismatch");
  310. //do detail checks avoiding assertions in JsonNode
  311. if(validType)
  312. {
  313. switch (actual.getType())
  314. {
  315. case JsonNode::DATA_NULL:
  316. break; //do nothing
  317. case JsonNode::DATA_BOOL:
  318. check(actual.Bool() == expected.Bool(), "mismatch");
  319. break;
  320. case JsonNode::DATA_FLOAT:
  321. checkEqualFloat(actual.Float(),expected.Float());
  322. break;
  323. case JsonNode::DATA_STRING:
  324. checkEqualString(actual.String(),expected.String());
  325. break;
  326. case JsonNode::DATA_VECTOR:
  327. checkEqualJson(actual.Vector(), expected.Vector());
  328. break;
  329. case JsonNode::DATA_STRUCT:
  330. checkEqualJson(actual.Struct(), expected.Struct());
  331. break;
  332. case JsonNode::DATA_INTEGER:
  333. checkEqualInteger(actual.Integer(), expected.Integer());
  334. break;
  335. default:
  336. FAIL() << "Unknown Json type";
  337. break;
  338. }
  339. }
  340. }
  341. void JsonMapComparer::checkExcessStructField(const JsonNode & actualValue, const std::string & name, const JsonMap & expected)
  342. {
  343. auto guard = pushName(name);
  344. if(!vstd::contains(expected, name))
  345. {
  346. if(!isEmpty(actualValue))
  347. addError("excess");
  348. }
  349. }
  350. void JsonMapComparer::checkStructField(const JsonMap & actual, const std::string & name, const JsonNode & expectedValue)
  351. {
  352. auto guard = pushName(name);
  353. if(!vstd::contains(actual, name))
  354. {
  355. if(!isEmpty(expectedValue))
  356. addError("missing");
  357. }
  358. else
  359. checkEqualJson(actual.at(name), expectedValue);
  360. }
  361. void JsonMapComparer::compare(const std::string & name, const JsonNode & actual, const JsonNode & expected)
  362. {
  363. auto guard = pushName(name);
  364. checkEqualJson(actual, expected);
  365. }
  366. void JsonMapComparer::compareHeader(const JsonNode & actual, const JsonNode & expected)
  367. {
  368. compare("hdr", actual, expected);
  369. }
  370. void JsonMapComparer::compareObjects(const JsonNode & actual, const JsonNode & expected)
  371. {
  372. compare("obj", actual, expected);
  373. }