mirror of
https://github.com/worldforge/worldforge
synced 2026-08-17 16:26:05 -04:00
This is a mainly automatic reformat of all code so that it uses the same format as Ember. The idea is to make development easier by removing any differences in formatting. In addition to formatting there has been a couple of fixes to smaller code issues.
594 lines
16 KiB
C++
594 lines
16 KiB
C++
#include <cppunit/extensions/TestFactoryRegistry.h>
|
|
#include <cppunit/ui/text/TestRunner.h>
|
|
#include <cppunit/extensions/HelperMacros.h>
|
|
#include <cppunit/BriefTestProgressListener.h>
|
|
#include <cppunit/TestResult.h>
|
|
|
|
#include "components/ogre/terrain/ICompilerTechniqueProvider.h"
|
|
#include "components/ogre/terrain/ITerrainPageBridge.h"
|
|
#include "components/ogre/terrain/TerrainHandler.h"
|
|
#include "components/ogre/terrain/Types.h"
|
|
#include "components/ogre/terrain/TerrainDefPoint.h"
|
|
#include "components/ogre/terrain/TerrainInfo.h"
|
|
#include "components/ogre/terrain/TerrainPageSurfaceCompiler.h"
|
|
#include "components/ogre/ILightning.h"
|
|
|
|
#include "framework/Exception.h"
|
|
#include "framework/TimeFrame.h"
|
|
#include "framework/MainLoopController.h"
|
|
|
|
#include <Eris/Entity.h>
|
|
#include <Eris/EventService.h>
|
|
|
|
#include <Atlas/Message/Element.h>
|
|
|
|
#include <Mercator/Terrain.h>
|
|
|
|
#include <wfmath/timestamp.h>
|
|
#include <wfmath/atlasconv.h>
|
|
|
|
#include <Ogre.h>
|
|
#include <sigc++/signal.h>
|
|
#include <sigc++/trackable.h>
|
|
|
|
#include <condition_variable>
|
|
|
|
using namespace Ember::OgreView;
|
|
using namespace Ember::OgreView::Terrain;
|
|
|
|
namespace Ember {
|
|
|
|
class DummyTerrainTechnique : public TerrainPageSurfaceCompilerTechnique {
|
|
virtual bool prepareMaterial() {
|
|
return true;
|
|
}
|
|
|
|
virtual bool compileMaterial(Ogre::MaterialPtr material, std::set <std::string>& managedTextures) const {
|
|
return true;
|
|
}
|
|
|
|
virtual bool compileCompositeMapMaterial(Ogre::MaterialPtr material, std::set <std::string>& managedTextures) const {
|
|
return true;
|
|
}
|
|
|
|
|
|
virtual std::string getShadowTextureName(const Ogre::MaterialPtr& material) const {
|
|
return "";
|
|
}
|
|
|
|
|
|
virtual bool requiresPregenShadow() const {
|
|
return false;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
class DummyCompilerTechniqueProvider : public ICompilerTechniqueProvider {
|
|
public:
|
|
virtual TerrainPageSurfaceCompilerTechnique*
|
|
createTechnique(const TerrainPageGeometryPtr& geometry, const SurfaceLayerStore& terrainPageSurfaces, const TerrainPageShadow* terrainPageShadow) const {
|
|
return new DummyTerrainTechnique();
|
|
}
|
|
|
|
};
|
|
|
|
class DummyTerrainBridge : public ITerrainPageBridge {
|
|
public:
|
|
|
|
bool pageReady;
|
|
|
|
DummyTerrainBridge() :
|
|
pageReady(false) {
|
|
}
|
|
|
|
/**
|
|
* @brief Updates the Ogre mesh after changes to the underlying heightdata.
|
|
*
|
|
* Call this when the heightdata has changed and you want the Ogre representation to be updated to reflect this.
|
|
*/
|
|
virtual void updateTerrain(TerrainPageGeometry& geometry) {
|
|
}
|
|
|
|
/**
|
|
* @brief Notifies class in the ogre side about the page being ready (after being created or so).
|
|
*/
|
|
virtual void terrainPageReady() {
|
|
pageReady = true;
|
|
}
|
|
|
|
virtual bool isPageShown() const {
|
|
return true;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
class DummyLightning : public ILightning {
|
|
public:
|
|
/**
|
|
* @brief Gets the direction of the main light, in world space.
|
|
* @returns The direction of the main light, in world space.
|
|
*/
|
|
virtual WFMath::Vector<3> getMainLightDirection() const {
|
|
return WFMath::Vector<3>::ZERO();
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the default ambient light colour.
|
|
* @returns The default ambient light colour.
|
|
*/
|
|
virtual Ogre::ColourValue getAmbientLightColour() const {
|
|
return Ogre::ColourValue::Black;
|
|
}
|
|
};
|
|
|
|
class DummyEntity : public Eris::Entity {
|
|
public:
|
|
DummyEntity() :
|
|
Eris::Entity("0", 0) {
|
|
}
|
|
|
|
Eris::TypeService* getTypeService() const {
|
|
return 0;
|
|
}
|
|
|
|
void removeFromMovementPrediction() {
|
|
}
|
|
|
|
void addToMovementPrediction() {
|
|
}
|
|
|
|
Eris::Entity* getEntity(const std::string& id) {
|
|
return 0;
|
|
}
|
|
|
|
void setAttr(const std::string& p, const Atlas::Message::Element& v) {
|
|
Eris::Entity::setAttr(p, v);
|
|
}
|
|
|
|
void setFromMessage(const Atlas::Message::MapType& attrs) {
|
|
beginUpdate();
|
|
|
|
Atlas::Message::MapType::const_iterator A;
|
|
|
|
for (A = attrs.begin(); A != attrs.end(); ++A) {
|
|
if (A->first == "loc" || A->first == "id" || A->first == "contains")
|
|
continue;
|
|
|
|
Eris::Entity::AttrMap::iterator I = m_attrs.find(A->first);
|
|
if ((I != m_attrs.end()) && (I->second == A->second))
|
|
continue;
|
|
|
|
setAttr(A->first, A->second);
|
|
}
|
|
|
|
endUpdate();
|
|
}
|
|
|
|
};
|
|
|
|
class EntityHolder {
|
|
public:
|
|
Eris::Entity& entity;
|
|
|
|
EntityHolder(Eris::Entity& entity) :
|
|
entity(entity) {
|
|
|
|
}
|
|
|
|
~EntityHolder() {
|
|
entity.shutdown();
|
|
}
|
|
};
|
|
|
|
class Timer {
|
|
public:
|
|
|
|
WFMath::TimeStamp startTime;
|
|
|
|
Timer() :
|
|
startTime(WFMath::TimeStamp::now()) {
|
|
|
|
}
|
|
|
|
bool hasElapsed(long milliseconds) {
|
|
return (WFMath::TimeStamp::now() - startTime).milliseconds() > milliseconds;
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* @brief Base class for listening to events. After the event is triggered isCompleted() will return true.
|
|
*
|
|
* For background events, call waitForCompletion(...).
|
|
*/
|
|
class CompleteEventListener : public virtual sigc::trackable {
|
|
protected:
|
|
std::condition_variable mCondition;
|
|
std::mutex mMutex;
|
|
int mCompletedCount;
|
|
|
|
void handleEvent() {
|
|
{
|
|
std::lock_guard <std::mutex> lock(mMutex);
|
|
mCompletedCount++;
|
|
}
|
|
mCondition.notify_all();
|
|
}
|
|
|
|
public:
|
|
CompleteEventListener() :
|
|
mCompletedCount(0) {
|
|
}
|
|
|
|
int waitForCompletion(long milliseconds) {
|
|
std::unique_lock <std::mutex> lock(mMutex);
|
|
if (mCompletedCount) {
|
|
return true;
|
|
}
|
|
mCondition.wait_for(lock, std::chrono::milliseconds(milliseconds));
|
|
return mCompletedCount;
|
|
}
|
|
|
|
int getCompletedCount() {
|
|
return mCompletedCount;
|
|
}
|
|
};
|
|
|
|
class WorldSizeChangedListener : public CompleteEventListener {
|
|
protected:
|
|
|
|
void eventListener() {
|
|
handleEvent();
|
|
}
|
|
|
|
public:
|
|
WorldSizeChangedListener(sigc::signal<void()>& event) {
|
|
event.connect(sigc::mem_fun(*this, &WorldSizeChangedListener::eventListener));
|
|
}
|
|
};
|
|
|
|
class AfterTerrainUpdateListener : public CompleteEventListener {
|
|
protected:
|
|
|
|
void eventListener(const std::vector <WFMath::AxisBox<2>>& areas, const std::set<TerrainPage*>& pages) {
|
|
handleEvent();
|
|
}
|
|
|
|
public:
|
|
AfterTerrainUpdateListener(sigc::signal<void(const std::vector <WFMath::AxisBox<2>>&, const std::set<TerrainPage*>&)>& event) {
|
|
event.connect(sigc::mem_fun(*this, &AfterTerrainUpdateListener::eventListener));
|
|
}
|
|
};
|
|
|
|
class TestTerrainHandler : public Terrain::TerrainHandler {
|
|
public:
|
|
|
|
TestTerrainHandler(int pageIndexSize, ICompilerTechniqueProvider& compilerTechniqueProvider, Eris::EventService& es) :
|
|
Terrain::TerrainHandler(pageIndexSize, compilerTechniqueProvider, es) {
|
|
mLightning = new DummyLightning();
|
|
}
|
|
|
|
Mercator::Terrain* getTerrain() {
|
|
return mTerrain;
|
|
}
|
|
|
|
/**
|
|
* Checks the height, using an integer.
|
|
* This is because of the floats being used, we can't be 100% certain that a piece of terrain should be exactly the specified height.
|
|
*/
|
|
bool checkTerrainHeight(float x, float y, int height) {
|
|
float realHeight = 0;
|
|
getHeight(TerrainPosition(x, y), realHeight);
|
|
return (int) (realHeight) == height;
|
|
}
|
|
};
|
|
|
|
class TerrainSetup {
|
|
public:
|
|
boost::asio::io_service io_service;
|
|
Eris::EventService es;
|
|
DummyCompilerTechniqueProvider compilerTechniqueProvider;
|
|
TestTerrainHandler terrainHandler;
|
|
|
|
|
|
TerrainSetup() :
|
|
es(io_service), terrainHandler(513, compilerTechniqueProvider, es) {
|
|
}
|
|
|
|
bool createBaseTerrain(float height) {
|
|
WorldSizeChangedListener worldSizeChangedListener(terrainHandler.EventWorldSizeChanged);
|
|
|
|
TerrainDefPointStore terrainDefPoints;
|
|
terrainDefPoints.push_back(TerrainDefPoint(-1, -1, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(-1, 0, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(-1, 1, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(0, -1, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(0, 0, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(0, 1, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(1, -1, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(1, 0, height));
|
|
terrainDefPoints.push_back(TerrainDefPoint(1, 1, height));
|
|
|
|
terrainHandler.updateTerrain(terrainDefPoints);
|
|
Timer timer;
|
|
do {
|
|
es.processAllHandlers();
|
|
} while (!timer.hasElapsed(5000) && !worldSizeChangedListener.getCompletedCount());
|
|
return worldSizeChangedListener.getCompletedCount() > 0;
|
|
}
|
|
|
|
bool createPages() {
|
|
DummyTerrainBridge* bridge1 = new DummyTerrainBridge();
|
|
DummyTerrainBridge* bridge2 = new DummyTerrainBridge();
|
|
DummyTerrainBridge* bridge3 = new DummyTerrainBridge();
|
|
DummyTerrainBridge* bridge4 = new DummyTerrainBridge();
|
|
|
|
terrainHandler.setUpTerrainPageAtIndex(TerrainIndex(0, 0), bridge1);
|
|
terrainHandler.setUpTerrainPageAtIndex(TerrainIndex(0, 1), bridge2);
|
|
terrainHandler.setUpTerrainPageAtIndex(TerrainIndex(-1, 0), bridge3);
|
|
terrainHandler.setUpTerrainPageAtIndex(TerrainIndex(-1, 1), bridge4);
|
|
|
|
{
|
|
Timer timer;
|
|
do {
|
|
es.processAllHandlers();
|
|
} while ((!timer.hasElapsed(5000)) && (!bridge1->pageReady || !bridge2->pageReady || !bridge3->pageReady || !bridge4->pageReady));
|
|
}
|
|
CPPUNIT_ASSERT(bridge1->pageReady);
|
|
CPPUNIT_ASSERT(bridge2->pageReady);
|
|
CPPUNIT_ASSERT(bridge3->pageReady);
|
|
CPPUNIT_ASSERT(bridge4->pageReady);
|
|
return true;
|
|
}
|
|
|
|
bool reloadTerrain() {
|
|
AfterTerrainUpdateListener afterTerrainUpdateListener(terrainHandler.EventAfterTerrainUpdate);
|
|
std::vector <TerrainPosition> positions;
|
|
positions.push_back(TerrainPosition(-32, -32));
|
|
positions.push_back(TerrainPosition(-32, 32));
|
|
positions.push_back(TerrainPosition(32, 32));
|
|
positions.push_back(TerrainPosition(32, -32));
|
|
|
|
terrainHandler.reloadTerrain(positions);
|
|
|
|
{
|
|
Timer timer;
|
|
do {
|
|
es.processAllHandlers();
|
|
} while ((!timer.hasElapsed(5000)) && afterTerrainUpdateListener.getCompletedCount() < 4);
|
|
}
|
|
CPPUNIT_ASSERT(afterTerrainUpdateListener.getCompletedCount() == 4);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class TerrainTestCase : public CppUnit::TestFixture {
|
|
CPPUNIT_TEST_SUITE( TerrainTestCase);
|
|
// CPPUNIT_TEST( testCreateTerrain);
|
|
// CPPUNIT_TEST( testAlterTerrain);
|
|
CPPUNIT_TEST( testApplyMod);
|
|
// CPPUNIT_TEST( testUpdateMod);
|
|
|
|
CPPUNIT_TEST_SUITE_END();
|
|
|
|
protected:
|
|
|
|
public:
|
|
void testCreateTerrain() {
|
|
|
|
Ogre::Root root;
|
|
|
|
TerrainSetup terrainSetup;
|
|
|
|
float terrainHeight = 25.0f;
|
|
|
|
CPPUNIT_ASSERT(terrainSetup.createBaseTerrain(terrainHeight));
|
|
CPPUNIT_ASSERT(terrainSetup.createPages());
|
|
CPPUNIT_ASSERT(terrainSetup.reloadTerrain());
|
|
|
|
CPPUNIT_ASSERT(terrainSetup.terrainHandler.getTerrainInfo().getTotalNumberOfPagesX() == 2);
|
|
CPPUNIT_ASSERT(terrainSetup.terrainHandler.getTerrainInfo().getTotalNumberOfPagesY() == 2);
|
|
float height = 0;
|
|
CPPUNIT_ASSERT(terrainSetup.terrainHandler.getHeight(TerrainPosition(-1, -1), height));
|
|
CPPUNIT_ASSERT(height == terrainHeight);
|
|
|
|
}
|
|
|
|
void testAlterTerrain() {
|
|
|
|
Ogre::Root root;
|
|
TerrainSetup terrainSetup;
|
|
|
|
Terrain::TerrainHandler& terrainHandler = terrainSetup.terrainHandler;
|
|
CPPUNIT_ASSERT(terrainSetup.createBaseTerrain(25.0f));
|
|
CPPUNIT_ASSERT(terrainSetup.createPages());
|
|
CPPUNIT_ASSERT(terrainSetup.reloadTerrain());
|
|
|
|
TerrainDefPointStore terrainDefPoints;
|
|
terrainDefPoints.push_back(TerrainDefPoint(-1, -1, -200));
|
|
terrainDefPoints.push_back(TerrainDefPoint(-1, 0, -200));
|
|
terrainDefPoints.push_back(TerrainDefPoint(0, -1, -200));
|
|
terrainDefPoints.push_back(TerrainDefPoint(0, 0, -200));
|
|
|
|
terrainHandler.updateTerrain(terrainDefPoints);
|
|
|
|
AfterTerrainUpdateListener afterTerrainUpdateListener(terrainHandler.EventAfterTerrainUpdate);
|
|
|
|
{
|
|
Timer timer;
|
|
do {
|
|
terrainSetup.es.processAllHandlers();
|
|
} while ((!timer.hasElapsed(5000)) && afterTerrainUpdateListener.getCompletedCount() < 4);
|
|
}
|
|
CPPUNIT_ASSERT(afterTerrainUpdateListener.getCompletedCount() == 4);
|
|
CPPUNIT_ASSERT(terrainHandler.getTerrainInfo().getTotalNumberOfPagesX() == 2);
|
|
CPPUNIT_ASSERT(terrainHandler.getTerrainInfo().getTotalNumberOfPagesY() == 2);
|
|
float height = 0;
|
|
CPPUNIT_ASSERT(terrainHandler.getHeight(TerrainPosition(-32, -32), height));
|
|
CPPUNIT_ASSERT(height == -200);
|
|
}
|
|
|
|
void testApplyMod() {
|
|
|
|
bool shouldQuit, pollEris;
|
|
MainLoopController loopController(shouldQuit, pollEris);
|
|
Ogre::Root root;
|
|
|
|
TerrainSetup terrainSetup;
|
|
Terrain::TerrainHandler& terrainHandler = terrainSetup.terrainHandler;
|
|
DummyEntity entity;
|
|
EntityHolder entityHolder(entity);
|
|
entity.setAttr("pos", WFMath::Point<3>::ZERO().toAtlas());
|
|
|
|
float terrainHeight = 25.0f;
|
|
|
|
CPPUNIT_ASSERT(terrainSetup.createBaseTerrain(terrainHeight));
|
|
CPPUNIT_ASSERT(terrainSetup.createPages());
|
|
CPPUNIT_ASSERT(terrainSetup.reloadTerrain());
|
|
|
|
Atlas::Message::ListType polygon;
|
|
|
|
polygon.push_back(WFMath::Point<2>(-10, -10).toAtlas());
|
|
polygon.push_back(WFMath::Point<2>(-10, 10).toAtlas());
|
|
polygon.push_back(WFMath::Point<2>(10, 10).toAtlas());
|
|
polygon.push_back(WFMath::Point<2>(10, -10).toAtlas());
|
|
|
|
Atlas::Message::MapType shape;
|
|
shape["points"] = polygon;
|
|
shape["type"] = "polygon";
|
|
|
|
Atlas::Message::MapType mod;
|
|
mod["shape"] = shape;
|
|
mod["type"] = "levelmod";
|
|
mod["heightoffset"] = 2.0f;
|
|
|
|
entity.setAttr("terrainmod", mod);
|
|
|
|
OgreView::Terrain::TerrainMod terrainMod(entity);
|
|
terrainMod.init();
|
|
terrainHandler.addTerrainMod(&terrainMod);
|
|
|
|
}
|
|
|
|
void testUpdateMod() {
|
|
|
|
Ogre::Root root;
|
|
|
|
TerrainSetup terrainSetup;
|
|
TestTerrainHandler& terrainHandler = terrainSetup.terrainHandler;
|
|
DummyEntity entity;
|
|
EntityHolder entityHolder(entity);
|
|
|
|
entity.setAttr("pos", WFMath::Point<3>::ZERO().toAtlas());
|
|
|
|
float terrainHeight = 25.0f;
|
|
|
|
CPPUNIT_ASSERT(terrainSetup.createBaseTerrain(terrainHeight));
|
|
CPPUNIT_ASSERT(terrainSetup.createPages());
|
|
CPPUNIT_ASSERT(terrainSetup.reloadTerrain());
|
|
|
|
Atlas::Message::ListType polygon;
|
|
|
|
polygon.push_back(WFMath::Point<2>(-10, -10).toAtlas());
|
|
polygon.push_back(WFMath::Point<2>(-10, 10).toAtlas());
|
|
polygon.push_back(WFMath::Point<2>(10, 10).toAtlas());
|
|
polygon.push_back(WFMath::Point<2>(10, -10).toAtlas());
|
|
|
|
Atlas::Message::MapType shape;
|
|
shape["points"] = polygon;
|
|
shape["type"] = "polygon";
|
|
|
|
Atlas::Message::MapType mod;
|
|
mod["shape"] = shape;
|
|
mod["type"] = "levelmod";
|
|
mod["heightoffset"] = 2.0f;
|
|
|
|
entity.setAttr("terrainmod", mod);
|
|
|
|
OgreView::Terrain::TerrainMod* terrainMod = new Terrain::TerrainMod(entity);
|
|
terrainMod->init();
|
|
|
|
{
|
|
AfterTerrainUpdateListener afterTerrainUpdateListener(terrainHandler.EventAfterTerrainUpdate);
|
|
|
|
//The shape should affect all four pages.
|
|
terrainHandler.addTerrainMod(terrainMod);
|
|
|
|
{
|
|
Timer timer;
|
|
do {
|
|
terrainSetup.es.processAllHandlers();
|
|
} while (afterTerrainUpdateListener.getCompletedCount() < 4);
|
|
}
|
|
CPPUNIT_ASSERT(afterTerrainUpdateListener.getCompletedCount() == 4);
|
|
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(-5, -5, 2.0f));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(-5, 5, 2.0f));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(5, 5, 2.0f));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(5, -5, 2.0f));
|
|
|
|
}
|
|
|
|
mod["heightoffset"] = 20.0f;
|
|
//Setting the attribute should trigger a mod change
|
|
entity.setAttr("terrainmod", mod);
|
|
|
|
{
|
|
AfterTerrainUpdateListener afterTerrainUpdateListener(terrainHandler.EventAfterTerrainUpdate);
|
|
|
|
{
|
|
Timer timer;
|
|
do {
|
|
terrainSetup.es.processAllHandlers();
|
|
// } while ((!timer.hasElapsed(5000)) && afterTerrainUpdateListener.getCompletedCount() < 4);
|
|
} while (afterTerrainUpdateListener.getCompletedCount() < 4);
|
|
}
|
|
CPPUNIT_ASSERT(afterTerrainUpdateListener.getCompletedCount() == 4);
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(-5, -5, 20.0f));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(-5, 5, 20.0f));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(5, 5, 20.0f));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(5, -5, 20.0f));
|
|
|
|
}
|
|
|
|
//Move the mod out of the way
|
|
entity.setAttr("pos", WFMath::Point<3>(40, 40, 0).toAtlas());
|
|
|
|
{
|
|
AfterTerrainUpdateListener afterTerrainUpdateListener(terrainHandler.EventAfterTerrainUpdate);
|
|
|
|
{
|
|
Timer timer;
|
|
do {
|
|
terrainSetup.es.processAllHandlers();
|
|
} while ((!timer.hasElapsed(5000)) && afterTerrainUpdateListener.getCompletedCount() < 4);
|
|
}
|
|
CPPUNIT_ASSERT(afterTerrainUpdateListener.getCompletedCount() == 4);
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(-5, -5, 25));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(-5, 5, 25));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(5, 5, 25));
|
|
CPPUNIT_ASSERT(terrainHandler.checkTerrainHeight(5, -5, 25));
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
CPPUNIT_TEST_SUITE_REGISTRATION( Ember::TerrainTestCase);
|
|
|
|
int main(int argc, char** argv) {
|
|
CppUnit::TextUi::TestRunner runner;
|
|
CppUnit::TestFactoryRegistry& registry = CppUnit::TestFactoryRegistry::getRegistry();
|
|
runner.addTest(registry.makeTest());
|
|
|
|
// Shows a message as each test starts
|
|
CppUnit::BriefTestProgressListener listener;
|
|
runner.eventManager().addListener(&listener);
|
|
|
|
bool wasSuccessful = runner.run("", false);
|
|
return !wasSuccessful;
|
|
}
|
|
|