/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the 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 of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with 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 "irrlichttypes_extrabloated.h"
#include "debug.h"
#include "map.h"
#include "player.h"
#include "main.h"
#include "socket.h"
#include "connection.h"
#include "serialization.h"
#include "voxel.h"
#include "collision.h"
#include <sstream>
#include "porting.h"
#include "content_mapnode.h"
#include "nodedef.h"
#include "mapsector.h"
#include "settings.h"
#include "log.h"
#include "util/string.h"
#include "filesys.h"
#include "voxelalgorithms.h"
#include "inventory.h"
#include "util/numeric.h"
#include "util/serialize.h"
#include "noise.h" // PseudoRandom used for random data for compression
#include "clientserver.h" // LATEST_PROTOCOL_VERSION
#include <algorithm>
/*
Asserts that the exception occurs
*/
#define EXCEPTION_CHECK(EType, code)\
{\
bool exception_thrown = false;\
try{ code; }\
catch(EType &e) { exception_thrown = true; }\
UASSERT(exception_thrown);\
}
#define UTEST(x, fmt, ...)\
{\
if(!(x)){\
LOGLINEF(LMT_ERROR, "Test (%s) failed: " fmt, #x, ##__VA_ARGS__);\
test_failed = true;\
}\
}
#define UASSERT(x) UTEST(x, "UASSERT")
/*
A few item and node definitions for those tests that need them
*/
static content_t CONTENT_STONE;
static content_t CONTENT_GRASS;
static content_t CONTENT_TORCH;
void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *ndef)
{
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);
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);
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);
CONTENT_TORCH = ndef->set(f.name, f);
}
struct TestBase
{
bool test_failed;
TestBase():
test_failed(false)
{}
};
struct TestUtilities: public TestBase
{
void Run()
{
/*infostream<<"wrapDegrees(100.0) = "<<wrapDegrees(100.0)<<std::endl;
infostream<<"wrapDegrees(720.5) = "<<wrapDegrees(720.5)<<std::endl;
infostream<<"wrapDegrees(-0.5) = "<<wrapDegrees(-0.5)<<std::endl;*/
UASSERT(fabs(wrapDegrees(100.0) - 100.0) < 0.001);
UASSERT(fabs(wrapDegrees(720.5) - 0.5) < 0.001);
UASSERT(fabs(wrapDegrees(-0.5) - (-0.5)) < 0.001);
UASSERT(fabs(wrapDegrees(-365.5) - (-5.5)) < 0.001);
UASSERT(lowercase("Foo bAR") == "foo bar");
UASSERT(trim("\n \t\r Foo bAR \r\n\t\t ") == "Foo bAR");
UASSERT(trim("\n \t\r \r\n\t\t ") == "");
UASSERT(is_yes("YeS") == true);
UASSERT(is_yes("") == false);
UASSERT(is_yes("FAlse") == false);
UASSERT(is_yes("-1") == true);
UASSERT(is_yes("0") == false);
UASSERT(is_yes("1") == true);
UASSERT(is_yes("2") == true);
const char *ends[] = {"abc", "c", "bc", NULL};
UASSERT(removeStringEnd("abc", ends) == "");
UASSERT(removeStringEnd("bc", ends) == "b");
UASSERT(removeStringEnd("12c", ends) == "12");
UASSERT(removeStringEnd("foo", ends) == "");
}
};
struct TestPath: public TestBase
{
// adjusts a POSIX path to system-specific conventions
// -> changes '/' to DIR_DELIM
// -> absolute paths start with "C:\\" on windows
std::string p(std::string path)
{
for(size_t i = 0; i < path.size(); ++i){
if(path[i] == '/'){
path.replace(i, 1, DIR_DELIM);
i += std::string(DIR_DELIM).size() - 1; // generally a no-op
}
}
#ifdef _WIN32
if(path[0] == '\\')
path = "C:" + path;
#endif
return path;
}
void Run()
{
std::string path, result, removed;
/*
Test fs::IsDirDelimiter
*/
UASSERT(fs::IsDirDelimiter('/') == true);
UASSERT(fs::IsDirDelimiter('A') == false);
UASSERT(fs::IsDirDelimiter(0) == false);
#ifdef _WIN32
UASSERT(fs::IsDirDelimiter('\\') == true);
#else
UASSERT(fs::IsDirDelimiter('\\') == false);
#endif
/*
Test fs::PathStartsWith
*/
{
const int numpaths = 12;
std::string paths[numpaths] = {
"",
p("/"),
p("/home/user/minetest"),
p("/home/user/minetest/bin"),
p("/home/user/.minetest"),
p("/tmp/dir/file"),
p("/tmp/file/"),
p("/tmP/file"),
p("/tmp"),
p("/tmp/dir"),
p("/home/user2/minetest/worlds"),
p("/home/user2/minetest/world"),
};
/*
expected fs::PathStartsWith results
0 = returns false
1 = returns true
2 = returns false on windows, false elsewhere
3 = returns true on windows, true elsewhere
4 = returns true if and only if
FILESYS_CASE_INSENSITIVE is true
*/
int expected_results[numpaths][numpaths] = {
{1,2,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0,0,0},
{1,1,0,0,1,0,0,0,0,0,0,0},
{1,1,0,0,0,1,0,0,1,1,0,0},
{1,1,0,0,0,0,1,4,1,0,0,0},
{1,1,0,0,0,0,4,1,4,0,0,0},
{1,1,0,0,0,0,0,0,1,0,0,0},
{1,1,0,0,0,0,0,0,1,1,0,0},
{1,1,0,0,0,0,0,0,0,0,1,0},
{1,1,0,0,0,0,0,0,0,0,0,1},
};
for (int i = 0; i < numpaths; i++)
for (int j = 0; j < numpaths; j++){
/*verbosestream<<"testing fs::PathStartsWith(\""
<<paths[i]<<"\", \""
<<paths[j]<<"\")"<<std::endl;*/
bool starts = fs::PathStartsWith(paths[i], paths[j]);
int expected = expected_results[i][j];
if(expected == 0){
UASSERT(starts == false);
}
else if(expected == 1){
UASSERT(starts == true);
}
#ifdef _WIN32
else if(expected == 2){
UASSERT(starts == false);
}
else if(expected == 3){
UASSERT(starts == true);
}
#else
else if(expected == 2){
UASSERT(starts == true);
}
else if(expected == 3){
UASSERT(starts == false);
}
#endif
else if(expected == 4){
UASSERT(starts == (bool)FILESYS_CASE_INSENSITIVE);
}
}
}
/*
Test fs::RemoveLastPathComponent
*/
UASSERT(fs::RemoveLastPathComponent("") == "");
path = p("/home/user/minetest/bin/..//worlds/world1");
result = fs::RemoveLastPathComponent(path, &removed, 0);
UASSERT(result == path);
UASSERT(removed == "");
result = fs::RemoveLastPathComponent(path, &removed, 1);
UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
UASSERT(removed == p("world1"));
result = fs::RemoveLastPathComponent(path, &removed, 2);
UASSERT(result == p("/home/user/minetest/bin/.."));
UASSERT(removed == p("worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 3);
UASSERT(result == p("/home/user/minetest/bin"));
UASSERT(removed == p("../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 4);
UASSERT(result == p("/home/user/minetest"));
UASSERT(removed == p("bin/../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 5);
UASSERT(result == p("/home/user"));
UASSERT(removed == p("minetest/bin/../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 6);
UASSERT(result == p("/home"));
UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 7);
#ifdef _WIN32
UASSERT(result == "C:");
#else
UASSERT(result == "");
#endif
UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
/*
Now repeat the test with a trailing delimiter
*/
path = p("/home/user/minetest/bin/..//worlds/world1/");
result = fs::RemoveLastPathComponent(path, &removed, 0);
UASSERT(result == path);
UASSERT(removed == "");
result = fs::RemoveLastPathComponent(path, &removed, 1);
UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
UASSERT(removed == p("world1"));
result = fs::RemoveLastPathComponent(path, &removed, 2);
UASSERT(result == p("/home/user/minetest/bin/.."));
UASSERT(removed == p("worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 3);
UASSERT(result == p("/home/user/minetest/bin"));
UASSERT(removed == p("../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 4);
UASSERT(result == p("/home/user/minetest"));
UASSERT(removed == p("bin/../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 5);
UASSERT(result == p("/home/user"));
UASSERT(removed == p("minetest/bin/../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 6);
UASSERT(result == p("/home"));
UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
result = fs::RemoveLastPathComponent(path, &removed, 7);
#ifdef _WIN32
UASSERT(result == "C:");
#else
UASSERT(result == "");
#endif
|