1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

-Added test subfolder and updated CMakeLists for unit testing - Added a test case for the DrawTerrainOperation class(does not pass all tests successfully, 6 failures left to fix) - Fixed a few bugs

This commit is contained in:
beegee1 2013-04-22 14:49:28 +00:00
parent a9cfb4bddb
commit dd78205ce8
15 changed files with 554 additions and 72 deletions

View File

@ -16,6 +16,7 @@ set(VCMI_VERSION_PATCH 0)
option(DISABLE_ERM "Disable compilation of ERM scripting module" ON) option(DISABLE_ERM "Disable compilation of ERM scripting module" ON)
option(ENABLE_EDITOR "Enable compilation of map editor" OFF) option(ENABLE_EDITOR "Enable compilation of map editor" OFF)
option(ENABLE_TEST "Enable compilation of unit tests" OFF)
############################################ ############################################
# Building section # # Building section #
@ -56,6 +57,10 @@ if (ENABLE_EDITOR)
find_package(Qt5Widgets REQUIRED) find_package(Qt5Widgets REQUIRED)
endif() endif()
if(ENABLE_TEST)
find_package(Boost 1.46.0 COMPONENTS unit_test_framework REQUIRED)
endif()
if(NOT WIN32) if(NOT WIN32)
set(FFmpeg_FIND_COMPONENTS AVFORMAT SWSCALE) set(FFmpeg_FIND_COMPONENTS AVFORMAT SWSCALE)
find_package(FFmpeg REQUIRED) find_package(FFmpeg REQUIRED)
@ -125,6 +130,9 @@ endif()
if (ENABLE_EDITOR) if (ENABLE_EDITOR)
add_subdirectory(editor) add_subdirectory(editor)
endif() endif()
if(ENABLE_TEST)
add_subdirectory(test)
endif()
####################################### #######################################
# Installation section # # Installation section #

View File

