123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175/*MinetestCopyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU Lesser General Public License as published bythe Free Software Foundation; either version 2.1 of the License, or(at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/#include"test.h"#include <sstream>#include"irrlichttypes_extrabloated.h"#include"log.h"#include"serialization.h"#include"nodedef.h"#include"noise.h"class TestCompression :public TestBase {public:TestCompression() {TestManager::registerTestModule(this); }const char*getName() {return"TestCompression"; }voidrunTests(IGameDef *gamedef);voidtestRLECompression();voidtestZlibCompression();voidtestZlibLargeData();};static TestCompression g_test_instance;voidTestCompression::runTests(IGameDef *gamedef){TEST(testRLECompression);TEST(testZlibCompression);TEST(testZlibLargeData);}////////////////////////////////////////////////////////////////////////////////voidTestCompression::testRLECompression(){
SharedBuffer<u8>fromdata(4);
fromdata[0]=1;
fromdata[1]=5;
fromdata[2]=5;
fromdata[3]=1;std::ostringstream os(std::ios_base::binary);compress(fromdata, os,0);std::string str_out =349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605for(u32 i =0; i < str_out.size(); i++)
infostream << (u32)str_out[i] <<",";
infostream <<std::endl;std::istringstream is(str_out,std::ios_base::binary);std::ostringstream os2(std::ios_base::binary);decompress(is/*MinetestCopyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU Lesser General Public License as published bythe Free Software Foundation; either version 2.1 of the License, or(at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU Lesser General Public License for more details.You should have received a copy of the GNU Lesser General Public License alongwith this program; if not, write to the Free Software Foundation, Inc.,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.*/#include"test.h"#include"nodedef.h"#include"itemdef.h"#include"gamedef.h"#include"mods.h"
content_t t_CONTENT_STONE;
content_t t_CONTENT_GRASS;
content_t t_CONTENT_TORCH;
content_t t_CONTENT_WATER;
content_t t_CONTENT_LAVA;
content_t t_CONTENT_BRICK;//////////////////////////////////////////////////////////////////////////////////////// TestGameDef////class TestGameDef :public IGameDef {public:TestGameDef();~TestGameDef();
IItemDefManager *getItemDefManager() {return m_itemdef; }
INodeDefManager *getNodeDefManager() {return m_nodedef; }
ICraftDefManager *getCraftDefManager() {return m_craftdef; }
ITextureSource *getTextureSource() {return m_texturesrc; }
IShaderSource *getShaderSource() {return m_shadersrc; }
ISoundManager *getSoundManager() {return m_soundmgr; }
MtEventManager *getEventManager() {return m_eventmgr; }scene::ISceneManager *getSceneManager() {return m_scenemgr; }
IRollbackManager *getRollbackManager() {return m_rollbackmgr; }
EmergeManager *getEmergeManager() {return m_emergemgr; }scene::IAnimatedMesh *getMesh(conststd::string &filename) {return NULL; }boolcheckLocalPrivilege(conststd::string &priv) {return false; }
u16 allocateUnknownNodeId(conststd::string &name) {return0; }voiddefineSomeNodes();virtualconststd::vector<ModSpec> &getMods()const{staticstd::vector<ModSpec> testmodspec;return testmodspec;}virtualconst ModSpec*getModSpec(conststd::string &modname)const{return NULL; }virtual std::string getModStoragePath()const{return"."; }virtualboolregisterModStorage(ModMetadata *meta) {return true; }virtualvoidunregisterModStorage(conststd::string &name) {}private:
IItemDefManager *m_itemdef;
INodeDefManager *m_nodedef;
ICraftDefManager *m_craftdef;
ITextureSource *m_texturesrc;
IShaderSource *m_shadersrc;
ISoundManager *m_soundmgr;
MtEventManager *m_eventmgr;scene::ISceneManager *m_scenemgr;
IRollbackManager *m_rollbackmgr;
EmergeManager *m_emergemgr;};TestGameDef::TestGameDef(){
m_itemdef =createItemDefManager();
m_nodedef =createNodeDefManager();defineSomeNodes();}TestGameDef::~TestGameDef(){delete m_itemdef;delete m_nodedef;}voidTestGameDef::defineSomeNodes(){
IWritableItemDefManager *idef = (IWritableItemDefManager *)m_itemdef;
IWritableNodeDefManager *ndef = (IWritableNodeDefManager *)m_nodedef;
ItemDefinition itemdef;
ContentFeatures f;//// Stone
itemdef =ItemDefinition();
itemdef.type = ITEM_NODE;
itemdef.name ="default:stone";
itemdef.description ="Stone";
itemdef.groups["cracky"] =3;
itemdef.inventory_image ="[inventorycube""{default_stone.png""{default_stone.png""{default_stone.png";
f =ContentFeatures();
f.name = itemdef.name;for(int i =0; i <6; i++)
f.tiledef[i].name ="default_stone.png";
f.is_ground_content =true;
idef->registerItem(itemdef);
t_CONTENT_STONE = ndef->set(f.name, f);//// Grass
itemdef =ItemDefinition();
itemdef.type = ITEM_NODE;
itemdef.name ="default:dirt_with_grass";
itemdef.description ="Dirt with grass";
itemdef.groups["crumbly"] =3;
itemdef.inventory_image ="[inventorycube""{default_grass.png""{default_dirt.png&default_grass_side.png""{default_dirt.png&default_grass_side.png";
f =ContentFeatures();
f.name = itemdef.name;
f.tiledef[0].name ="default_grass.png";
f.tiledef[1].name ="default_dirt.png";for(int i =2; i <6; i++)
f.tiledef[i].name ="default_dirt.png^default_grass_side.png";
f.is_ground_content =true;
idef->registerItem(itemdef);
t_CONTENT_GRASS = ndef->set(f.name, f);//// Torch (minimal definition for lighting tests)
itemdef =ItemDefinition();
itemdef.type = ITEM_NODE;
itemdef.name ="default:torch";
f =ContentFeatures();
f.name = itemdef.name;
f.param_type = CPT_LIGHT;
f.light_propagates =true;
f.sunlight_propagates =true;
f.light_source = LIGHT_MAX-1;
idef->registerItem(itemdef);
t_CONTENT_TORCH = ndef->set(f.name, f);//// Water
itemdef =ItemDefinition();
itemdef.type = ITEM_NODE;
itemdef.name ="default:water";
itemdef.description ="Water";
itemdef.inventory_image ="[inventorycube""{default_water.png""{default_water.png""{default_water.png";
f =ContentFeatures();
f.name = itemdef.name;
f.alpha =128;
f.liquid_type = LIQUID_SOURCE;
f.liquid_viscosity =4;
f.is_ground_content =true;
f.groups["liquids"] =3;for(int i =0; i <6; i++)
f.tiledef[i].name ="default_water.png";
idef->registerItem(itemdef);
t_CONTENT_WATER = ndef->set(f.name, f);//// Lava
itemdef =ItemDefinition();
itemdef.type = ITEM_NODE;
itemdef.name ="default:lava";
itemdef.description ="Lava";
itemdef.inventory_image ="[inventorycube""{default_lava.png""{default_lava.png""{default_lava.png";
f =ContentFeatures();
f.name = itemdef.name;
f.alpha =128;
f.liquid_type = LIQUID_SOURCE;
f.liquid_viscosity =7;
f.light_source = LIGHT_MAX-1;
f.is_ground_content =true;
f.groups["liquids"] =3;for(int i =0; i <6; i++)
f.tiledef[i].name ="default_lava.png";
idef->registerItem(itemdef);
t_CONTENT_LAVA = ndef->set(f.name, f);//// Brick
itemdef =ItemDefinition();
itemdef.type = ITEM_NODE;
itemdef.name ="default:brick";
itemdef.description ="Brick";
itemdef.groups["cracky"] =3;
itemdef.inventory_image ="[inventorycube""{default_brick.png""{default_brick.png""{default_brick.png";
f =ContentFeatures();
f.name = itemdef.name;for(int i =0; i <6; i++)
f.tiledef[i].name ="default_brick.png";
f.is_ground_content =true;
idef->registerItem(itemdef);
t_CONTENT_BRICK = ndef->set(f.name, f);}//////// run_tests////boolrun_tests(){DSTACK(FUNCTION_NAME);
u32 t1 =porting::getTime(PRECISION_MILLI);
TestGameDef gamedef;
g_logger.setLevelSilenced(LL_ERROR,true);
u32 num_modules_failed =0;
u32 num_total_tests_failed =0;
u32 num_total_tests_run =0;std::vector<TestBase *> &testmods =TestManager::getTestModules();for(size_t i =0; i != testmods.size(); i++) {if(!testmods[i]->testModule(&gamedef))
num_modules_failed++;
num_total_tests_failed += testmods[i]->num_tests_failed;
num_total_tests_run += testmods[i]->num_tests_run;}
u32 tdiff =porting::getTime(PRECISION_MILLI) - t1;
g_logger.setLevelSilenced(LL_ERROR,false);const char*overall_status = (num_modules_failed ==0) ?"PASSED":"FAILED";
rawstream
<<"++++++++++++++++++++++++++++++++++++++++"<<"++++++++++++++++++++++++++++++++++++++++"<<std::endl
<<"Unit Test Results: "<< overall_status <<std::endl
<<" "<< num_modules_failed <<" / "<< testmods.size()<<" failed modules ("<< num_total_tests_failed <<" / "<< num_total_tests_run <<" failed individual tests)."<<std::endl
<<" Testing took "<< tdiff <<"ms total."<<std::endl
<<"++++++++++++++++++++++++++++++++++++++++"<<"++++++++++++++++++++++++++++++++++++++++"<<std::endl;return num_modules_failed;}//////// TestBase////boolTestBase::testModule(IGameDef *gamedef){
rawstream <<"======== Testing module "<<getName() <<std::endl;
u32 t1 =porting::getTime(PRECISION_MILLI);runTests(gamedef);
u32 tdiff =porting::getTime(PRECISION_MILLI) - t1;
rawstream <<"======== Module "<<getName() <<" "<< (num_tests_failed ?"failed":"passed") <<" ("<< num_tests_failed
<<" failures / "<< num_tests_run <<" tests) - "<< tdiff
<<"ms"<<std::endl;if(!m_test_dir.empty())fs::RecursiveDelete(m_test_dir);return num_tests_failed ==0;}std::string TestBase::getTestTempDirectory(){if(!m_test_dir.empty())return m_test_dir;char buf[32];snprintf(buf,sizeof(buf),"%08X",myrand());
m_test_dir =fs::TempPath() + DIR_DELIM "mttest_"+ buf;if(!fs::CreateDir(m_test_dir))throwTestFailedException();return m_test_dir;}std::string TestBase::getTestTempFile(){char buf[32];snprintf(buf,sizeof(buf),"%08X",myrand());returngetTestTempDirectory() + DIR_DELIM + buf +".tmp";}/* NOTE: These tests became non-working then NodeContainer was removed. These should be redone, utilizing some kind of a virtual interface for Map (IMap would be fine).*/#if 0struct TestMapBlock:public TestBase
{class TC :public NodeContainer
{public:
MapNode node;bool position_valid;core::list<v3s16> validity_exceptions;TC(){
position_valid =true;}virtualboolisValidPosition(v3s16 p){//return position_valid ^ (p==position_valid_exception);bool exception =false;for(core::list<v3s16>::Iterator i=validity_exceptions.begin();
i != validity_exceptions.end(); i++){if(p == *i){
exception =true;break;}}return exception ? !position_valid : position_valid;}virtual MapNode getNode(v3s16 p){if(isValidPosition(p) ==false)throwInvalidPositionException();return node;}virtualvoidsetNode(v3s16 p, MapNode & n){if(isValidPosition(p) ==false)throwInvalidPositionException();};virtual u16 nodeContainerId()const{return666;}};voidRun(){
TC parent;
MapBlock b(&parent,v3s16(1,1,1));
v3s16 relpos(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);UASSERT(b.getPosRelative() == relpos);UASSERT(b.getBox().MinEdge.X == MAP_BLOCKSIZE);UASSERT(b.getBox().MaxEdge.X == MAP_BLOCKSIZE*2-1);UASSERT(b.getBox().MinEdge.Y == MAP_BLOCKSIZE);UASSERT(b.getBox().MaxEdge.Y == MAP_BLOCKSIZE*2-1);UASSERT(b.getBox().MinEdge.Z == MAP_BLOCKSIZE);UASSERT(b.getBox().MaxEdge.Z == MAP_BLOCKSIZE*2-1);UASSERT(b.isValidPosition(v3s16(0,0,0)) ==true);UASSERT(b.isValidPosition(v3s16(-1,0,0)) ==false);UASSERT(b.isValidPosition(v3s16(-1,-142,-2341)) ==false);UASSERT(b.isValidPosition(v3s16(-124,142,2341)) ==false);UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) ==true);UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE,MAP_BLOCKSIZE-1)) ==false);/* TODO: this method should probably be removed if the block size isn't going to be set variable *//*UASSERT(b.getSizeNodes() == v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE));*/// Changed flag should be initially setUASSERT(b.getModified() == MOD_STATE_WRITE_NEEDED);
b.resetModified();UASSERT(b.getModified() == MOD_STATE_CLEAN);// All nodes should have been set to// .d=CONTENT_IGNORE and .getLight() = 0for(u16 z=0; z<MAP_BLOCKSIZE; z++)for(u16 y=0; y<MAP_BLOCKSIZE; y++)for(u16 x=0; x<MAP_BLOCKSIZE; x++){//UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_AIR);UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_IGNORE);UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) ==0);UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) ==0);}{
MapNode n(CONTENT_AIR);for(u16 z=0; z<MAP_BLOCKSIZE; z++)for(u16 y=0; y<MAP_BLOCKSIZE; y++)for(u16 x=0; x<MAP_BLOCKSIZE; x++){
b.setNode(v3s16(x,y,z), n);}}/* Parent fetch functions */
parent.position_valid =false;
parent.node.setContent(5);
MapNode n;// Positions in the block should still be validUASSERT(b.isValidPositionParent(v3s16(0,0,0)) ==true);UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) ==true);
n = b.getNodeParent(v3s16(0,MAP_BLOCKSIZE-1,0));UASSERT(n.getContent() == CONTENT_AIR);// ...but outside the block they should be invalidUASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) ==false);UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) ==false);UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) ==false);{bool exception_thrown =false;try{// This should throw an exception
MapNode n = b.getNodeParent(v3s16(0,0,-1));}catch(InvalidPositionException &e){
exception_thrown =true;}UASSERT(exception_thrown);}
parent.position_valid =true;// Now the positions outside should be validUASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) ==true);UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) ==true);UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) ==true);
n = b.getNodeParent(v3s16(0,0,MAP_BLOCKSIZE));UASSERT(n.getContent() ==5);/* Set a node */
v3s16 p(1,2,0);
n.setContent(4);
b.setNode(p, n);UASSERT(b.getNode(p).getContent() ==4);//TODO: Update to new system/*UASSERT(b.getNodeTile(p) == 4); UASSERT(b.getNodeTile(v3s16(-1,-1,0)) == 5);*//* propagateSunlight() */// Set lighting of all nodes to 0for(u16 z=0; z<MAP_BLOCKSIZE; z++){for(u16 y=0; y<MAP_BLOCKSIZE; y++){for(u16 x=0; x<MAP_BLOCKSIZE; x++){
MapNode n = b.getNode(v3s16(x,y,z));
n.setLight(LIGHTBANK_DAY,0);
n.setLight(LIGHTBANK_NIGHT,0);
b.setNode(v3s16(x,y,z), n);}}}{/* Check how the block handles being a lonely sky block */
parent.position_valid =true;
b.setIsUnderground(false);
parent.node.setContent(CONTENT_AIR);
parent.node.setLight(LIGHTBANK_DAY, LIGHT_SUN);
parent.node.setLight(LIGHTBANK_NIGHT,0);core::map<v3s16,bool> light_sources;// The bottom block is invalid, because we have a shadowing nodeUASSERT(b.propagateSunlight(light_sources) ==false);UASSERT(b.getNode(v3s16(1,4,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);UASSERT(b.getNode(v3s16(1,3,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);UASSERT(b.getNode(v3s16(1,2,0)).getLight(LIGHTBANK_DAY) ==0);UASSERT(b.getNode(v3s16(1,1,0)).getLight(LIGHTBANK_DAY) ==0);UASSERT(b.getNode(v3s16(1,0,0)).getLight(LIGHTBANK_DAY) ==0);UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);UASSERT(b.getFaceLight2(1000, p,v3s16(0,1,0)) == LIGHT_SUN);UASSERT(b.getFaceLight2(1000, p,v3s16(0,-1,0)) ==0);UASSERT(b.getFaceLight2(0, p,v3s16(0,-1,0)) ==0);// According to MapBlock::getFaceLight,// The face on the z+ side should have double-diminished light//UASSERT(b.getFaceLight(p, v3s16(0,0,1)) == diminish_light(diminish_light(LIGHT_MAX)));// The face on the z+ side should have diminished lightUASSERT(b.getFaceLight2(1000, p,v3s16(0,0,1)) ==diminish_light(LIGHT_MAX));}/* Check how the block handles being in between blocks with some non-sunlight while being underground */{// Make neighbours to exist and set some non-sunlight to them
parent.position_valid =true;
b.setIsUnderground(true);
parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);core::map<v3s16,bool> light_sources;// The block below should be valid because there shouldn't be// sunlight in there eitherUASSERT(b.propagateSunlight(light_sources,true) ==true);// Should not touch nodes that are not affected (that is, all of them)//UASSERT(b.getNode(v3s16(1,2,3)).getLight() == LIGHT_SUN);// Should set light of non-sunlighted blocks to 0.UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) ==0);}/* Set up a situation where: - There is only air in this block - There is a valid non-sunlighted block at the bottom, and - Invalid blocks elsewhere. - the block is not underground. This should result in bottom block invalidity */{
b.setIsUnderground(false);// Clear blockfor(u16 z=0; z<MAP_BLOCKSIZE; z++){for(u16 y=0; y<MAP_BLOCKSIZE; y++){for(u16 x=0; x<MAP_BLOCKSIZE; x++){
MapNode n;
n.setContent(CONTENT_AIR);
n.setLight(LIGHTBANK_DAY,0);
b.setNode(v3s16(x,y,z), n);}}}// Make neighbours invalid
parent.position_valid =false;// Add exceptions to the top of the bottom blockfor(u16 x=0; x<MAP_BLOCKSIZE; x++)for(u16 z=0; z<MAP_BLOCKSIZE; z++){
parent.validity_exceptions.push_back(v3s16(MAP_BLOCKSIZE+x, MAP_BLOCKSIZE-1, MAP_BLOCKSIZE+z));}// Lighting value for the valid nodes
parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);core::map<v3s16,bool> light_sources;// Bottom block is not validUASSERT(b.propagateSunlight(light_sources) ==false);}}};struct TestMapSector:public TestBase
{class TC :public NodeContainer
{public:
MapNode node;bool position_valid;TC(){
position_valid =true;}virtualboolisValidPosition(v3s16 p){return position_valid;}virtual MapNode getNode(v3s16 p){if(position_valid ==false)throwInvalidPositionException();return node;}virtualvoidsetNode(v3s16 p, MapNode & n){if(position_valid ==false)throwInvalidPositionException();};virtual u16 nodeContainerId()const{return666;}};voidRun(){
TC parent;
parent.position_valid =false;// Create one with no heightmaps
ServerMapSector sector(&parent,v2s16(1,1));UASSERT(sector.getBlockNoCreateNoEx(0) ==0);UASSERT(sector.getBlockNoCreateNoEx(1) ==0);
MapBlock * bref = sector.createBlankBlock(-2);UASSERT(sector.getBlockNoCreateNoEx(0) ==0);UASSERT(sector.getBlockNoCreateNoEx(-2) == bref);//TODO: Check for AlreadyExistsException/*bool exception_thrown = false; try{ sector.getBlock(0); } catch(InvalidPositionException &e){ exception_thrown = true; } UASSERT(exception_thrown);*/}};#endif