MapComparer.cpp 13 KB

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