@ -45,6 +45,7 @@
[ [
// Standard transitions // Standard transitions
{ {
"id" : "s1",
"data" : "data" :
[ [
"?", "?", "T", "?", "?", "T",
@ -54,6 +55,7 @@
"mapping" : "0-3, 20-23" "mapping" : "0-3, 20-23"
}, },
{ {
"id" : "s2",
"data" : "data" :
[ [
"?", "N", "N", "?", "N", "N",
@ -63,6 +65,7 @@
"mapping" : "4-7, 24-27" "mapping" : "4-7, 24-27"
}, },
{ {
"id" : "s3",
"data" : "data" :
[ [
"?", "T", "?", "?", "T", "?",
@ -72,6 +75,7 @@
"mapping" : "8-11, 28-31" "mapping" : "8-11, 28-31"
}, },
{ {
"id" : "s4",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
@ -81,28 +85,30 @@
"mapping" : "12-15, 32-35" "mapping" : "12-15, 32-35"
}, },
{ {
"id" : "s5",
"data" : "data" :
[ [
"T", "T", "a-1,?", "T", "T", "s5-1,?",
"T", "N", "N", "T", "N", "N",
"a-1,?", "N", "N" "s5-1,?", "N", "N"
], ],
"mapping" : "16-17, 36-37", "mapping" : "16-17, 36-37",
"id" : "a",
"minPoints" : 1 "minPoints" : 1
}, },
{ {
"id" : "s6",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
"N", "N", "a-1,N", "N", "N", "s5-1,N",
"N", "a-1,N", "T" "N", "s5-1,N", "T"
], ],
"mapping" : "18-19, 38-39", "mapping" : "18-19, 38-39",
"minPoints" : 1 "minPoints" : 1
}, },
// Mixed transitions // Mixed transitions
{ {
"id" : "m1",
"data" : "data" :
[ [
"T", "N", "N", "T", "N", "N",
@ -112,6 +118,7 @@
"mapping" : "40, 42" "mapping" : "40, 42"
}, },
{ {
"id" : "m2",
"data" : "data" :
[ [
"D", "N", "N", "D", "N", "N",
@ -121,6 +128,7 @@
"mapping" : "41" "mapping" : "41"
}, },
{ {
"id" : "m3",
"data" : "data" :
[ [
"N", "N", "D,N", "N", "N", "D,N",
@ -130,6 +138,7 @@
"mapping" : "43" "mapping" : "43"
}, },
{ {
"id" : "m4",
"data" : "data" :
[ [
"N", "N", "S", "N", "N", "S",
@ -139,6 +148,7 @@
"mapping" : "44" "mapping" : "44"
}, },
{ {
"id" : "m5",
"data" : "data" :
[ [
"N", "N", "D,N", "N", "N", "D,N",
@ -148,6 +158,7 @@
"mapping" : "45" "mapping" : "45"
}, },
{ {
"id" : "m6",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
@ -157,6 +168,7 @@
"mapping" : "46" "mapping" : "46"
}, },
{ {
"id" : "m7",
"data" : "data" :
[ [
"N", "N", "D,S,N", "N", "N", "D,S,N",
@ -166,6 +178,7 @@
"mapping" : "47" "mapping" : "47"
}, },
{ {
"id" : "m8",
"data" : "data" :
[ [
"N", "N", "D", "N", "N", "D",
@ -176,6 +189,7 @@
}, },
// No transition // No transition
{ {
"id" : "n1",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
@ -189,6 +203,7 @@
[ [
// Standard transitions // Standard transitions
{ {
"id" : "s1",
"data" : "data" :
[ [
"?", "S", "S", "?", "S", "S",
@ -198,6 +213,7 @@
"mapping" : "0-3" "mapping" : "0-3"
}, },
{ {
"id" : "s2",
"data" : "data" :
[ [
"?", "D", "D", "?", "D", "D",
@ -207,6 +223,7 @@
"mapping" : "4-7" "mapping" : "4-7"
}, },
{ {
"id" : "s3",
"data" : "data" :
[ [
"?", "S", "?", "?", "S", "?",
@ -216,6 +233,7 @@
"mapping" : "8-11" "mapping" : "8-11"
}, },
{ {
"id" : "s4",
"data" : "data" :
[ [
"D", "D", "D", "D", "D", "D",
@ -225,29 +243,30 @@
"mapping" : "12-15" "mapping" : "12-15"
}, },
{ {
"id" : "s5",
"data" : "data" :
[ [
"S", "S", "D", "S", "S", "D",
"S", "N", "b-1,D", "S", "N", "s6-1,D",
"D", "b-1,D", "D" "D", "s6-1,D", "D"
], ],
"mapping" : "16-17", "mapping" : "16-17",
"id" : "a",
"minPoints" : 1 "minPoints" : 1
}, },
{ {
"id" : "s6",
"data" : "data" :
[ [
"D", "D", "D", "D", "D", "D",
"D", "N", "a-1,D", "D", "N", "s5-1,D",
"D", "a-1,D", "S" "D", "s5-1,D", "S"
], ],
"mapping" : "18-19", "mapping" : "18-19",
"id" : "b",
"minPoints" : 1 "minPoints" : 1
}, },
// Mixed transition // Mixed transition
{ {
"id" : "m1",
"data" : "data" :
[ [
"S", "D", "D", "S", "D", "D",
@ -258,6 +277,7 @@
}, },
// No transition // No transition
{ {
"id" : "n1",
"data" : "data" :
[ [
"D", "D", "D", "D", "D", "D",
@ -270,6 +290,7 @@
"sand" : "sand" :
[ [
{ {
"id" : "n1",
"data" : "data" :
[ [
"?", "?", "?", "?", "?", "?",
@ -283,6 +304,7 @@
[ [
// Standard transitions // Standard transitions
{ {
"id" : "s1",
"data" : "data" :
[ [
"S", "S", "S", "S", "S", "S",
@ -292,6 +314,7 @@
"mapping" : "0-3" "mapping" : "0-3"
}, },
{ {
"id" : "s2",
"data" : "data" :
[ [
"?", "N", "N", "?", "N", "N",
@ -301,6 +324,7 @@
"mapping" : "4-7" "mapping" : "4-7"
}, },
{ {
"id" : "s3",
"data" : "data" :
[ [
"?", "S", "?", "?", "S", "?",
@ -310,6 +334,7 @@
"mapping" : "8-11" "mapping" : "8-11"
}, },
{ {
"id" : "s4",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
@ -319,6 +344,7 @@
"mapping" : "12-15" "mapping" : "12-15"
}, },
{ {
"id" : "s5",
"data" : "data" :
[ [
"S", "S", "N", "S", "S", "N",
@ -326,20 +352,21 @@
"N", "N", "N" "N", "N", "N"
], ],
"mapping" : "16-17", "mapping" : "16-17",
"id" : "a"
}, },
{ {
"id" : "s6",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
"N", "N", "a-1,N", "N", "N", "s5-1,N",
"N", "a-1,N", "S" "N", "s5-1,N", "S"
], ],
"mapping" : "18-19", "mapping" : "18-19",
"minPoints" : 1 "minPoints" : 1
}, },
// No transition // No transition
{ {
"id" : "n1",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
@ -353,6 +380,7 @@
[ [
// No transition // No transition
{ {
"id" : "n1",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
@ -363,6 +391,7 @@
}, },
// Standard transitions // Standard transitions
{ {
"id" : "s1",
"data" : "data" :
[ [
"?", "S", "?", "?", "S", "?",
@ -373,6 +402,7 @@
"flipMode" : "diffImages" "flipMode" : "diffImages"
}, },
{ {
"id" : "s2",
"data" : "data" :
[ [
"?", "N", "N", "?", "N", "N",
@ -383,6 +413,7 @@
"flipMode" : "diffImages" "flipMode" : "diffImages"
}, },
{ {
"id" : "s3",
"data" : "data" :
[ [
"?", "S", "?", "?", "S", "?",
@ -393,6 +424,7 @@
"flipMode" : "diffImages" "flipMode" : "diffImages"
}, },
{ {
"id" : "s4",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
@ -403,6 +435,7 @@
"flipMode" : "diffImages" "flipMode" : "diffImages"
}, },
{ {
"id" : "s5",
"data" : "data" :
[ [
"S", "S", "N", "S", "S", "N",
@ -410,15 +443,15 @@
"N", "N", "N" "N", "N", "N"
], ],
"mapping" : "32-39", "mapping" : "32-39",
"flipMode" : "diffImages", "flipMode" : "diffImages"
"id" : "a"
}, },
{ {
"id" : "s6",
"data" : "data" :
[ [
"N", "N", "N", "N", "N", "N",
"N", "N", "a-1,N", "N", "N", "s5-1,N",
"N", "a-1,N", "S" "N", "s5-1,N", "S"
], ],
"mapping" : "40-47", "mapping" : "40-47",
"flipMode" : "diffImages", "flipMode" : "diffImages",

View File

@ -258,7 +258,7 @@ public:
const CColorMapping & getColorMapping() const; const CColorMapping & getColorMapping() const;
void setColorMapping(const CColorMapping & colorMapping); void setColorMapping(const CColorMapping & colorMapping);
void write(const LogRecord & record); void write(const LogRecord & record) override;
private: private:
CConsoleHandler * console; CConsoleHandler * console;
@ -281,7 +281,7 @@ public:
const CLogFormatter & getFormatter() const; const CLogFormatter & getFormatter() const;
void setFormatter(const CLogFormatter & formatter); void setFormatter(const CLogFormatter & formatter);
void write(const LogRecord & record); void write(const LogRecord & record) override;
private: private:
std::ofstream file; std::ofstream file;

View File

@ -5,6 +5,77 @@
#include "../filesystem/CResourceLoader.h" #include "../filesystem/CResourceLoader.h"
#include "../CDefObjInfoHandler.h" #include "../CDefObjInfoHandler.h"
MapRect::MapRect() : x(0), y(0), z(0), width(0), height(0)
{
}
MapRect::MapRect(int3 pos, si32 width, si32 height) : x(pos.x), y(pos.y), z(pos.z), width(width), height(height)
{
}
MapRect MapRect::operator&(const MapRect & rect) const
{
bool intersect = right() > rect.left() && rect.right() > left() &&
bottom() > rect.top() && rect.bottom() > top() &&
z == rect.z;
if(intersect)
{
MapRect ret;
ret.x = std::max(left(), rect.left());
ret.y = std::max(top(), rect.top());
ret.z = rect.z;
ret.width = std::min(right(), rect.right()) - ret.x;
ret.height = std::min(bottom(), rect.bottom()) - ret.y;
return ret;
}
else
{
return MapRect();
}
}
si32 MapRect::left() const
{
return x;
}
si32 MapRect::right() const
{
return x + width;
}
si32 MapRect::top() const
{
return y;
}
si32 MapRect::bottom() const
{
return y + height;
}
int3 MapRect::topLeft() const
{
return int3(x, y, z);
}
int3 MapRect::topRight() const
{
return int3(right(), y, z);
}
int3 MapRect::bottomLeft() const
{
return int3(x, bottom(), z);
}
int3 MapRect::bottomRight() const
{
return int3(right(), bottom(), z);
}
CMapOperation::CMapOperation(CMap * map) : map(map) CMapOperation::CMapOperation(CMap * map) : map(map)
{ {
@ -181,12 +252,11 @@ CTerrainViewPatternConfig & CTerrainViewPatternConfig::get()
CTerrainViewPatternConfig::CTerrainViewPatternConfig() CTerrainViewPatternConfig::CTerrainViewPatternConfig()
{ {
const JsonNode config(ResourceID("config/terrainViewPatterns.json")); const JsonNode config(ResourceID("config/terrainViewPatterns.json"));
const std::map<std::string, ETerrainGroup::ETerrainGroup> terGroups const auto & groupMap = config.Struct();
= boost::assign::map_list_of("normal", ETerrainGroup::NORMAL)("dirt", ETerrainGroup::DIRT) BOOST_FOREACH(const auto & groupPair, groupMap)
("sand", ETerrainGroup::SAND)("water", ETerrainGroup::WATER)("rock", ETerrainGroup::ROCK);
BOOST_FOREACH(auto terMapping, terGroups)
{ {
BOOST_FOREACH(const JsonNode & ptrnNode, config[terMapping.first].Vector()) auto terGroup = getTerrainGroup(groupPair.first);
BOOST_FOREACH(const JsonNode & ptrnNode, groupPair.second.Vector())
{ {
TerrainViewPattern pattern; TerrainViewPattern pattern;
@ -238,8 +308,8 @@ CTerrainViewPatternConfig::CTerrainViewPatternConfig()
pattern.flipMode = TerrainViewPattern::FLIP_MODE_SAME_IMAGE; pattern.flipMode = TerrainViewPattern::FLIP_MODE_SAME_IMAGE;
} }
pattern.terGroup = terMapping.second; pattern.terGroup = terGroup;
patterns[terMapping.second].push_back(pattern); patterns[terGroup].push_back(pattern);
} }
} }
} }
@ -249,6 +319,17 @@ CTerrainViewPatternConfig::~CTerrainViewPatternConfig()
} }
ETerrainGroup::ETerrainGroup CTerrainViewPatternConfig::getTerrainGroup(const std::string & terGroup) const
{
static const std::map<std::string, ETerrainGroup::ETerrainGroup> terGroups
= boost::assign::map_list_of("normal", ETerrainGroup::NORMAL)("dirt", ETerrainGroup::DIRT)
("sand", ETerrainGroup::SAND)("water", ETerrainGroup::WATER)("rock", ETerrainGroup::ROCK);
auto it = terGroups.find(terGroup);
if(it == terGroups.end()) throw std::runtime_error(boost::str(boost::format("Terrain group '%s' does not exist.") % terGroup));
return it->second;
}
const std::vector<TerrainViewPattern> & CTerrainViewPatternConfig::getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const const std::vector<TerrainViewPattern> & CTerrainViewPatternConfig::getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const
{ {
return patterns.find(terGroup)->second; return patterns.find(terGroup)->second;
@ -275,18 +356,19 @@ DrawTerrainOperation::DrawTerrainOperation(CMap * map, const MapRect & rect, ETe
void DrawTerrainOperation::execute() void DrawTerrainOperation::execute()
{ {
for(int i = rect.pos.x; i < rect.pos.x + rect.width; ++i) for(int i = rect.x; i < rect.x + rect.width; ++i)
{ {
for(int j = rect.pos.y; j < rect.pos.y + rect.height; ++j) for(int j = rect.y; j < rect.y + rect.height; ++j)
{ {
map->getTile(int3(i, j, rect.pos.z)).terType = terType; map->getTile(int3(i, j, rect.z)).terType = terType;
} }
} }
//TODO there are situations where more tiles are affected implicitely //TODO there are situations where more tiles are affected implicitely
//TODO add coastal bit to extTileFlags appropriately //TODO add coastal bit to extTileFlags appropriately
updateTerrainViews(MapRect(int3(rect.pos.x - 1, rect.pos.y - 1, rect.pos.z), rect.width + 2, rect.height + 2)); MapRect viewRect(int3(rect.x - 1, rect.y - 1, rect.z), rect.width + 2, rect.height + 2); // Has to overlap 1 tile around
updateTerrainViews(viewRect & MapRect(int3(0, 0, viewRect.z), map->width, map->height)); // Rect should not overlap map dimensions
} }
void DrawTerrainOperation::undo() void DrawTerrainOperation::undo()
@ -306,12 +388,12 @@ std::string DrawTerrainOperation::getLabel() const
void DrawTerrainOperation::updateTerrainViews(const MapRect & rect) void DrawTerrainOperation::updateTerrainViews(const MapRect & rect)
{ {
for(int i = rect.pos.x; i < rect.pos.x + rect.width; ++i) for(int i = rect.x; i < rect.x + rect.width; ++i)
{ {
for(int j = rect.pos.y; j < rect.pos.y + rect.height; ++j) for(int j = rect.y; j < rect.y + rect.height; ++j)
{ {
const auto & patterns = const auto & patterns =
CTerrainViewPatternConfig::get().getPatternsForGroup(getTerrainGroup(map->getTile(int3(i, j, rect.pos.z)).terType)); CTerrainViewPatternConfig::get().getPatternsForGroup(getTerrainGroup(map->getTile(int3(i, j, rect.z)).terType));
// Detect a pattern which fits best // Detect a pattern which fits best
int bestPattern = -1, bestFlip = -1; int bestPattern = -1, bestFlip = -1;
@ -322,10 +404,10 @@ void DrawTerrainOperation::updateTerrainViews(const MapRect & rect)
for(int flip = 0; flip < 4; ++flip) for(int flip = 0; flip < 4; ++flip)
{ {
auto valRslt = validateTerrainView(int3(i, j, rect.pos.z), flip > 0 ? getFlippedPattern(pattern, flip) : pattern); auto valRslt = validateTerrainView(int3(i, j, rect.z), flip > 0 ? getFlippedPattern(pattern, flip) : pattern);
if(valRslt.result) if(valRslt.result)
{ {
logGlobal->debugStream() << "Pattern detected at pos " << i << "x" << j << "x" << rect.pos.z << ": P-Nr. " << k logGlobal->debugStream() << "Pattern detected at pos " << i << "x" << j << "x" << rect.z << ": P-Nr. " << k
<< ", Flip " << flip << ", Repl. " << valRslt.transitionReplacement; << ", Flip " << flip << ", Repl. " << valRslt.transitionReplacement;
bestPattern = k; bestPattern = k;
@ -338,7 +420,7 @@ void DrawTerrainOperation::updateTerrainViews(const MapRect & rect)
if(bestPattern == -1) if(bestPattern == -1)
{ {
// This shouldn't be the case // This shouldn't be the case
logGlobal->warnStream() << "No pattern detected at pos " << i << "x" << j << "x" << rect.pos.z; logGlobal->warnStream() << "No pattern detected at pos " << i << "x" << j << "x" << rect.z;
continue; continue;
} }
@ -355,7 +437,7 @@ void DrawTerrainOperation::updateTerrainViews(const MapRect & rect)
} }
// Set terrain view // Set terrain view
auto & tile = map->getTile(int3(i, j, rect.pos.z)); auto & tile = map->getTile(int3(i, j, rect.z));
if(pattern.flipMode == TerrainViewPattern::FLIP_MODE_SAME_IMAGE) if(pattern.flipMode == TerrainViewPattern::FLIP_MODE_SAME_IMAGE)
{ {
tile.terView = gen->getInteger(mapping.first, mapping.second); tile.terView = gen->getInteger(mapping.first, mapping.second);
@ -363,9 +445,9 @@ void DrawTerrainOperation::updateTerrainViews(const MapRect & rect)
} }
else else
{ {
int range = (mapping.second - mapping.first) / 4; const int framesPerRot = 2;
tile.terView = gen->getInteger(mapping.first + bestFlip * range, int firstFrame = mapping.first + bestFlip * framesPerRot;
mapping.first + (bestFlip + 1) * range - 1); tile.terView = gen->getInteger(firstFrame, firstFrame + framesPerRot - 1);
tile.extTileFlags = 0; tile.extTileFlags = 0;
} }
} }

View File

@ -18,28 +18,30 @@ class CGObjectInstance;
class CTerrainViewPatternConfig; class CTerrainViewPatternConfig;
struct TerrainViewPattern; struct TerrainViewPattern;
namespace ETerrainGroup
{
enum ETerrainGroup
{
NORMAL,
DIRT,
SAND,
WATER,
ROCK
};
}
/// Represents a map rectangle. /// Represents a map rectangle.
struct DLL_LINKAGE MapRect struct DLL_LINKAGE MapRect
{ {
MapRect(int3 pos, si32 width, si32 height) : pos(pos), width(width), height(height) { }; MapRect();
int3 pos; MapRect(int3 pos, si32 width, si32 height);
si32 x, y, z;
si32 width, height; si32 width, height;
si32 left() const;
si32 right() const;
si32 top() const;
si32 bottom() const;
int3 topLeft() const; /// Top left corner of this rect.
int3 topRight() const; /// Top right corner of this rect.
int3 bottomLeft() const; /// Bottom left corner of this rect.
int3 bottomRight() const; /// Bottom right corner of this rect.
/// Returns a MapRect of the intersection of this rectangle and the given one.
MapRect operator&(const MapRect & rect) const;
}; };
/// The abstract base class CMapOperation defines an operation that can be executed, undone and redone. /// The abstract base class CMapOperation defines an operation that can be executed, undone and redone.
class CMapOperation class DLL_LINKAGE CMapOperation : public boost::noncopyable
{ {
public: public:
CMapOperation(CMap * map); CMapOperation(CMap * map);
@ -55,7 +57,7 @@ protected:
}; };
/// The CMapUndoManager provides the functionality to save operations and undo/redo them. /// The CMapUndoManager provides the functionality to save operations and undo/redo them.
class CMapUndoManager : boost::noncopyable class DLL_LINKAGE CMapUndoManager : boost::noncopyable
{ {
public: public:
CMapUndoManager(); CMapUndoManager();
@ -77,8 +79,8 @@ public:
private: private:
typedef std::list<unique_ptr<CMapOperation> > TStack; typedef std::list<unique_ptr<CMapOperation> > TStack;
inline void doOperation(TStack & fromStack, TStack & toStack, bool doUndo); void doOperation(TStack & fromStack, TStack & toStack, bool doUndo);
inline const CMapOperation * peek(const TStack & stack) const; const CMapOperation * peek(const TStack & stack) const;
TStack undoStack; TStack undoStack;
TStack redoStack; TStack redoStack;
@ -113,9 +115,21 @@ private:
/* Implementation/Detail classes, Private API */ /* Implementation/Detail classes, Private API */
/* ---------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------- */
namespace ETerrainGroup
{
enum ETerrainGroup
{
NORMAL,
DIRT,
SAND,
WATER,
ROCK
};
}
/// The terrain view pattern describes a specific composition of terrain tiles /// The terrain view pattern describes a specific composition of terrain tiles
/// in a 3x3 matrix and notes which terrain view frame numbers can be used. /// in a 3x3 matrix and notes which terrain view frame numbers can be used.
struct TerrainViewPattern struct DLL_LINKAGE TerrainViewPattern
{ {
struct WeightedRule struct WeightedRule
{ {
@ -186,13 +200,14 @@ struct TerrainViewPattern
}; };
/// The terrain view pattern config loads pattern data from the filesystem. /// The terrain view pattern config loads pattern data from the filesystem.
class CTerrainViewPatternConfig : public boost::noncopyable class DLL_LINKAGE CTerrainViewPatternConfig : public boost::noncopyable
{ {
public: public:
static CTerrainViewPatternConfig & get(); static CTerrainViewPatternConfig & get();
const std::vector<TerrainViewPattern> & getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const; const std::vector<TerrainViewPattern> & getPatternsForGroup(ETerrainGroup::ETerrainGroup terGroup) const;
const TerrainViewPattern & getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const; const TerrainViewPattern & getPatternById(ETerrainGroup::ETerrainGroup terGroup, const std::string & id) const;
ETerrainGroup::ETerrainGroup getTerrainGroup(const std::string & terGroup) const;
private: private:
CTerrainViewPatternConfig(); CTerrainViewPatternConfig();
@ -208,10 +223,10 @@ class DrawTerrainOperation : public CMapOperation
public: public:
DrawTerrainOperation(CMap * map, const MapRect & rect, ETerrainType terType, CRandomGenerator * gen); DrawTerrainOperation(CMap * map, const MapRect & rect, ETerrainType terType, CRandomGenerator * gen);
void execute(); void execute() override;
void undo(); void undo() override;
void redo(); void redo() override;
std::string getLabel() const; std::string getLabel() const override;
private: private:
struct ValidationResult struct ValidationResult
@ -246,10 +261,10 @@ class InsertObjectOperation : public CMapOperation
public: public:
InsertObjectOperation(CMap * map, const int3 & pos, CGObjectInstance * obj); InsertObjectOperation(CMap * map, const int3 & pos, CGObjectInstance * obj);
void execute(); void execute() override;
void undo(); void undo() override;
void redo(); void redo() override;
std::string getLabel() const; std::string getLabel() const override;
private: private:
int3 pos; int3 pos;

View File

@ -357,7 +357,7 @@ void CMapGenOptions::CPlayerSettings::setPlayerType(EPlayerType::EPlayerType val
playerType = value; playerType = value;
} }
CMapGenerator::CMapGenerator(const CMapGenOptions & mapGenOptions, int randomSeed) : CMapGenerator::CMapGenerator(const CMapGenOptions & mapGenOptions, int randomSeed /*= std::time(nullptr)*/) :
mapGenOptions(mapGenOptions), randomSeed(randomSeed) mapGenOptions(mapGenOptions), randomSeed(randomSeed)
{ {
gen.seed(randomSeed); gen.seed(randomSeed);
@ -376,6 +376,7 @@ std::unique_ptr<CMap> CMapGenerator::generate()
map = make_unique<CMap>(); map = make_unique<CMap>();
editManager = map->getEditManager(); editManager = map->getEditManager();
editManager->getUndoManager().setUndoRedoLimit(0);
addHeaderInfo(); addHeaderInfo();
genTerrain(); genTerrain();
@ -472,7 +473,7 @@ void CMapGenerator::addPlayerInfo()
void CMapGenerator::genTerrain() void CMapGenerator::genTerrain()
{ {
map->initTerrain(); //FIXME nicer solution map->initTerrain();
editManager->clearTerrain(&gen); editManager->clearTerrain(&gen);
editManager->drawTerrain(MapRect(int3(10, 10, 0), 20, 30), ETerrainType::GRASS, &gen); editManager->drawTerrain(MapRect(int3(10, 10, 0), 20, 30), ETerrainType::GRASS, &gen);
} }

View File

@ -169,7 +169,7 @@ public:
class DLL_LINKAGE CMapGenerator class DLL_LINKAGE CMapGenerator
{ {
public: public:
CMapGenerator(const CMapGenOptions & mapGenOptions, int randomSeed); explicit CMapGenerator(const CMapGenOptions & mapGenOptions, int randomSeed = std::time(nullptr));
~CMapGenerator(); // required due to unique_ptr ~CMapGenerator(); // required due to unique_ptr
std::unique_ptr<CMap> generate(); std::unique_ptr<CMap> generate();

27
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 2.6)
enable_testing()
include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_HOME_DIRECTORY}/test)
include_directories(${Boost_INCLUDE_DIRS})
set(test_SRCS
StdInc.cpp
CVcmiTestConfig.cpp
CMapEditManagerTest.cpp
)
add_executable(vcmitest ${test_SRCS})
target_link_libraries(vcmitest vcmi ${Boost_LIBRARIES})
add_test(vcmitest vcmitest)
# Files to copy to the build directory after compilation
set(vcmitest_FILES
TerrainViewTest.h3m
terrainViewMappings.json
)
foreach(file ${vcmitest_FILES})
add_custom_command(TARGET vcmitest POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${file}" $<TARGET_FILE_DIR:vcmitest>
)
endforeach()

View File

@ -0,0 +1,83 @@
/*
* CMapEditManagerTest.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include <boost/test/unit_test.hpp>
#include "../lib/mapping/CMapService.h"
#include "../lib/mapping/CMap.h"
#include "../lib/filesystem/CResourceLoader.h"
#include "../lib/filesystem/CFilesystemLoader.h"
#include "../lib/JsonNode.h"
#include "../lib/mapping/CMapEditManager.h"
#include "../lib/int3.h"
#include "../lib/CRandomGenerator.h"
BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain)
{
try
{
// Load maps and json config
auto loader = make_shared<CFilesystemLoader>(".");
CResourceHandler::get()->addLoader("test/", loader, false);
const auto originalMap = CMapService::loadMap("test/TerrainViewTest");
auto map = CMapService::loadMap("test/TerrainViewTest");
logGlobal->infoStream() << "Loaded test map successfully.";
// Validate edit manager
auto editManager = map->getEditManager();
CRandomGenerator gen;
const JsonNode viewNode(ResourceID("test/terrainViewMappings", EResType::TEXT));
const auto & mappingsNode = viewNode["mappings"].Vector();
BOOST_FOREACH(const auto & node, mappingsNode)
{
// Get terrain group and id
const auto & patternStr = node["pattern"].String();
std::vector<std::string> patternParts;
boost::split(patternParts, patternStr, boost::is_any_of("."));
if(patternParts.size() != 2) throw std::runtime_error("A pattern should consist of two parts, the group and the id. Continue with next pattern.");
const auto & groupStr = patternParts[0];
const auto & id = patternParts[1];
auto terGroup = CTerrainViewPatternConfig::get().getTerrainGroup(groupStr);
// Get mapping range
const auto & pattern = CTerrainViewPatternConfig::get().getPatternById(terGroup, id);
const auto & mapping = pattern.mapping;
const auto & positionsNode = node["pos"].Vector();
BOOST_FOREACH(const auto & posNode, positionsNode)
{
const auto & posVector = posNode.Vector();
if(posVector.size() != 3) throw std::runtime_error("A position should consist of three values x,y,z. Continue with next position.");
int3 pos(posVector[0].Float(), posVector[1].Float(), posVector[2].Float());
logGlobal->infoStream() << boost::format("Test pattern '%s' on position x '%d', y '%d', z '%d'.") % patternStr % pos.x % pos.y % pos.z;
const auto & originalTile = originalMap->getTile(pos);
editManager->drawTerrain(MapRect(pos, 1, 1), originalTile.terType, &gen);
const auto & tile = map->getTile(pos);
bool isInRange = false;
BOOST_FOREACH(const auto & range, mapping)
{
if(tile.terView >= range.first && tile.terView <= range.second)
{
isInRange = true;
break;
}
}
BOOST_CHECK(isInRange);
if(!isInRange) logGlobal->errorStream() << "No or invalid pattern found for current position.";
}
}
}
catch(const std::exception & e)
{
logGlobal-> errorStream() << e.what();
}
}

37
test/CVcmiTestConfig.cpp Normal file
View File

@ -0,0 +1,37 @@
/*
* CVcmiTestConfig.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CVcmiTestConfig.h"
#include "../lib/CConsoleHandler.h"
#include "../lib/logging/CBasicLogConfigurator.h"
#include "../lib/VCMIDirs.h"
#include "../lib/VCMI_Lib.h"
#include "../lib/logging/CLogger.h"
#include "../lib/CConfigHandler.h"
CVcmiTestConfig::CVcmiTestConfig()
{
console = new CConsoleHandler;
CBasicLogConfigurator logConfig(VCMIDirs::get().localPath() + "/VCMI_Test_log.txt", console);
logConfig.configureDefault();
preinitDLL(console);
settings.init();
logConfig.configure();
loadDLLClasses();
logGlobal->infoStream() << "Initialized global test setup.";
}
CVcmiTestConfig::~CVcmiTestConfig()
{
std::cout << "Ending global test tear-down." << std::endl;
}

20
test/CVcmiTestConfig.h Normal file
View File

@ -0,0 +1,20 @@
/*
* CVcmiTestConfig.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
/// Global setup/tear down class for unit tests.
class CVcmiTestConfig
{
public:
CVcmiTestConfig();
~CVcmiTestConfig();
};

7
test/StdInc.cpp Normal file
View File

@ -0,0 +1,7 @@
// Creates the precompiled header
#include "StdInc.h"
#define BOOST_TEST_MODULE VcmiTest
#include <boost/test/unit_test.hpp>
#include "CVcmiTestConfig.h"
BOOST_GLOBAL_FIXTURE(CVcmiTestConfig);

8
test/StdInc.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "../Global.h"
#define BOOST_TEST_DYN_LINK

BIN
test/TerrainViewTest.h3m Normal file

Binary file not shown.

View File

@ -0,0 +1,161 @@
{
"mappings" : [
// Normal type
{
"pos" : [ [ 14,13,0 ], [ 4,34,0 ] ],
"pattern" : "normal.s1"
},
{
"pos" : [ [ 14,15,0 ], [ 29,19,0 ] ],
"pattern" : "normal.s2"
},
{
"pos" : [ [ 16,13,0 ], [ 23,30,0 ] ],
"pattern" : "normal.s3"
},
{
"pos" : [ [ 16,14,0 ], [ 29,20,0 ] ],
"pattern" : "normal.s4"
},
{
"pos" : [ [ 5,14,0 ], [ 31,13,0 ] ],
"pattern" : "normal.s5"
},
{
"pos" : [ [ 9,17,0 ], [ 3,17,0 ] ],
"pattern" : "normal.s6"
},
{
"pos" : [ [ 21,20,1 ], [ 28,27,1 ] ],
"pattern" : "normal.m1"
},
{
"pos" : [ [ 22,22,1 ] ],
"pattern" : "normal.m2"
},
{
"pos" : [ [ 12,31,0 ] ],
"pattern" : "normal.m3"
},
{
"pos" : [ [ 5,34,0 ] ],
"pattern" : "normal.m4"
},
{
"pos" : [ [ 12,34,0 ] ],
"pattern" : "normal.m5"
},
{
"pos" : [ [ 8,25,0 ] ],
"pattern" : "normal.m6"
},
{
"pos" : [ [ 5,32,0 ] ],
"pattern" : "normal.m7"
},
{
"pos" : [ [ 28,23,1 ] ],
"pattern" : "normal.m8"
},
{
"pos" : [ [ 7,23,0 ] ],
"pattern" : "normal.n1"
},
// Dirt type
{
"pos" : [ [ 22,26,0 ], [ 27,35,0 ] ],
"pattern" : "dirt.s1"
},
{
"pos" : [ [ 15,33,0 ], [ 19,34,0 ] ],
"pattern" : "dirt.s2"
},
{
"pos" : [ [ 24,32,0 ], [ 25,26,0 ] ],
"pattern" : "dirt.s3"
},
{
"pos" : [ [ 19,32,0 ] ],
"pattern" : "dirt.s4"
},
{
"pos" : [ [ 26,28,1 ] ],
"pattern" : "dirt.s5"
},
{
"pos" : [ [ 25,28,1 ] ],
"pattern" : "dirt.s6"
},
{
"pos" : [ [ 9,25,1 ] ],
"pattern" : "dirt.m1"
},
{
"pos" : [ [ 20,27,1 ] ],
"pattern" : "dirt.n1"
},
// Sand type
{
"pos" : [ [ 22,27,1 ] ],
"pattern" : "sand.n1"
},
// Water type
{
"pos" : [ [ 16,17,0 ] ],
"pattern" : "water.s1"
},
{
"pos" : [ [ 20,15,0 ] ],
"pattern" : "water.s2"
},
{
"pos" : [ [ 23,18,0 ] ],
"pattern" : "water.s3"
},
{
"pos" : [ [ 20,11,0 ] ],
"pattern" : "water.s4"
},
{
"pos" : [ [ 8,14,0 ] ],
"pattern" : "water.s5"
},
{
"pos" : [ [ 8,13,0 ] ],
"pattern" : "water.s6"
},
{
"pos" : [ [ 9,13,0 ] ],
"pattern" : "water.n1"
},
// Rock type
{
"pos" : [ [ 13,12,1 ] ],
"pattern" : "rock.n1"
},
{
"pos" : [ [ 11,21,1 ] ],
"pattern" : "rock.s1"
},
{
"pos" : [ [ 8,21,1 ] ],
"pattern" : "rock.s2"
},
{
"pos" : [ [ 10,12,1 ] ],
"pattern" : "rock.s3"
},
{
"pos" : [ [ 13,21,1 ] ],
"pattern" : "rock.s4"
},
{
"pos" : [ [ 10,22,1 ] ],
"pattern" : "rock.s5"
},
{
"pos" : [ [ 8,23,1 ] ],
"pattern" : "rock.s6"
},
]
}