code:
 /*
Minetest
Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

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 "noise.h"
#include "settings.h"
#include "mapgen_v5.h"
#include "util/sha1.h"
#include "map_settings_manager.h"

class TestMapSettingsManager : public TestBase {
public:
	TestMapSettingsManager() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestMapSettingsManager"; }

	void makeUserConfig(Settings *conf);
	std::string makeMetaFile(bool make_corrupt);

	void runTests(IGameDef *gamedef);

	void testMapSettingsManager();
	void testMapMetaSaveLoad();
	void testMapMetaFailures();
};

static TestMapSettingsManager g_test_instance;

void TestMapSettingsManager::runTests(IGameDef *gamedef)
{
	TEST(testMapSettingsManager);
	TEST(testMapMetaSaveLoad);
	TEST(testMapMetaFailures);
}

////////////////////////////////////////////////////////////////////////////////

void check_noise_params(const NoiseParams *np1, const NoiseParams *np2)
{
	UASSERTEQ(float, np1->offset, np2->offset);
	UASSERTEQ(float, np1->scale, np2->scale);
	UASSERT(np1->spread == np2->spread);
	UASSERTEQ(s32, np1->seed, np2->seed);
	UASSERTEQ(u16, np1->octaves, np2->octaves);
	UASSERTEQ(float, np1->persist, np2->persist);
	UASSERTEQ(float, np1->lacunarity, np2->lacunarity);
	UASSERTEQ(u32, np1->flags, np2->flags);
}

std::string read_file_to_string(const std::string &filepath)
{
	std::string buf;
	FILE *f = fopen(filepath.c_str(), "rb");
	if (!f)
		return "";

	fseek(f, 0, SEEK_END);

	long filesize = ftell(f);
	if (filesize == -1) {
		fclose(f);
		return "";
	}
	rewind(f);

	buf.resize(filesize);

	UASSERTEQ(size_t, fread(&buf[0], 1, filesize, f), 1);

	fclose(f);
	return buf;
}

void TestMapSettingsManager::makeUserConfig(Settings *conf)
{
	conf->set("mg_name", "v7");
	conf->set("seed", "5678");
	conf->set("water_level", "20");
	conf->set("mgv5_np_factor", "0, 12,  (500, 250, 500), 920382, 5, 0.45, 3.0");
	conf->set("mgv5_np_height", "0, 15, (500, 250, 500), 841746,  5, 0.5,  3.0");
	conf->set("mgv5_np_filler_depth", "20, 1, (150, 150, 150), 261, 4, 0.7,  1.0");
	conf->set("mgv5_np_ground", "-43, 40, (80,  80,  80),  983240, 4, 0.55, 2.0");
}

std::string TestMapSettingsManager::makeMetaFile(bool make_corrupt)
{
	std::string metafile = getTestTempFile();

	const char *metafile_contents =
		"mg_name = v5\n"
		"seed = 1234\n"
		"mg_flags = light\n"
		"mgv5_np_filler_depth = 20, 1, (150, 150, 150), 261, 4, 0.7,  1.0\n"
		"mgv5_np_height = 20, 10, (250, 250, 250), 84174,  4, 0.5,  1.0\n";

	FILE *f = fopen(metafile.c_str(), "wb");
	UASSERT(f != NULL);

	fputs(metafile_contents, f);
	if (!make_corrupt)
		fputs("[end_of_params]\n", f);

	fclose(f);

	return metafile;
}

void TestMapSettingsManager::testMapSettingsManager()
{
	Settings user_settings;
	makeUserConfig(&user_settings);

	std::string test_mapmeta_path = makeMetaFile(false);

	MapSettingsManager mgr(&user_settings, test_mapmeta_path);
	std::string value;

	UASSERT(mgr.getMapSetting("mg_name", &value));
	UASSERT(value == "v7");

	// Pretend we're initializing the ServerMap
	UASSERT(mgr.loadMapMeta());

	// Pretend some scripts are requesting mapgen params
	UASSERT(mgr.getMapSetting("mg_name", &value));
	UASSERT(value == "v5");
	UASSERT(mgr.getMapSetting("seed", &value));
	UASSERT(value == "1234");
	UASSERT(mgr.getMapSetting("water_level", &value));
	UASSERT(value == "20");

    // Pretend we have some mapgen settings configured from the scripting
	UASSERT(mgr.setMapSetting("water_level", "15"));
	UASSERT(mgr.setMapSetting("seed", "02468"));
	UASSERT(mgr.setMapSetting("mg_flags", "nolight", true));

	NoiseParams script_np_filler_depth(0, 100, v3f(200, 100, 200), 261, 4, 0.7, 2.0);
	NoiseParams script_np_factor(0, 100, v3f(50, 50, 50), 920381, 3, 0.45, 2.0);
	NoiseParams script_np_height(0, 100, v3f(450, 450, 450), 84174, 4, 0.5, 2.0);
	NoiseParams meta_np_height(20, 10, v3f(250, 250, 250), 84174,  4, 0.5,  1.0);
	NoiseParams user_np_ground(-43, 40, v3f(80,  80,  80),  983240, 4, 0.55, 2.0, NOISE_FLAG_EASED);

	mgr.setMapSettingNoiseParams("mgv5_np_filler_depth", &script_np_filler_depth, true);
	mgr.setMapSettingNoiseParams("mgv5_np_height", &script_np_height);
	mgr.setMapSettingNoiseParams("mgv5_np_factor", &script_np_factor);

	// Now make our Params and see if the values are correctly sourced
	MapgenParams *params = mgr.makeMapgenParams();
	UASSERT(params->mgtype == MAPGEN_V5);
	UASSERT(params->chunksize == 5);
	UASSERT(params->water_level == 15);
	UASSERT(params->seed == 1234);
	UASSERT((params->flags & MG_LIGHT) == 0);

	MapgenV5Params *v5params = (MapgenV5Params *)params;

	check_noise_params(&v5params->np_filler_depth, &script_np_filler_depth);
	check_noise_params(&v5params->np_factor, &script_np_factor);
	check_noise_params(&v5params->np_height, &meta_np_height);
	check_noise_params(&v5params->np_ground, &user_np_ground);

	UASSERT(mgr.setMapSetting("foobar", "25") == false);

	// Pretend the ServerMap is shutting down
	UASSERT(mgr.saveMapMeta());

	// Make sure our interface expectations are met
	UASSERT(mgr.mapgen_params == params);
	UASSERT(mgr.makeMapgenParams() == params);

#if 0
	// TODO(paramat or hmmmm): change this to compare the result against a static file

	// Load the resulting map_meta.txt and make sure it contains what we expect
	unsigned char expected_contents_hash[20] = {
		0x48, 0x3f, 0x88, 0x5a, 0xc0, 0x7a, 0x14, 0x48, 0xa4, 0x71,
		0x78, 0x56, 0x95, 0x2d, 0xdc, 0x6a, 0xf7, 0x61, 0x36, 0x5f
	};

	SHA1 ctx;
	std::string metafile_contents = read_file_to_string(test_mapmeta_path);
	ctx.addBytes(&metafile_contents[0], metafile_contents.size());
	unsigned char *sha1_result = ctx.getDigest();
	int resultdiff = memcmp(sha1_result, expected_contents_hash, 20);
	free(sha1_result);

	UASSERT(!resultdiff);
#endif
}

void TestMapSettingsManager::testMapMetaSaveLoad()
{
	Settings conf;
	std::string path = getTestTempDirectory()
		+ DIR_DELIM + "foobar" + DIR_DELIM + "map_meta.txt";

	// Create a set of mapgen params and save them to map meta
	conf.set("seed", "12345");
	conf.set("water_level", "5");
	MapSettingsManager mgr1(&conf, path);
	MapgenParams *params1 = mgr1.makeMapgenParams();
	UASSERT(params1);
	UASSERT(mgr1.saveMapMeta());

	// Now try loading the map meta to mapgen params
	conf.set("seed", "67890");
	conf.set("water_level", "32");
	MapSettingsManager mgr2(&conf, path);
	UASSERT(mgr2.loadMapMeta());
	MapgenParams *params2 = mgr2.makeMapgenParams();
	UASSERT(params2);

	// Check that both results are correct
	UASSERTEQ(u64, params1->seed, 12345);
	UASSERTEQ(s16, params1->water_level, 5);
	UASSERTEQ(u64, params2->seed, 12345);
	UASSERTEQ(s16, params2->water_level, 5);
}

void TestMapSettingsManager::testMapMetaFailures()
{
	std::string test_mapmeta_path;
	Settings conf;

	// Check to see if it'll fail on a non-existent map meta file
	test_mapmeta_path = "woobawooba/fgdfg/map_meta.txt";
	UASSERT(!fs::PathExists(test_mapmeta_path));

	MapSettingsManager mgr1(&conf, test_mapmeta_path);
	UASSERT(!mgr1.loadMapMeta());

	// Check to see if it'll fail on a corrupt map meta file
	test_mapmeta_path = makeMetaFile(true);
	UASSERT(fs::PathExists(test_mapmeta_path));

	MapSettingsManager mgr2(&conf, test_mapmeta_path);
	UASSERT(!mgr2.loadMapMeta());
}


code:
/*
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 "gamedef.h"
#include "nodedef.h"
#include "content_mapnode.h"

class TestMapNode : public TestBase
{
public:
	TestMapNode() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestMapNode"; }

	void runTests(IGameDef *gamedef);

	void testNodeProperties(INodeDefManager *nodedef);
};

static TestMapNode g_test_instance;

void TestMapNode::runTests(IGameDef *gamedef)
{
	TEST(testNodeProperties, gamedef->getNodeDefManager());
}

////////////////////////////////////////////////////////////////////////////////

void TestMapNode::testNodeProperties(INodeDefManager *nodedef)
{
	MapNode n(CONTENT_AIR);

	UASSERT(n.getContent() == CONTENT_AIR);
	UASSERT(n.getLight(LIGHTBANK_DAY, nodedef) == 0);
	UASSERT(n.getLight(LIGHTBANK_NIGHT, nodedef) == 0);

	// Transparency
	n.setContent(CONTENT_AIR);
	UASSERT(nodedef->get(n).light_propagates == true);
}

repo_name: pgimeno/minetest

license: unknown


code:
/*
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 <sstream>

#include "gamedef.h"
#include "nodedef.h"
#include "network/networkprotocol.h"

class TestNodeDef : public TestBase
{
public:
	TestNodeDef() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestNodeDef"; }

	void runTests(IGameDef *gamedef);

	void testContentFeaturesSerialization();
};

static TestNodeDef g_test_instance;

void TestNodeDef::runTests(IGameDef *gamedef)
{
	TEST(testContentFeaturesSerialization);
}

////////////////////////////////////////////////////////////////////////////////

void TestNodeDef::testContentFeaturesSerialization()
{
	ContentFeatures f;

	f.name = "default:stone";
	for (int i = 0; i < 6; i++)
		f.tiledef[i].name = "default_stone.png";
	f.is_ground_content = true;

	std::ostringstream os(std::ios::binary);
	f.serialize(os, LATEST_PROTOCOL_VERSION);
	// verbosestream<<"Test ContentFeatures size: "<<os.str().size()<<std::endl;

	std::istringstream is(os.str(), std::ios::binary);
	ContentFeatures f2;
	f2.deSerialize(is);

	UASSERT(f.walkable == f2.walkable);
	UASSERT(f.node_box.type == f2.node_box.type);
}


code:
/*
Minetest
Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

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 "util/numeric.h"
#include "exceptions.h"
#include "gamedef.h"
#include "nodedef.h"

#include <algorithm>

class TestNodeResolver : public TestBase {
public:
	TestNodeResolver() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestNodeResolver"; }

	void runTests(IGameDef *gamedef);

	void testNodeResolving(IWritableNodeDefManager *ndef);
	void testPendingResolveCancellation(IWritableNodeDefManager *ndef);
	void testDirectResolveMethod(IWritableNodeDefManager *ndef);
	void testNoneResolveMethod(IWritableNodeDefManager *ndef);
};

static TestNodeResolver g_test_instance;

void TestNodeResolver::runTests(IGameDef *gamedef)
{
	IWritableNodeDefManager *ndef =
		(IWritableNodeDefManager *)gamedef->getNodeDefManager();

	ndef->resetNodeResolveState();
	TEST(testNodeResolving, ndef);

	ndef->resetNodeResolveState();
	TEST(testPendingResolveCancellation, ndef);
}

class Foobar : public NodeResolver {
public:
	void resolveNodeNames();

	content_t test_nr_node1;
	content_t test_nr_node2;
	content_t test_nr_node3;
	content_t test_nr_node4;
	content_t test_nr_node5;
	std::vector<content_t> test_nr_list;
	std::vector<content_t> test_nr_list_group;
	std::vector<content_t> test_nr_list_required;
	std::vector<content_t> test_nr_list_empty;
};

class Foobaz : public NodeResolver {
public:
	void resolveNodeNames();

	content_t test_content1;
	content_t test_content2;
};

////////////////////////////////////////////////////////////////////////////////

void Foobar::resolveNodeNames()
{
	UASSERT(getIdFromNrBacklog(&test_nr_node1, "", CONTENT_IGNORE) == true);
	UASSERT(getIdsFromNrBacklog(&test_nr_list) == true);
	UASSERT(getIdsFromNrBacklog(&test_nr_list_group) == true);
	UASSERT(getIdsFromNrBacklog(&test_nr_list_required,
		true, CONTENT_AIR) == false);
	UASSERT(getIdsFromNrBacklog(&test_nr_list_empty) == true);

	UASSERT(getIdFromNrBacklog(&test_nr_node2, "", CONTENT_IGNORE) == true);
	UASSERT(getIdFromNrBacklog(&test_nr_node3,
		"default:brick", CONTENT_IGNORE) == true);
	UASSERT(getIdFromNrBacklog(&test_nr_node4,
		"default:gobbledygook", CONTENT_AIR) == false);
	UASSERT(getIdFromNrBacklog(&test_nr_node5, "", CONTENT_IGNORE) == false);
}

void Foobaz::resolveNodeNames()
{
	UASSERT(getIdFromNrBacklog(&test_content1, "", CONTENT_IGNORE) == true);
	UASSERT(getIdFromNrBacklog(&test_content2, "", CONTENT_IGNORE) == false);
}

void TestNodeResolver::testNodeResolving(IWritableNodeDefManager *ndef)
{
	Foobar foobar;
	size_t i;

	foobar.m_nodenames.push_back("default:torch");

	foobar.m_nodenames.push_back("default:dirt_with_grass");
	foobar.m_nodenames.push_back("default:water");
	foobar.m_nodenames.push_back("default:abloobloobloo");
	foobar.m_nodenames.push_back("default:stone");
	foobar.m_nodenames.push_back("default:shmegoldorf");
	foobar.m_nnlistsizes.push_back(5);

	foobar.m_nodenames.push_back("group:liquids");
	foobar.m_nnlistsizes.push_back(1);

	foobar.m_nodenames.push_back("default:warf");
	foobar.m_nodenames.push_back("default:stone");
	foobar.m_nodenames.push_back("default:bloop");
	foobar.m_nnlistsizes.push_back(3);

	foobar.m_nnlistsizes.push_back(0);

	foobar.m_nodenames.push_back("default:brick");
	foobar.m_nodenames.push_back("default:desert_stone");
	foobar.m_nodenames.push_back("default:shnitzle");

	ndef->pendNodeResolve(&foobar);
	UASSERT(foobar.m_ndef == ndef);

	ndef->setNodeRegistrationStatus(true);
	ndef->runNodeResolveCallbacks();

	// Check that we read single nodes successfully
	UASSERTEQ(content_t, foobar.test_nr_node1, t_CONTENT_TORCH);
	UASSERTEQ(content_t, foobar.test_nr_node2, t_CONTENT_BRICK);
	UASSERTEQ(content_t, foobar.test_nr_node3, t_CONTENT_BRICK);
	UASSERTEQ(content_t, foobar.test_nr_node4, CONTENT_AIR);
	UASSERTEQ(content_t, foobar.test_nr_node5, CONTENT_IGNORE);

	// Check that we read all the regular list items
	static const content_t expected_test_nr_list[] = {
		t_CONTENT_GRASS,
		t_CONTENT_WATER,
		t_CONTENT_STONE,
	};
	UASSERTEQ(size_t, foobar.test_nr_list.size(), 3);
	for (i = 0; i != foobar.test_nr_list.size(); i++)
		UASSERTEQ(content_t, foobar.test_nr_list[i], expected_test_nr_list[i]);

	// Check that we read all the list items that were from a group entry
	static const content_t expected_test_nr_list_group[] = {
		t_CONTENT_WATER,
		t_CONTENT_LAVA,
	};
	UASSERTEQ(size_t, foobar.test_nr_list_group.size(), 2);
	for (i = 0; i != foobar.test_nr_list_group.size(); i++) {
		UASSERT(CONTAINS(foobar.test_nr_list_group,
			expected_test_nr_list_group[i]));
	}

	// Check that we read all the items we're able to in a required list
	static const content_t expected_test_nr_list_required[] = {
		CONTENT_AIR,
		t_CONTENT_STONE,
		CONTENT_AIR,
	};
	UASSERTEQ(size_t, foobar.test_nr_list_required.size(), 3);
	for (i = 0; i != foobar.test_nr_list_required.size(); i++)
		UASSERTEQ(content_t, foobar.test_nr_list_required[i],
			expected_test_nr_list_required[i]);

	// Check that the edge case of 0 is successful
	UASSERTEQ(size_t, foobar.test_nr_list_empty.size(), 0);
}

void TestNodeResolver::testPendingResolveCancellation(IWritableNodeDefManager *ndef)
{
	Foobaz foobaz1;
	foobaz1.test_content1 = 1234;
	foobaz1.test_content2 = 5678;
	foobaz1.m_nodenames.push_back("default:dirt_with_grass");
	foobaz1.m_nodenames.push_back("default:abloobloobloo");
	ndef->pendNodeResolve(&foobaz1);

	Foobaz foobaz2;
	foobaz2.test_content1 = 1234;
	foobaz2.test_content2 = 5678;
	foobaz2.m_nodenames.push_back("default:dirt_with_grass");
	foobaz2.m_nodenames.push_back("default:abloobloobloo");
	ndef->pendNodeResolve(&foobaz2);

	ndef->cancelNodeResolveCallback(&foobaz1);

	ndef->setNodeRegistrationStatus(true);
	ndef->runNodeResolveCallbacks();

	UASSERT(foobaz1.test_content1 == 1234);
	UASSERT(foobaz1.test_content2 == 5678);
	UASSERT(foobaz2.test_content1 == t_CONTENT_GRASS);
	UASSERT(foobaz2.test_content2 == CONTENT_IGNORE);
}


code:
/*
Minetest
Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

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 "exceptions.h"
#include "noise.h"

class TestNoise : public TestBase {
public:
	TestNoise() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestNoise"; }

	void runTests(IGameDef *gamedef);

	void testNoise2dPoint();
	void testNoise2dBulk();
	void testNoise3dPoint();
	void testNoise3dBulk();
	void testNoiseInvalidParams();

	static const float expected_2d_results[10 * 10];
	static const float expected_3d_results[10 * 10 * 10];
};

static TestNoise g_test_instance;

void TestNoise::runTests(IGameDef *gamedef)
{
	TEST(testNoise2dPoint);
	TEST(testNoise2dBulk);
	TEST(testNoise3dPoint);
	TEST(testNoise3dBulk);
	TEST(testNoiseInvalidParams);
}

////////////////////////////////////////////////////////////////////////////////

void TestNoise::testNoise2dPoint()
{
	NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9,  5, 0.6, 2.0);

	u32 i = 0;
	for (u32 y = 0; y != 10; y++)
	for (u32 x = 0; x != 10; x++, i++) {
		float actual   = NoisePerlin2D(&np_normal, x, y, 1337);
		float expected = expected_2d_results[i];
		UASSERT(fabs(actual - expected) <= 0.00001);
	}
}

void TestNoise::testNoise2dBulk()
{
	NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9,  5, 0.6, 2.0);
	Noise noise_normal_2d(&np_normal, 1337, 10, 10);
	float *noisevals = noise_normal_2d.perlinMap2D(0, 0, NULL);

	for (u32 i = 0; i != 10 * 10; i++) {
		float actual   = noisevals[i];
		float expected = expected_2d_results[i];
		UASSERT(fabs(actual - expected) <= 0.00001);
	}
}

void TestNoise::testNoise3dPoint()
{
	NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9,  5, 0.6, 2.0);

	u32 i = 0;
	for (u32 z = 0; z != 10; z++)
	for (u32 y = 0; y != 10; y++)
	for (u32 x = 0; x != 10; x++, i++) {
		float actual   = NoisePerlin3D(&np_normal, x, y, z, 1337);
		float expected = expected_3d_results[i];
		UASSERT(fabs(actual - expected) <= 0.00001);
	}
}

void TestNoise::testNoise3dBulk()
{
	NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9, 5, 0.6, 2.0);
	Noise noise_normal_3d(&np_normal, 1337, 10, 10, 10);
	float *noisevals = noise_normal_3d.perlinMap3D(0, 0, 0, NULL);

	for (u32 i = 0; i != 10 * 10 * 10; i++) {
		float actual   = noisevals[i];
		float expected = expected_3d_results[i];
		UASSERT(fabs(actual - expected) <= 0.00001);
	}
}

void TestNoise::testNoiseInvalidParams()
{
	bool exception_thrown = false;

	try {
		NoiseParams np_highmem(4, 70, v3f(1, 1, 1), 5, 60, 0.7, 10.0);
		Noise noise_highmem_3d(&np_highmem, 1337, 200, 200, 200);
		noise_highmem_3d.perlinMap3D(0, 0, 0, NULL);
	} catch (InvalidNoiseParamsException) {
		exception_thrown = true;
	}

	UASSERT(exception_thrown);
}

const float TestNoise::expected_2d_results[10 * 10] = {
	19.11726, 18.49626, 16.48476, 15.02135, 14.75713, 16.26008, 17.54822,
	18.06860, 18.57016, 18.48407, 18.49649, 17.89160, 15.94162, 14.54901,
	14.31298, 15.72643, 16.94669, 17.55494, 18.58796, 18.87925, 16.08101,
	15.53764, 13.83844, 12.77139, 12.73648, 13.95632, 14.97904, 15.81829,
	18.37694, 19.73759, 13.19182, 12.71924, 11.34560, 10.78025, 11.18980,
	12.52303, 13.45012, 14.30001, 17.43298, 19.15244, 10.93217, 10.48625,
	 9.30923,  9.18632, 10.16251, 12.11264, 13.19697, 13.80801, 16.39567,
	17.66203, 10.40222,  9.86070,  8.47223,  8.45471, 10.04780, 13.54730,
	15.33709, 15.48503, 16.46177, 16.52508, 10.80333, 10.19045,  8.59420,
	 8.47646, 10.22676, 14.43173, 16.48353, 16.24859, 16.20863, 15.52847,
	11.01179, 10.45209,  8.98678,  8.83986, 10.43004, 14.46054, 16.29387,
	15.73521, 15.01744, 13.85542, 10.55201, 10.33375,  9.85102, 10.07821,
	11.58235, 15.62046, 17.35505, 16.13181, 12.66011,  9.51853, 11.50994,
	11.54074, 11.77989, 12.29790, 13.76139, 17.81982, 19.49008, 17.79470,
	12.34344,  7.78363,
};

const float TestNoise::expected_3d_results[10 * 10 * 10] = {
	19.11726, 18.01059, 16.90392, 15.79725, 16.37154, 17.18597, 18.00040,
	18.33467, 18.50889, 18.68311, 17.85386, 16.90585, 15.95785, 15.00985,
	15.61132, 16.43415, 17.25697, 17.95415, 18.60942, 19.26471, 16.59046,
	15.80112, 15.01178, 14.22244, 14.85110, 15.68232, 16.51355, 17.57361,
	18.70996, 19.84631, 15.32705, 14.69638, 14.06571, 13.43504, 14.09087,
	14.93050, 15.77012, 17.19309, 18.81050, 20.42790, 15.06729, 14.45855,
	13.84981, 13.24107, 14.39364, 15.79782, 17.20201, 18.42640, 19.59085,
	20.75530, 14.95090, 14.34456, 13.73821, 13.13187, 14.84825, 16.89645,
	18.94465, 19.89025, 20.46832, 21.04639, 14.83452, 14.23057, 13.62662,
	13.02267, 15.30287, 17.99508, 20.68730, 21.35411, 21.34580, 21.33748,
	15.39817, 15.03590, 14.67364, 14.31137, 16.78242, 19.65824, 22.53405,
	22.54626, 21.60395, 20.66164, 16.18850, 16.14768, 16.10686, 16.06603,
	18.60362, 21.50956, 24.41549, 23.64784, 21.65566, 19.66349, 16.97884,
	17.25946, 17.54008, 17.82069, 20.42482, 23.36088, 26.29694, 24.74942,
	21.70738, 18.66534, 18.78506, 17.51834, 16.25162, 14.98489, 15.14217,
	15.50287, 15.86357, 16.40597, 17.00895, 17.61193, 18.20160, 16.98795,
	15.77430, 14.56065, 14.85059, 15.35533, 15.86007, 16.63399, 17.49763,
	18.36128, 17.61814, 16.45757, 15.29699, 14.13641, 14.55902, 15.20779,
	15.85657, 16.86200, 17.98632, 19.11064, 17.03468, 15.92718, 14.81968,
	13.71218, 14.26744, 15.06026, 15.85306, 17.09001, 18.47501, 19.86000,
	16.67870, 15.86256, 15.04641, 14.23026, 15.31397, 16.66909, 18.02420,
	18.89042, 19.59369, 20.29695, 16.35522, 15.86447, 15.37372, 14.88297,
	16.55165, 18.52883, 20.50600, 20.91547, 20.80237, 20.68927, 16.03174,
	15.86639, 15.70103, 15.53568, 17.78933, 20.38857, 22.98780, 22.94051,
	22.01105, 21.08159, 16.42434, 16.61407, 16.80381, 16.99355, 19.16133,
	21.61169, 24.06204, 23.65252, 22.28970, 20.92689, 17.05562, 17.61035,
	18.16508, 18.71981, 20.57809, 22.62260, 24.66711, 23.92686, 22.25835,
	20.58984, 17.68691, 18.60663, 19.52635, 20.44607, 21.99486, 23.63352,
	25.27217, 24.20119, 22.22699, 20.25279, 18.45285, 17.02608, 15.59931,
	14.17254, 13.91279, 13.81976, 13.72674, 14.47727, 15.50900, 16.54073,
	18.54934, 17.07005, 15.59076, 14.11146, 14.08987, 14.27651, 14.46316,
	15.31383, 16.38584, 17.45785, 18.64582, 17.11401, 15.58220, 14.05039,
	14.26694, 14.73326, 15.19958, 16.15038, 17.26268, 18.37498, 18.74231,
	17.15798, 15.57364, 13.98932, 14.44402, 15.19001, 15.93600, 16.98694,
	18.13952, 19.29210, 18.29012, 17.26656, 16.24301, 15.21946, 16.23430,
	17.54035, 18.84639, 19.35445, 19.59653, 19.83860, 17.75954, 17.38438,
	17.00923, 16.63407, 18.25505, 20.16120, 22.06734, 21.94068, 21.13642,
	20.33215, 17.22896, 17.50220, 17.77544, 18.04868, 20.27580, 22.78205,
	25.28829, 24.52691, 22.67631, 20.82571, 17.45050, 18.19224, 18.93398,
	19.67573, 21.54024, 23.56514, 25.59004, 24.75878, 22.97546, 21.19213,
	17.92274, 19.07302, 20.22330, 21.37358, 22.55256, 23.73565, 24.91873,
	24.20587, 22.86103, 21.51619, 18.39499, 19.95381, 21.51263, 23.07145,
	23.56490, 23.90615, 24.24741, 23.65296, 22.74660, 21.84024, 18.12065,
	16.53382, 14.94700, 13.36018, 12.68341, 12.13666, 11.58990, 12.54858,
	14.00906, 15.46955, 18.89708, 17.15214, 15.40721, 13.66227, 13.32914,
	13.19769, 13.06625, 13.99367, 15.27405, 16.55443, 19.67351, 17.77046,
	15.86741, 13.96436, 13.97486, 14.25873, 14.54260, 15.43877, 16.53904,
	17.63931, 20.44994, 18.38877, 16.32761, 14.26645, 14.62059, 15.31977,
	16.01895, 16.88387, 17.80403, 18.72419, 19.90153, 18.67057, 17.43962,
	16.20866, 17.15464, 18.41161, 19.66858, 19.81848, 19.59936, 19.38024,
	19.16386, 18.90429, 18.64473, 18.38517, 19.95845, 21.79357, 23.62868,
	22.96589, 21.47046, 19.97503, 18.42618, 19.13802, 19.84985, 20.56168,
	22.76226, 25.17553, 27.58879, 26.11330, 23.34156, 20.56982, 18.47667,
	19.77041, 21.06416, 22.35790, 23.91914, 25.51859, 27.11804, 25.86504,
	23.66121, 21.45738, 18.78986, 20.53570, 22.28153, 24.02736, 24.52704,
	24.84869, 25.17035, 24.48488, 23.46371, 22.44254, 19.10306, 21.30098,
	23.49890, 25.69682, 25.13494, 24.17879, 23.22265, 23.10473, 23.26621,
	23.42769, 17.93453, 16.72707, 15.51962, 14.31216, 12.96039, 11.58800,
	10.21561, 11.29675, 13.19573, 15.09471, 18.05853, 16.85308, 15.64762,
	14.44216, 13.72634, 13.08047, 12.43459, 13.48912, 15.11045, 16.73179,
	18.18253, 16.97908, 15.77562, 14.57217, 14.49229, 14.57293, 14.65357,
	15.68150, 17.02518, 18.36887, 18.30654, 17.10508, 15.90363, 14.70217,
	15.25825, 16.06540, 16.87255, 17.87387, 18.93991, 20.00595, 17.54117,
	17.32369, 17.10622, 16.88875, 18.07494, 19.46166, 20.84837, 21.12988,
	21.04298, 20.95609, 16.64874, 17.55554, 18.46234, 19.36913, 21.18461,
	23.12989, 25.07517, 24.53784, 23.17297, 21.80810, 15.75632, 17.78738,
	19.81845, 21.84951, 24.29427, 26.79812, 29.30198, 27.94580, 25.30295,
	22.66010, 15.98046, 18.43027, 20.88008, 23.32989, 25.21976, 27.02964,
	28.83951, 27.75863, 25.71416, 23.66970, 16.57679, 19.21017, 21.84355,
	24.47693, 25.41719, 26.11557, 26.81396, 26.37308, 25.55245, 24.73182,
	17.17313, 19.99008, 22.80702, 25.62397, 25.61462, 25.20151, 24.78840,
	24.98753, 25.39074, 25.79395, 17.76927, 17.01824, 16.26722, 15.51620,
	13.45256, 11.20141,  8.95025, 10.14162, 12.48049, 14.81936, 17.05051,
	16.49955, 15.94860, 15.39764, 14.28896, 13.10061, 11.91225, 13.10109,
	15.08232, 17.06355, 16.33175, 15.98086, 15.62998, 15.27909, 15.12537,
	14.99981, 14.87425, 16.06056, 17.68415, 19.30775, 15.61299, 15.46217,
	15.31136, 15.16054, 15.96177, 16.89901, 17.83625, 19.02003, 20.28599,
	21.55194, 14.61341, 15.58383, 16.55426, 17.52469, 18.99524, 20.53725,
	22.07925, 22.56233, 22.69243, 22.82254, 13.57371, 15.79697, 18.02024,
	20.24351, 22.34258, 24.42392, 26.50526, 26.18790, 25.07097, 23.95404,
	12.53401, 16.01011, 19.48622, 22.96232, 25.68993, 28.31060, 30.93126,
	29.81347, 27.44951, 25.08555, 12.98106, 16.67323, 20.36540, 24.05756,
	26.36633, 28.47748, 30.58862, 29.76471, 27.96244, 26.16016, 13.92370,
	17.48634, 21.04897, 24.61161, 26.15244, 27.40443, 28.65643, 28.49117,
	27.85349, 27.21581, 14.86633, 18.29944, 21.73255, 25.16566, 25.93854,
	26.33138, 26.72423, 27.21763, 27.74455, 28.27147, 17.60401, 17.30942,
	17.01482, 16.72023, 13.94473, 10.81481,  7.68490,  8.98648, 11.76524,
	14.54400, 16.04249, 16.14603, 16.24958, 16.35312, 14.85158, 13.12075,
	11.38991, 12.71305, 15.05418, 17.39531, 14.48097, 14.98265, 15.48433,
	15.98602, 15.75844, 15.42668, 15.09493, 16.43962, 18.34312, 20.24663,
	12.91945, 13.81927, 14.71909, 15.61891, 16.66530, 17.73262, 18.79995,
	20.16619, 21.63206, 23.09794, 11.68565, 13.84398, 16.00230, 18.16062,
	19.91554, 21.61284, 23.31013, 23.99478, 24.34188, 24.68898, 10.49868,
	14.03841, 17.57814, 21.11788, 23.50056, 25.71795, 27.93534, 27.83796,
	26.96897, 26.09999,  9.31170, 14.23284, 19.15399, 24.07513, 27.08558,
	29.82307, 32.56055, 31.68113, 29.59606, 27.51099,  9.98166, 14.91619,
	19.85071, 24.78524, 27.51291, 29.92532, 32.33773, 31.77077, 30.21070,
	28.65063, 11.27060, 15.76250, 20.25440, 24.74629, 26.88768, 28.69329,
	30.49889, 30.60925, 30.15453, 29.69981, 12.55955, 16.60881, 20.65808,
	24.70735, 26.26245, 27.46126, 28.66005, 29.44773, 30.09835, 30.74898,
	15.20134, 15.53016, 15.85898, 16.18780, 13.53087, 10.44740,  7.36393,
	 8.95806, 12.11139, 15.26472, 13.87432, 14.52378, 15.17325, 15.82272,
	14.49093, 12.87611, 11.26130, 12.73342, 15.23453, 17.73563, 12.54730,
	13.51741, 14.48752, 15.45763, 15.45100, 15.30483, 15.15867, 16.50878,
	18.35766, 20.20654, 11.22027, 12.51103, 13.80179, 15.09254, 16.41106,
	17.73355, 19.05603, 20.28415, 21.48080, 22.67745, 10.27070, 12.53633,
	14.80195, 17.06758, 19.04654, 20.98454, 22.92254, 23.63840, 23.94687,
	24.25534,  9.37505, 12.70901, 16.04297, 19.37693, 21.92136, 24.35300,
	26.78465, 26.93249, 26.31907, 25.70565,  8.47939, 12.88168, 17.28398,
	21.68627, 24.79618, 27.72146, 30.64674, 30.22658, 28.69127, 27.15597,
	 9.77979, 13.97583, 18.17186, 22.36790, 25.18828, 27.81215, 30.43601,
	30.34293, 29.34420, 28.34548, 11.81220, 15.37712, 18.94204, 22.50695,
	24.75282, 26.81024, 28.86766, 29.40003, 29.42404, 29.44806, 13.84461,
	16.77841, 19.71221, 22.64601, 24.31735, 25.80833, 27.29932, 28.45713,
	29.50388, 30.55064, 12.05287, 13.06077, 14.06866, 15.07656, 12.81500,
	10.08638,  7.35776,  9.30520, 12.81134, 16.31747, 11.31943, 12.47863,
	13.63782, 14.79702, 13.82253, 12.54323, 11.26392, 12.88993, 15.48436,
	18.07880, 10.58600, 11.89649, 13.20698, 14.51747, 14.83005, 15.00007,
	15.17009, 16.47465, 18.15739, 19.84013,  9.85256, 11.31435, 12.77614,
	14.23793, 15.83757, 17.45691, 19.07625, 20.05937, 20.83042, 21.60147,
	 9.36002, 11.37275, 13.38548, 15.39822, 17.58109, 19.78828, 21.99546,
	22.68573, 22.87036, 23.05500,  8.90189, 11.52266, 14.14343, 16.76420,
	19.42976, 22.10172, 24.77368, 25.17519, 24.81987, 24.46455,  8.44375,
	11.67256, 14.90137, 18.13018, 21.27843, 24.41516, 27.55190, 27.66464,
	26.76937, 25.87411, 10.51042, 13.30769, 16.10496, 18.90222, 21.70659,
	24.51197, 27.31734, 27.77045, 27.43945, 27.10846, 13.41869, 15.43789,
	17.45709, 19.47628, 21.66124, 23.86989, 26.07853, 27.08170, 27.68305,
	28.28440, 16.32697, 17.56809, 18.80922, 20.05033, 21.61590, 23.22781,
	24.83972, 26.39296, 27.92665, 29.46033,  8.90439, 10.59137, 12.27835,
	13.96532, 12.09914,  9.72536,  7.35159,  9.65235, 13.51128, 17.37022,
	 8.76455, 10.43347, 12.10239, 13.77132, 13.15412, 12.21033, 11.26655,
	13.04643, 15.73420, 18.42198,  8.62470, 10.27557, 11.92644, 13.57731,
	14.20910, 14.69531, 15.18151, 16.44051, 17.95712, 19.47373,  8.48485,
	10.11767, 11.75049, 13.38331, 15.26408, 17.18027, 19.09647, 19.83460,
	20.18004, 20.52548,  8.44933, 10.20917, 11.96901, 13.72885, 16.11565,
	18.59202, 21.06838, 21.73307, 21.79386, 21.85465,  8.42872, 10.33631,
	12.24389, 14.15147, 16.93816, 19.85044, 22.76272, 23.41788, 23.32067,
	23.22346,  8.40812, 10.46344, 12.51877, 14.57409, 17.76068, 21.10886,
	24.45705, 25.10269, 24.84748, 24.59226, 11.24106, 12.63955, 14.03805,
	15.43654, 18.22489, 21.21178, 24.19868, 25.19796, 25.53469, 25.87143,
	15.02519, 15.49866, 15.97213, 16.44560, 18.56967, 20.92953, 23.28940,
	24.76337, 25.94205, 27.12073, 18.80933, 18.35777, 17.90622, 17.45466,
	18.91445, 20.64729, 22.38013, 24.32880, 26.34941, 28.37003,
};


code:
/*
Minetest
Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

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 "exceptions.h"
#include "objdef.h"

class TestObjDef : public TestBase
{
public:
	TestObjDef() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestObjDef"; }

	void runTests(IGameDef *gamedef);

	void testHandles();
	void testAddGetSetClear();
};

static TestObjDef g_test_instance;

void TestObjDef::runTests(IGameDef *gamedef)
{
	TEST(testHandles);
	TEST(testAddGetSetClear);
}

////////////////////////////////////////////////////////////////////////////////

void TestObjDef::testHandles()
{
	u32 uid = 0;
	u32 index = 0;
	ObjDefType type = OBJDEF_GENERIC;

	ObjDefHandle handle = ObjDefManager::createHandle(9530, OBJDEF_ORE, 47);

	UASSERTEQ(ObjDefHandle, 0xAF507B55, handle);

	UASSERT(ObjDefManager::decodeHandle(handle, &index, &type, &uid));

	UASSERTEQ(u32, 9530, index);
	UASSERTEQ(u32, 47, uid);
	UASSERTEQ(ObjDefHandle, OBJDEF_ORE, type);
}

void TestObjDef::testAddGetSetClear()
{
	ObjDefManager testmgr(NULL, OBJDEF_GENERIC);
	ObjDefHandle hObj0, hObj1, hObj2, hObj3;
	ObjDef *obj0, *obj1, *obj2, *obj3;

	UASSERTEQ(ObjDefType, testmgr.getType(), OBJDEF_GENERIC);

	obj0 = new ObjDef;
	obj0->name = "foobar";
	hObj0 = testmgr.add(obj0);
	UASSERT(hObj0 != OBJDEF_INVALID_HANDLE);
	UASSERTEQ(u32, obj0->index, 0);

	obj1 = new ObjDef;
	obj1->name = "FooBaz";
	hObj1 = testmgr.add(obj1);
	UASSERT(hObj1 != OBJDEF_INVALID_HANDLE);
	UASSERTEQ(u32, obj1->index, 1);

	obj2 = new ObjDef;
	obj2->name = "asdf";
	hObj2 = testmgr.add(obj2);
	UASSERT(hObj2 != OBJDEF_INVALID_HANDLE);
	UASSERTEQ(u32, obj2->index, 2);

	obj3 = new ObjDef;
	obj3->name = "foobaz";
	hObj3 = testmgr.add(obj3);
	UASSERT(hObj3 == OBJDEF_INVALID_HANDLE);

	UASSERTEQ(size_t, testmgr.getNumObjects(), 3);

	UASSERT(testmgr.get(hObj0) == obj0);
	UASSERT(testmgr.getByName("FOOBAZ") == obj1);

	UASSERT(testmgr.set(hObj0, obj3) == obj0);
	UASSERT(testmgr.get(hObj0) == obj3);
	delete obj0;

	testmgr.clear();
	UASSERTEQ(size_t, testmgr.getNumObjects(), 0);
}


code:
/*
Minetest
Copyright (C) 2010-2016 nerzhul, Loic Blot <loic.blot@unix-experience.fr>

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 "exceptions.h"
#include "remoteplayer.h"
#include "content_sao.h"
#include "server.h"

class TestPlayer : public TestBase
{
public:
	TestPlayer() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestPlayer"; }

	void runTests(IGameDef *gamedef);
};

static TestPlayer g_test_instance;

void TestPlayer::runTests(IGameDef *gamedef)
{
}


code:
/*
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 "profiler.h"

class TestProfiler : public TestBase
{
public:
	TestProfiler() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestProfiler"; }

	void runTests(IGameDef *gamedef);

	void testProfilerAverage();
};

static TestProfiler g_test_instance;

void TestProfiler::runTests(IGameDef *gamedef)
{
	TEST(testProfilerAverage);
}

////////////////////////////////////////////////////////////////////////////////

void TestProfiler::testProfilerAverage()
{
	Profiler p;

	p.avg("Test1", 1.f);
	UASSERT(p.getValue("Test1") == 1.f);

	p.avg("Test1", 2.f);
	UASSERT(p.getValue("Test1") == 1.5f);

	p.avg("Test1", 3.f);
	UASSERT(p.getValue("Test1") == 2.f);

	p.avg("Test1", 486.f);
	UASSERT(p.getValue("Test1") == 123.f);

	p.avg("Test1", 8);
	UASSERT(p.getValue("Test1") == 100.f);

	p.avg("Test1", 700);
	UASSERT(p.getValue("Test1") == 200.f);

	p.avg("Test1", 10000);
	UASSERT(p.getValue("Test1") == 1600.f);

	p.avg("Test2", 123.56);
	p.avg("Test2", 123.58);

	UASSERT(p.getValue("Test2") == 123.57f);
}


code:
 /*
Minetest
Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

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 "util/numeric.h"
#include "exceptions.h"
#include "noise.h"

class TestRandom : public TestBase {
public:
	TestRandom() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestRandom"; }

	void runTests(IGameDef *gamedef);

	void testPseudoRandom();
	void testPseudoRandomRange();
	void testPcgRandom();
	void testPcgRandomRange();
	void testPcgRandomBytes();
	void testPcgRandomNormalDist();

	static const int expected_pseudorandom_results[256];
	static const u32 expected_pcgrandom_results[256];
	static const u8 expected_pcgrandom_bytes_result[24];
	static const u8 expected_pcgrandom_bytes_result2[24];
};

static TestRandom g_test_instance;

void TestRandom::runTests(IGameDef *gamedef)
{
	TEST(testPseudoRandom);
	TEST(testPseudoRandomRange);
	TEST(testPcgRandom);
	TEST(testPcgRandomRange);
	TEST(testPcgRandomBytes);
	TEST(testPcgRandomNormalDist);
}

////////////////////////////////////////////////////////////////////////////////

void TestRandom::testPseudoRandom()
{
	PseudoRandom pr(814538);

	for (u32 i = 0; i != 256; i++)
		UASSERTEQ(int, pr.next(), expected_pseudorandom_results[i]);
}

void TestRandom::testPseudoRandomRange()
{
	PseudoRandom pr((int)time(NULL));

	EXCEPTION_CHECK(PrngException, pr.range(2000, 6000));
	EXCEPTION_CHECK(PrngException, pr.range(5, 1));

	for (u32 i = 0; i != 32768; i++) {
		int min = (pr.next() % 3000) - 500;
		int max = (pr.next() % 3000) - 500;
		if (min > max)
			SWAP(int, min, max);

		int randval = pr.range(min, max);
		UASSERT(randval >= min);
		UASSERT(randval <= max);
	}
}

void TestRandom::testPcgRandom()
{
	PcgRandom pr(814538, 998877);

	for (u32 i = 0; i != 256; i++)
		UASSERTEQ(u32, pr.next(), expected_pcgrandom_results[i]);
}

void TestRandom::testPcgRandomRange()
{
	PcgRandom pr((int)time(NULL));

	EXCEPTION_CHECK(PrngException, pr.range(5, 1));

	// Regression test for bug 3027
	pr.range(pr.RANDOM_MIN, pr.RANDOM_MAX);

	for (u32 i = 0; i != 32768; i++) {
		int min = (pr.next() % 3000) - 500;
		int max = (pr.next() % 3000) - 500;
		if (min > max)
			SWAP(int, min, max);

		int randval = pr.range(min, max);
		UASSERT(randval >= min);
		UASSERT(randval <= max);
	}
}

void TestRandom::testPcgRandomBytes()
{
	char buf[32];
	PcgRandom r(1538, 877);

	memset(buf, 0, sizeof(buf));
	r.bytes(buf + 5, 23);
	UASSERT(memcmp(buf + 5, expected_pcgrandom_bytes_result,
		sizeof(expected_pcgrandom_bytes_result)) == 0);

	memset(buf, 0, sizeof(buf));
	r.bytes(buf, 17);
	UASSERT(memcmp(buf, expected_pcgrandom_bytes_result2,
		sizeof(expected_pcgrandom_bytes_result2)) == 0);
}

void TestRandom::testPcgRandomNormalDist()
{
	static const int max = 120;
	static const int min = -120;
	static const int num_trials = 20;
	static const u32 num_samples = 61000;
	s32 bins[max - min + 1];
	memset(bins, 0, sizeof(bins));

	PcgRandom r(486179 + (int)time(NULL));

	for (u32 i = 0; i != num_samples; i++) {
		s32 randval = r.randNormalDist(min, max, num_trials);
		UASSERT(randval <= max);
		UASSERT(randval >= min);
		bins[randval - min]++;
	}

	// Note that here we divide variance by the number of trials;
	// this is because variance is a biased estimator.
	int range      = (max - min + 1);
	float mean     = (max + min) / 2;
	float variance = ((range * range - 1) / 12) / num_trials;
	float stddev   = sqrt(variance);

	static const float prediction_intervals[] = {
		0.68269f, // 1.0
		0.86639f, // 1.5
		0.95450f, // 2.0
		0.98758f, // 2.5
		0.99730f, // 3.0
	};

	//// Simple normality test using the 68-95-99.7% rule
	for (u32 i = 0; i != ARRLEN(prediction_intervals); i++) {
		float deviations = i / 2.f + 1.f;
		int lbound = myround(mean - deviations * stddev);
		int ubound = myround(mean + deviations * stddev);
		UASSERT(lbound >= min);
		UASSERT(ubound <= max);

		int accum = 0;
		for (int j = lbound; j != ubound; j++)
			accum += bins[j - min];

		float actual = (float)accum / num_samples;
		UASSERT(fabs(actual - prediction_intervals[i]) < 0.02);
	}
}

const int TestRandom::expected_pseudorandom_results[256] = {
	0x02fa, 0x60d5, 0x6c10, 0x606b, 0x098b, 0x5f1e, 0x4f56, 0x3fbd, 0x77af,
	0x4fe9, 0x419a, 0x6fe1, 0x177b, 0x6858, 0x36f8, 0x6d83, 0x14fc, 0x2d62,
	0x1077, 0x23e2, 0x041b, 0x7a7e, 0x5b52, 0x215d, 0x682b, 0x4716, 0x47e3,
	0x08c0, 0x1952, 0x56ae, 0x146d, 0x4b4f, 0x239f, 0x3fd0, 0x6794, 0x7796,
	0x7be2, 0x75b7, 0x5691, 0x28ee, 0x2656, 0x40c0, 0x133c, 0x63cd, 0x2aeb,
	0x518f, 0x7dbc, 0x6ad8, 0x736e, 0x5b05, 0x160b, 0x589f, 0x6f64, 0x5edc,
	0x092c, 0x0a39, 0x199e, 0x1927, 0x562b, 0x2689, 0x3ba3, 0x366f, 0x46da,
	0x4e49, 0x0abb, 0x40a1, 0x3846, 0x40db, 0x7adb, 0x6ec1, 0x6efa, 0x01cc,
	0x6335, 0x4352, 0x72fb, 0x4b2d, 0x509a, 0x257e, 0x2f7d, 0x5891, 0x2195,
	0x6107, 0x5269, 0x56e3, 0x4849, 0x38f7, 0x2791, 0x04f2, 0x4e05, 0x78ff,
	0x6bae, 0x50b3, 0x74ad, 0x31af, 0x531e, 0x7d56, 0x11c9, 0x0b5e, 0x405e,
	0x1e15, 0x7f6a, 0x5bd3, 0x6649, 0x71b4, 0x3ec2, 0x6ab4, 0x520e, 0x6ad6,
	0x287e, 0x10b8, 0x18f2, 0x7107, 0x46ea, 0x1d85, 0x25cc, 0x2689, 0x35c1,
	0x3065, 0x6237, 0x3edd, 0x23d9, 0x6fb5, 0x37a1, 0x3211, 0x526a, 0x4b09,
	0x23f1, 0x58cc, 0x2e42, 0x341f, 0x5e16, 0x3d1a, 0x5e8c, 0x7a82, 0x4635,
	0x2bf8, 0x6577, 0x3603, 0x1daf, 0x539f, 0x2e91, 0x6bd8, 0x42d3, 0x7a93,
	0x26e3, 0x5a91, 0x6c67, 0x1b66, 0x3ac7, 0x18bf, 0x20d8, 0x7153, 0x558d,
	0x7262, 0x653d, 0x417d, 0x3ed3, 0x3117, 0x600d, 0x6d04, 0x719c, 0x3afd,
	0x6ba5, 0x17c5, 0x4935, 0x346c, 0x5479, 0x6ff6, 0x1fcc, 0x1054, 0x3f14,
	0x6266, 0x3acc, 0x3b77, 0x71d8, 0x478b, 0x20fa, 0x4e46, 0x7e77, 0x5554,
	0x3652, 0x719c, 0x072b, 0x61ad, 0x399f, 0x621d, 0x1bba, 0x41d0, 0x7fdc,
	0x3e6c, 0x6a2a, 0x5253, 0x094e, 0x0c10, 0x3f43, 0x73eb, 0x4c5f, 0x1f23,
	0x12c9, 0x0902, 0x5238, 0x50c0, 0x1b77, 0x3ffd, 0x0124, 0x302a, 0x26b9,
	0x3648, 0x30a6, 0x1abc, 0x3031, 0x4029, 0x6358, 0x6696, 0x74e8, 0x6142,
	0x4284, 0x0c00, 0x7e50, 0x41e3, 0x3782, 0x79a5, 0x60fe, 0x2d15, 0x3ed2,
	0x7f70, 0x2b27, 0x6366, 0x5100, 0x7c44, 0x3ee0, 0x4e76, 0x7d34, 0x3a60,
	0x140e, 0x613d, 0x1193, 0x268d, 0x1e2f, 0x3123, 0x6d61, 0x4e0b, 0x51ce,
	0x13bf, 0x58d4, 0x4f43, 0x05c6, 0x4d6a, 0x7eb5, 0x2921, 0x2c36, 0x1c89,
	0x63b9, 0x1555, 0x1f41, 0x2d9f,
};

const u32 TestRandom::expected_pcgrandom_results[256] = {
	0x48c593f8, 0x054f59f5, 0x0d062dc1, 0x23852a23, 0x7fbbc97b, 0x1f9f141e,
	0x364e6ed8, 0x995bba58, 0xc9307dc0, 0x73fb34c4, 0xcd8de88d, 0x52e8ce08,
	0x1c4a78e4, 0x25c0882e, 0x8a82e2e0, 0xe3bc3311, 0xb8068d42, 0x73186110,
	0x19988df4, 0x69bd970b, 0x7214728c, 0x0aee320c, 0x2a5a536c, 0xaf48d715,
	0x00bce504, 0xd2b8f548, 0x520df366, 0x96d8fff5, 0xa1bb510b, 0x63477049,
	0xb85990b7, 0x7e090689, 0x275fb468, 0x50206257, 0x8bab4f8a, 0x0d6823db,
	0x63faeaac, 0x2d92deeb, 0x2ba78024, 0x0d30f631, 0x338923a0, 0xd07248d8,
	0xa5db62d3, 0xddba8af6, 0x0ad454e9, 0x6f0fd13a, 0xbbfde2bf, 0x91188009,
	0x966b394d, 0xbb9d2012, 0x7e6926cb, 0x95183860, 0x5ff4c59b, 0x035f628a,
	0xb67085ef, 0x33867e23, 0x68d1b887, 0x2e3298d7, 0x84fd0650, 0x8bc91141,
	0x6fcb0452, 0x2836fee9, 0x2e83c0a3, 0xf1bafdc5, 0x9ff77777, 0xfdfbba87,
	0x527aebeb, 0x423e5248, 0xd1756490, 0xe41148fa, 0x3361f7b4, 0xa2824f23,
	0xf4e08072, 0xc50442be, 0x35adcc21, 0x36be153c, 0xc7709012, 0xf0eeb9f2,
	0x3d73114e, 0x1c1574ee, 0x92095b9c, 0x1503d01c, 0xd6ce0677, 0x026a8ec1,
	0x76d0084d, 0x86c23633, 0x36f75ce6, 0x08fa7bbe, 0x35f6ff2a, 0x31cc9525,
	0x2c1a35e6, 0x8effcd62, 0xc782fa07, 0x8a86e248, 0x8fdb7a9b, 0x77246626,
	0x5767723f, 0x3a78b699, 0xe548ce1c, 0x5820f37d, 0x148ed9b8, 0xf6796254,
	0x32232c20, 0x392bf3a2, 0xe9af6625, 0xd40b0d88, 0x636cfa23, 0x6a5de514,
	0xc4a69183, 0xc785c853, 0xab0de901, 0x16ae7e44, 0x376f13b5, 0x070f7f31,
	0x34cbc93b, 0xe6184345, 0x1b7f911f, 0x631fbe4b, 0x86d6e023, 0xc689b518,
	0x88ef4f7c, 0xddf06b45, 0xc97f18d4, 0x2aaee94b, 0x45694723, 0x6db111d2,
	0x91974fce, 0xe33e29e2, 0xc5e99494, 0x8017e02b, 0x3ebd8143, 0x471ffb80,
	0xc0d7ca1b, 0x4954c860, 0x48935d6a, 0xf2d27999, 0xb93d608d, 0x40696e90,
	0x60b18162, 0x1a156998, 0x09b8bbab, 0xc80a79b6, 0x8adbcfbc, 0xc375248c,
	0xa584e2ea, 0x5b46fe11, 0x58e84680, 0x8a8bc456, 0xd668b94f, 0x8b9035be,
	0x278509d4, 0x6663a140, 0x81a9817a, 0xd4f9d3cf, 0x6dc5f607, 0x6ae04450,
	0x694f22a4, 0x1d061788, 0x2e39ad8b, 0x748f4db2, 0xee569b52, 0xd157166d,
	0xdabc161e, 0xc8d50176, 0x7e3110e5, 0x9f7d033b, 0x128df67f, 0xb0078583,
	0xa3a75d26, 0xc1ad8011, 0x07dd89ec, 0xef04f456, 0x91bf866c, 0x6aac5306,
	0xdd5a1573, 0xf73ff97a, 0x4e1186ad, 0xb9680680, 0xc8894515, 0xdc95a08e,
	0xc894fd8e, 0xf84ade15, 0xd787f8c1, 0x40dcecca, 0x1b24743e, 0x1ce6ab23,
	0x72321653, 0xb80fbaf7, 0x1bcf099b, 0x1ff26805, 0x78f66c8e, 0xf93bf51a,
	0xfb0c06fe, 0xe50d48cf, 0x310947e0, 0x1b78804a, 0xe73e2c14, 0x8deb8381,
	0xe576122a, 0xe5a8df39, 0x42397c5e, 0xf5503f3c, 0xbe3dbf8d, 0x1b360e5c,
	0x9254caaf, 0x7a9f6744, 0x6d4144fa, 0xd77c65fe, 0x44ca7b12, 0xf58a4c00,
	0x159500d0, 0x92769857, 0x7134fdd4, 0xa3fea693, 0xbd044831, 0xeded39a1,
	0xe4570204, 0xaea37f2f, 0x9a302971, 0x620f8402, 0x1d2f3e5e, 0xf9c2f49c,
	0x738e813a, 0xb3c92251, 0x7ecba63b, 0xbe7eebc7, 0xf800267c, 0x3fdeb760,
	0xf12d5e7d, 0x5a18dce1, 0xb35a539c, 0xe565f057, 0x2babf38c, 0xae5800ad,
	0x421004dd, 0x6715acb6, 0xff529b64, 0xd520d207, 0x7cb193e7, 0xe9b18e4c,
	0xfd2a8a59, 0x47826ae3, 0x56ba43f8, 0x453b3d99, 0x8ae1675f, 0xf66f5c34,
	0x057a6ac1, 0x010769e4, 0xa8324158, 0x410379a5, 0x5dfc8c97, 0x72848afe,
	0x59f169e5, 0xe32acb78, 0x5dfaa9c4, 0x51bb956a,
};

const u8 TestRandom::expected_pcgrandom_bytes_result[24] = {
	0xf3, 0x79, 0x8f, 0x31, 0xac, 0xd9, 0x34, 0xf8, 0x3c, 0x6e, 0x82, 0x37,
	0x6b, 0x4b, 0x77, 0xe3, 0xbd, 0x0a, 0xee, 0x22, 0x79, 0x6e, 0x40, 0x00,
};

const u8 TestRandom::expected_pcgrandom_bytes_result2[24] = {
	0x47, 0x9e, 0x08, 0x3e, 0xd4, 0x21, 0x2d, 0xf6, 0xb4, 0xb1, 0x9d, 0x7a,
	0x60, 0x02, 0x5a, 0xb2, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};


code:
 /*
Minetest
Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>

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 "mg_schematic.h"
#include "gamedef.h"
#include "nodedef.h"

class TestSchematic : public TestBase {
public:
	TestSchematic() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestSchematic"; }

	void runTests(IGameDef *gamedef);

	void testMtsSerializeDeserialize(INodeDefManager *ndef);
	void testLuaTableSerialize(INodeDefManager *ndef);
	void testFileSerializeDeserialize(INodeDefManager *ndef);

	static const content_t test_schem1_data[7 * 6 * 4];
	static const content_t test_schem2_data[3 * 3 * 3];
	static const u8 test_schem2_prob[3 * 3 * 3];
	static const char *expected_lua_output;
};

static TestSchematic g_test_instance;

void TestSchematic::runTests(IGameDef *gamedef)
{
	IWritableNodeDefManager *ndef =
		(IWritableNodeDefManager *)gamedef->getNodeDefManager();

	ndef->setNodeRegistrationStatus(true);

	TEST(testMtsSerializeDeserialize, ndef);
	TEST(testLuaTableSerialize, ndef);
	TEST(testFileSerializeDeserialize, ndef);

	ndef->resetNodeResolveState();
}

////////////////////////////////////////////////////////////////////////////////

void TestSchematic::testMtsSerializeDeserialize(INodeDefManager *ndef)
{
	static const v3s16 size(7, 6, 4);
	static const u32 volume = size.X * size.Y * size.Z;

	std::stringstream ss(std::ios_base::binary |
		std::ios_base::in | std::ios_base::out);

	std::vector<std::string> names;
	names.push_back("foo");
	names.push_back("bar");
	names.push_back("baz");
	names.push_back("qux");

	Schematic schem, schem2;

	schem.flags       = 0;
	schem.size        = size;
	schem.schemdata   = new MapNode[volume];
	schem.slice_probs = new u8[size.Y];
	for (size_t i = 0; i != volume; i++)
		schem.schemdata[i] = MapNode(test_schem1_data[i], MTSCHEM_PROB_ALWAYS, 0);
	for (s16 y = 0; y != size.Y; y++)
		schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;

	UASSERT(schem.serializeToMts(&ss, names));

	ss.seekg(0);
	names.clear();

	UASSERT(schem2.deserializeFromMts(&ss, &names));

	UASSERTEQ(size_t, names.size(), 4);
	UASSERTEQ(std::string, names[0], "foo");
	UASSERTEQ(std::string, names[1], "bar");
	UASSERTEQ(std::string, names[2], "baz");
	UASSERTEQ(std::string, names[3], "qux");

	UASSERT(schem2.size == size);
	for (size_t i = 0; i != volume; i++)
		UASSERT(schem2.schemdata[i] == schem.schemdata[i]);
	for (s16 y = 0; y != size.Y; y++)
		UASSERTEQ(u8, schem2.slice_probs[y], schem.slice_probs[y]);
}

void TestSchematic::testLuaTableSerialize(INodeDefManager *ndef)
{
	static const v3s16 size(3, 3, 3);
	static const u32 volume = size.X * size.Y * size.Z;

	Schematic schem;

	schem.flags       = 0;
	schem.size        = size;
	schem.schemdata   = new MapNode[volume];
	schem.slice_probs = new u8[size.Y];
	for (size_t i = 0; i != volume; i++)
		schem.schemdata[i] = MapNode(test_schem2_data[i], test_schem2_prob[i], 0);
	for (s16 y = 0; y != size.Y; y++)
		schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;

	std::vector<std::string> names;
	names.push_back("air");
	names.push_back("default:lava_source");
	names.push_back("default:glass");

	std::ostringstream ss(std::ios_base::binary);

	UASSERT(schem.serializeToLua(&ss, names, false, 0));
	UASSERTEQ(std::string, ss.str(), expected_lua_output);
}

void TestSchematic::testFileSerializeDeserialize(INodeDefManager *ndef)
{
	static const v3s16 size(3, 3, 3);
	static const u32 volume = size.X * size.Y * size.Z;
	static const content_t content_map[] = {
		CONTENT_AIR,
		t_CONTENT_STONE,
		t_CONTENT_LAVA,
	};
	static const content_t content_map2[] = {
		CONTENT_AIR,
		t_CONTENT_STONE,
		t_CONTENT_WATER,
	};
	StringMap replace_names;
	replace_names["default:lava"] = "default:water";

	Schematic schem1, schem2;

	//// Construct the schematic to save
	schem1.flags          = 0;
	schem1.size           = size;
	schem1.schemdata      = new MapNode[volume];
	schem1.slice_probs    = new u8[size.Y];
	schem1.slice_probs[0] = 80;
	schem1.slice_probs[1] = 160;
	schem1.slice_probs[2] = 240;

	for (size_t i = 0; i != volume; i++) {
		content_t c = content_map[test_schem2_data[i]];
		schem1.schemdata[i] = MapNode(c, test_schem2_prob[i], 0);
	}

	std::string temp_file = getTestTempFile();
	UASSERT(schem1.saveSchematicToFile(temp_file, ndef));
	UASSERT(schem2.loadSchematicFromFile(temp_file, ndef, &replace_names));

	UASSERT(schem2.size == size);
	UASSERT(schem2.slice_probs[0] == 80);
	UASSERT(schem2.slice_probs[1] == 160);
	UASSERT(schem2.slice_probs[2] == 240);

	for (size_t i = 0; i != volume; i++) {
		content_t c = content_map2[test_schem2_data[i]];
		UASSERT(schem2.schemdata[i] == MapNode(c, test_schem2_prob[i], 0));
	}
}

// Should form a cross-shaped-thing...?
const content_t TestSchematic::test_schem1_data[7 * 6 * 4] = {
	3, 3, 1, 1, 1, 3, 3, // Y=0, Z=0
	3, 0, 1, 2, 1, 0, 3, // Y=1, Z=0
	3, 0, 1, 2, 1, 0, 3, // Y=2, Z=0
	3, 1, 1, 2, 1, 1, 3, // Y=3, Z=0
	3, 2, 2, 2, 2, 2, 3, // Y=4, Z=0
	3, 1, 1, 2, 1, 1, 3, // Y=5, Z=0

	0, 0, 1, 1, 1, 0, 0, // Y=0, Z=1
	0, 0, 1, 2, 1, 0, 0, // Y=1, Z=1
	0, 0, 1, 2, 1, 0, 0, // Y=2, Z=1
	1, 1, 1, 2, 1, 1, 1, // Y=3, Z=1
	1, 2, 2, 2, 2, 2, 1, // Y=4, Z=1
	1, 1, 1, 2, 1, 1, 1, // Y=5, Z=1

	0, 0, 1, 1, 1, 0, 0, // Y=0, Z=2
	0, 0, 1, 2, 1, 0, 0, // Y=1, Z=2
	0, 0, 1, 2, 1, 0, 0, // Y=2, Z=2
	1, 1, 1, 2, 1, 1, 1, // Y=3, Z=2
	1, 2, 2, 2, 2, 2, 1, // Y=4, Z=2
	1, 1, 1, 2, 1, 1, 1, // Y=5, Z=2

	3, 3, 1, 1, 1, 3, 3, // Y=0, Z=3
	3, 0, 1, 2, 1, 0, 3, // Y=1, Z=3
	3, 0, 1, 2, 1, 0, 3, // Y=2, Z=3
	3, 1, 1, 2, 1, 1, 3, // Y=3, Z=3
	3, 2, 2, 2, 2, 2, 3, // Y=4, Z=3
	3, 1, 1, 2, 1, 1, 3, // Y=5, Z=3
};

const content_t TestSchematic::test_schem2_data[3 * 3 * 3] = {
	0, 0, 0,
	0, 2, 0,
	0, 0, 0,

	0, 2, 0,
	2, 1, 2,
	0, 2, 0,

	0, 0, 0,
	0, 2, 0,
	0, 0, 0,
};

const u8 TestSchematic::test_schem2_prob[3 * 3 * 3] = {
	0x00, 0x00, 0x00,
	0x00, 0xFF, 0x00,
	0x00, 0x00, 0x00,

	0x00, 0xFF, 0x00,
	0xFF, 0xFF, 0xFF,
	0x00, 0xFF, 0x00,

	0x00, 0x00, 0x00,
	0x00, 0xFF, 0x00,
	0x00, 0x00, 0x00,
};

const char *TestSchematic::expected_lua_output =
	"schematic = {\n"
	"\tsize = {x=3, y=3, z=3},\n"
	"\tyslice_prob = {\n"
	"\t\t{ypos=0, prob=254},\n"
	"\t\t{ypos=1, prob=254},\n"
	"\t\t{ypos=2, prob=254},\n"
	"\t},\n"
	"\tdata = {\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
	"\t\t{name=\"default:lava_source\", prob=254, param2=0, force_place=true},\n"
	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t\t{name=\"air\", prob=0, param2=0},\n"
	"\t},\n"
	"}\n";


code:
/*
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 "util/string.h"
#include "util/serialize.h"

class TestSerialization : public TestBase {
public:
	TestSerialization() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestSerialization"; }

	void runTests(IGameDef *gamedef);
	void buildTestStrings();

	void testSerializeString();
	void testSerializeWideString();
	void testSerializeLongString();
	void testSerializeJsonString();
	void testSerializeHex();
	void testDeSerializeString();
	void testDeSerializeWideString();
	void testDeSerializeLongString();
	void testStreamRead();
	void testStreamWrite();
	void testVecPut();
	void testStringLengthLimits();
	void testBufReader();

	std::string teststring2;
	std::wstring teststring2_w;
	std::string teststring2_w_encoded;

	static const u8 test_serialized_data[12 * 13];
};

static TestSerialization g_test_instance;

void TestSerialization::runTests(IGameDef *gamedef)
{
	buildTestStrings();

	TEST(testSerializeString);
	TEST(testDeSerializeString);
	TEST(testSerializeWideString);
	TEST(testDeSerializeWideString);
	TEST(testSerializeLongString);
	TEST(testDeSerializeLongString);
	TEST(testSerializeJsonString);
	TEST(testSerializeHex);
	TEST(testStreamRead);
	TEST(testStreamWrite);
	TEST(testVecPut);
	TEST(testStringLengthLimits);
	TEST(testBufReader);
}

////////////////////////////////////////////////////////////////////////////////

// To be used like this:
//   mkstr("Some\0string\0with\0embedded\0nuls")
// since std::string("...") doesn't work as expected in that case.
template<size_t N> std::string mkstr(const char (&s)[N])
{
	return std::string(s, N - 1);
}

void TestSerialization::buildTestStrings()
{
	std::ostringstream tmp_os;
	std::wostringstream tmp_os_w;
	std::ostringstream tmp_os_w_encoded;
	for (int i = 0; i < 256; i++) {
		tmp_os << (char)i;
		tmp_os_w << (wchar_t)i;
		tmp_os_w_encoded << (char)0 << (char)i;
	}
	teststring2 = tmp_os.str();
	teststring2_w = tmp_os_w.str();
	teststring2_w_encoded = tmp_os_w_encoded.str();
}

void TestSerialization::testSerializeString()
{
	// Test blank string
	UASSERT(serializeString("") == mkstr("\0\0"));

	// Test basic string
	UASSERT(serializeString("Hello world!") == mkstr("\0\14Hello world!"));

	// Test character range
	UASSERT(serializeString(teststring2) == mkstr("\1\0") + teststring2);
}

void TestSerialization::testDeSerializeString()
{
	// Test deserialize
	{
		std::istringstream is(serializeString(teststring2), std::ios::binary);
		UASSERT(deSerializeString(is) == teststring2);
		UASSERT(!is.eof());
		is.get();
		UASSERT(is.eof());
	}

	// Test deserialize an incomplete length specifier
	{
		std::istringstream is(mkstr("\x53"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeString(is));
	}

	// Test deserialize a string with incomplete data
	{
		std::istringstream is(mkstr("\x00\x55 abcdefg"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeString(is));
	}
}

void TestSerialization::testSerializeWideString()
{
	// Test blank string
	UASSERT(serializeWideString(L"") == mkstr("\0\0"));

	// Test basic string
	UASSERT(serializeWideString(utf8_to_wide("Hello world!")) ==
		mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!"));

	// Test character range
	UASSERT(serializeWideString(teststring2_w) ==
		mkstr("\1\0") + teststring2_w_encoded);
}

void TestSerialization::testDeSerializeWideString()
{
	// Test deserialize
	{
		std::istringstream is(serializeWideString(teststring2_w), std::ios::binary);
		UASSERT(deSerializeWideString(is) == teststring2_w);
		UASSERT(!is.eof());
		is.get();
		UASSERT(is.eof());
	}

	// Test deserialize an incomplete length specifier
	{
		std::istringstream is(mkstr("\x53"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
	}

	// Test deserialize a string with an incomplete character
	{
		std::istringstream is(mkstr("\x00\x07\0a\0b\0c\0d\0e\0f\0"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
	}

	// Test deserialize a string with incomplete data
	{
		std::istringstream is(mkstr("\x00\x08\0a\0b\0c\0d\0e\0f"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
	}
}

void TestSerialization::testSerializeLongString()
{
	// Test blank string
	UASSERT(serializeLongString("") == mkstr("\0\0\0\0"));

	// Test basic string
	UASSERT(serializeLongString("Hello world!") == mkstr("\0\0\0\14Hello world!"));

	// Test character range
	UASSERT(serializeLongString(teststring2) == mkstr("\0\0\1\0") + teststring2);
}

void TestSerialization::testDeSerializeLongString()
{
	// Test deserialize
	{
		std::istringstream is(serializeLongString(teststring2), std::ios::binary);
		UASSERT(deSerializeLongString(is) == teststring2);
		UASSERT(!is.eof());
		is.get();
		UASSERT(is.eof());
	}

	// Test deserialize an incomplete length specifier
	{
		std::istringstream is(mkstr("\x53"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
	}

	// Test deserialize a string with incomplete data
	{
		std::istringstream is(mkstr("\x00\x00\x00\x05 abc"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
	}

	// Test deserialize a string with a length too large
	{
		std::istringstream is(mkstr("\xFF\xFF\xFF\xFF blah"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
	}
}

void TestSerialization::testSerializeJsonString()
{
	// Test blank string
	UASSERT(serializeJsonString("") == "\"\"");

	// Test basic string
	UASSERT(serializeJsonString("Hello world!") == "\"Hello world!\"");

	// MSVC fails when directly using "\\\\"
	std::string backslash = "\\";
	UASSERT(serializeJsonString(teststring2) ==
		mkstr("\"") +
		"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
		"\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
		"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
		"\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
		" !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
		"\\/" + teststring2.substr(0x30, 0x5c-0x30) +
		backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
		"\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
		"\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
		"\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
		"\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" +
		"\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" +
		"\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" +
		"\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" +
		"\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" +
		"\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" +
		"\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" +
		"\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" +
		"\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" +
		"\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" +
		"\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
		"\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
		"\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
		"\"");

	// Test deserialize
	std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
	UASSERT(deSerializeJsonString(is) == teststring2);
	UASSERT(!is.eof());
	is.get();
	UASSERT(is.eof());
}

void TestSerialization::testSerializeHex()
{
	// Test blank string
	UASSERT(serializeHexString("") == "");
	UASSERT(serializeHexString("", true) == "");

	// Test basic string
	UASSERT(serializeHexString("Hello world!") ==
		"48656c6c6f20776f726c6421");
	UASSERT(serializeHexString("Hello world!", true) ==
		"48 65 6c 6c 6f 20 77 6f 72 6c 64 21");

	// Test binary string
	UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff")) ==
		"000ab0631f00ff");
	UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff"), true) ==
		"00 0a b0 63 1f 00 ff");
}

void TestSerialization::testStreamRead()
{
	std::string datastr(
		(const char *)test_serialized_data,
		sizeof(test_serialized_data));
	std::istringstream is(datastr, std::ios_base::binary);

	UASSERT(readU8(is) == 0x11);
	UASSERT(readU16(is) == 0x2233);
	UASSERT(readU32(is) == 0x44556677);
	UASSERT(readU64(is) == 0x8899AABBCCDDEEFF);

	UASSERT(readS8(is) == -128);
	UASSERT(readS16(is) == 30000);
	UASSERT(readS32(is) == -6);
	UASSERT(readS64(is) == -43);

	UASSERT(readF1000(is) == 53.534f);
	UASSERT(readF1000(is) == -300000.32f);
	UASSERT(readF1000(is) == F1000_MIN);
	UASSERT(readF1000(is) == F1000_MAX);

	UASSERT(deSerializeString(is) == "foobar!");

	UASSERT(readV2S16(is) == v2s16(500, 500));
	UASSERT(readV3S16(is) == v3s16(4207, 604, -30));
	UASSERT(readV2S32(is) == v2s32(1920, 1080));
	UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855));
	UASSERT(readV2F1000(is) == v2f(500.656f, 350.345f));

	UASSERT(deSerializeWideString(is) == L"\x02~woof~\x5455");

	UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f));
	UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128));

	UASSERT(deSerializeLongString(is) == "some longer string here");

	UASSERT(is.rdbuf()->in_avail() == 2);
	UASSERT(readU16(is) == 0xF00D);
	UASSERT(is.rdbuf()->in_avail() == 0);
}

void TestSerialization::testStreamWrite()
{
	std::ostringstream os(std::ios_base::binary);
	std::string data;

	writeU8(os, 0x11);
	writeU16(os, 0x2233);
	writeU32(os, 0x44556677);
	writeU64(os, 0x8899AABBCCDDEEFF);

	writeS8(os, -128);
	writeS16(os, 30000);
	writeS32(os, -6);
	writeS64(os, -43);

	writeF1000(os, 53.53467f);
	writeF1000(os, -300000.32f);
	writeF1000(os, F1000_MIN);
	writeF1000(os, F1000_MAX);

	os << serializeString("foobar!");

	data = os.str();
	UASSERT(data.size() < sizeof(test_serialized_data));
	UASSERT(!memcmp(&data[0], test_serialized_data, data.size()));

	writeV2S16(os, v2s16(500, 500));
	writeV3S16(os, v3s16(4207, 604, -30));
	writeV2S32(os, v2s32(1920, 1080));
	writeV3S32(os, v3s32(-400, 6400054, 290549855));
	writeV2F1000(os, v2f(500.65661f, 350.34567f));

	os << serializeWideString(L"\x02~woof~\x5455");

	writeV3F1000(os, v3f(500, 10024.2f, -192.54f));
	writeARGB8(os, video::SColor(255, 128, 50, 128));

	os << serializeLongString("some longer string here");

	writeU16(os, 0xF00D);

	data = os.str();
	UASSERT(data.size() == sizeof(test_serialized_data));
	UASSERT(!memcmp(&data[0], test_serialized_data, sizeof(test_serialized_data)));
}

void TestSerialization::testVecPut()
{
	std::vector<u8> buf;

	putU8(&buf, 0x11);
	putU16(&buf, 0x2233);
	putU32(&buf, 0x44556677);
	putU64(&buf, 0x8899AABBCCDDEEFF);

	putS8(&buf, -128);
	putS16(&buf, 30000);
	putS32(&buf, -6);
	putS64(&buf, -43);

	putF1000(&buf, 53.53467f);
	putF1000(&buf, -300000.32f);
	putF1000(&buf, F1000_MIN);
	putF1000(&buf, F1000_MAX);

	putString(&buf, "foobar!");

	putV2S16(&buf, v2s16(500, 500));
	putV3S16(&buf, v3s16(4207, 604, -30));
	putV2S32(&buf, v2s32(1920, 1080));
	putV3S32(&buf, v3s32(-400, 6400054, 290549855));
	putV2F1000(&buf, v2f(500.65661f, 350.34567f));

	putWideString(&buf, L"\x02~woof~\x5455");

	putV3F1000(&buf, v3f(500, 10024.2f, -192.54f));
	putARGB8(&buf, video::SColor(255, 128, 50, 128));

	putLongString(&buf, "some longer string here");

	putU16(&buf, 0xF00D);

	UASSERT(buf.size() == sizeof(test_serialized_data));
	UASSERT(!memcmp(&buf[0], test_serialized_data, sizeof(test_serialized_data)));
}

void TestSerialization::testStringLengthLimits()
{
	std::vector<u8> buf;
	std::string too_long(STRING_MAX_LEN + 1, 'A');
	std::string way_too_large(LONG_STRING_MAX_LEN + 1, 'B');
	std::wstring too_long_wide(WIDE_STRING_MAX_LEN + 1, L'C');

	EXCEPTION_CHECK(SerializationError, putString(&buf, too_long));

	putLongString(&buf, too_long);
	too_long.resize(too_long.size() - 1);
	putString(&buf, too_long);

	EXCEPTION_CHECK(SerializationError, putWideString(&buf, too_long_wide));
	too_long_wide.resize(too_long_wide.size() - 1);
	putWideString(&buf, too_long_wide);
}

void TestSerialization::testBufReader()
{
	u8 u8_data;
	u16 u16_data;
	u32 u32_data;
	u64 u64_data;
	s8 s8_data;
	s16 s16_data;
	s32 s32_data;
	s64 s64_data;
	f32 f32_data, f32_data2, f32_data3, f32_data4;
	video::SColor scolor_data;
	v2s16 v2s16_data;
	v3s16 v3s16_data;
	v2s32 v2s32_data;
	v3s32 v3s32_data;
	v2f v2f_data;
	v3f v3f_data;
	std::string string_data;
	std::wstring widestring_data;
	std::string longstring_data;
	u8 raw_data[10] = {0};

	BufReader buf(test_serialized_data, sizeof(test_serialized_data));

	// Try reading data like normal
	UASSERT(buf.getU8() == 0x11);
	UASSERT(buf.getU16() == 0x2233);
	UASSERT(buf.getU32() == 0x44556677);
	UASSERT(buf.getU64() == 0x8899AABBCCDDEEFF);
	UASSERT(buf.getS8() == -128);
	UASSERT(buf.getS16() == 30000);
	UASSERT(buf.getS32() == -6);
	UASSERT(buf.getS64() == -43);
	UASSERT(buf.getF1000() == 53.534f);
	UASSERT(buf.getF1000() == -300000.32f);
	UASSERT(buf.getF1000() == F1000_MIN);
	UASSERT(buf.getF1000() == F1000_MAX);
	UASSERT(buf.getString() == "foobar!");
	UASSERT(buf.getV2S16() == v2s16(500, 500));
	UASSERT(buf.getV3S16() == v3s16(4207, 604, -30));
	UASSERT(buf.getV2S32() == v2s32(1920, 1080));
	UASSERT(buf.getV3S32() == v3s32(-400, 6400054, 290549855));
	UASSERT(buf.getV2F1000() == v2f(500.656f, 350.345f));
	UASSERT(buf.getWideString() == L"\x02~woof~\x5455");
	UASSERT(buf.getV3F1000() == v3f(500, 10024.2f, -192.54f));
	UASSERT(buf.getARGB8() == video::SColor(255, 128, 50, 128));
	UASSERT(buf.getLongString() == "some longer string here");

	// Verify the offset and data is unchanged after a failed read
	size_t orig_pos = buf.pos;
	u32_data = 0;
	UASSERT(buf.getU32NoEx(&u32_data) == false);
	UASSERT(buf.pos == orig_pos);
	UASSERT(u32_data == 0);

	// Now try the same for a failed string read
	UASSERT(buf.getStringNoEx(&string_data) == false);
	UASSERT(buf.pos == orig_pos);
	UASSERT(string_data == "");

	// Now try the same for a failed string read
	UASSERT(buf.getWideStringNoEx(&widestring_data) == false);
	UASSERT(buf.pos == orig_pos);
	UASSERT(widestring_data == L"");

	UASSERT(buf.getU16() == 0xF00D);

	UASSERT(buf.remaining() == 0);

	// Check to make sure these each blow exceptions as they're supposed to
	EXCEPTION_CHECK(SerializationError, buf.getU8());
	EXCEPTION_CHECK(SerializationError, buf.getU16());
	EXCEPTION_CHECK(SerializationError, buf.getU32());
	EXCEPTION_CHECK(SerializationError, buf.getU64());

	EXCEPTION_CHECK(SerializationError, buf.getS8());
	EXCEPTION_CHECK(SerializationError, buf.getS16());
	EXCEPTION_CHECK(SerializationError, buf.getS32());
	EXCEPTION_CHECK(SerializationError, buf.getS64());

	EXCEPTION_CHECK(SerializationError, buf.getF1000());
	EXCEPTION_CHECK(SerializationError, buf.getARGB8());

	EXCEPTION_CHECK(SerializationError, buf.getV2S16());
	EXCEPTION_CHECK(SerializationError, buf.getV3S16());
	EXCEPTION_CHECK(SerializationError, buf.getV2S32());
	EXCEPTION_CHECK(SerializationError, buf.getV3S32());
	EXCEPTION_CHECK(SerializationError, buf.getV2F1000());
	EXCEPTION_CHECK(SerializationError, buf.getV3F1000());

	EXCEPTION_CHECK(SerializationError, buf.getString());
	EXCEPTION_CHECK(SerializationError, buf.getWideString());
	EXCEPTION_CHECK(SerializationError, buf.getLongString());
	EXCEPTION_CHECK(SerializationError,
		buf.getRawData(raw_data, sizeof(raw_data)));

	// See if we can skip backwards
	buf.pos = 5;
	UASSERT(buf.getRawDataNoEx(raw_data, 3) == true);
	UASSERT(raw_data[0] == 0x66);
	UASSERT(raw_data[1] == 0x77);
	UASSERT(raw_data[2] == 0x88);

	UASSERT(buf.getU32() == 0x99AABBCC);
	UASSERT(buf.pos == 12);

	// Now let's try it all over again using the NoEx variants
	buf.pos = 0;

	UASSERT(buf.getU8NoEx(&u8_data));
	UASSERT(buf.getU16NoEx(&u16_data));
	UASSERT(buf.getU32NoEx(&u32_data));
	UASSERT(buf.getU64NoEx(&u64_data));

	UASSERT(buf.getS8NoEx(&s8_data));
	UASSERT(buf.getS16NoEx(&s16_data));
	UASSERT(buf.getS32NoEx(&s32_data));
	UASSERT(buf.getS64NoEx(&s64_data));

	UASSERT(buf.getF1000NoEx(&f32_data));
	UASSERT(buf.getF1000NoEx(&f32_data2));
	UASSERT(buf.getF1000NoEx(&f32_data3));
	UASSERT(buf.getF1000NoEx(&f32_data4));

	UASSERT(buf.getStringNoEx(&string_data));
	UASSERT(buf.getV2S16NoEx(&v2s16_data));
	UASSERT(buf.getV3S16NoEx(&v3s16_data));
	UASSERT(buf.getV2S32NoEx(&v2s32_data));
	UASSERT(buf.getV3S32NoEx(&v3s32_data));
	UASSERT(buf.getV2F1000NoEx(&v2f_data));
	UASSERT(buf.getWideStringNoEx(&widestring_data));
	UASSERT(buf.getV3F1000NoEx(&v3f_data));
	UASSERT(buf.getARGB8NoEx(&scolor_data));

	UASSERT(buf.getLongStringNoEx(&longstring_data));

	// and make sure we got the correct data
	UASSERT(u8_data == 0x11);
	UASSERT(u16_data == 0x2233);
	UASSERT(u32_data == 0x44556677);
	UASSERT(u64_data == 0x8899AABBCCDDEEFF);
	UASSERT(s8_data == -128);
	UASSERT(s16_data == 30000);
	UASSERT(s32_data == -6);
	UASSERT(s64_data == -43);
	UASSERT(f32_data == 53.534f);
	UASSERT(f32_data2 == -300000.32f);
	UASSERT(f32_data3 == F1000_MIN);
	UASSERT(f32_data4 == F1000_MAX);
	UASSERT(string_data == "foobar!");
	UASSERT(v2s16_data == v2s16(500, 500));
	UASSERT(v3s16_data == v3s16(4207, 604, -30));
	UASSERT(v2s32_data == v2s32(1920, 1080));
	UASSERT(v3s32_data == v3s32(-400, 6400054, 290549855));
	UASSERT(v2f_data == v2f(500.656f, 350.345f));
	UASSERT(widestring_data == L"\x02~woof~\x5455");
	UASSERT(v3f_data == v3f(500, 10024.2f, -192.54f));
	UASSERT(scolor_data == video::SColor(255, 128, 50, 128));
	UASSERT(longstring_data == "some longer string here");

	UASSERT(buf.remaining() == 2);
	UASSERT(buf.getRawDataNoEx(raw_data, 3) == false);
	UASSERT(buf.remaining() == 2);
	UASSERT(buf.getRawDataNoEx(raw_data, 2) == true);
	UASSERT(raw_data[0] == 0xF0);
	UASSERT(raw_data[1] == 0x0D);
	UASSERT(buf.remaining() == 0);

	// Make sure no more available data causes a failure
	UASSERT(!buf.getU8NoEx(&u8_data));
	UASSERT(!buf.getU16NoEx(&u16_data));
	UASSERT(!buf.getU32NoEx(&u32_data));
	UASSERT(!buf.getU64NoEx(&u64_data));

	UASSERT(!buf.getS8NoEx(&s8_data));
	UASSERT(!buf.getS16NoEx(&s16_data));
	UASSERT(!buf.getS32NoEx(&s32_data));
	UASSERT(!buf.getS64NoEx(&s64_data));

	UASSERT(!buf.getF1000NoEx(&f32_data));
	UASSERT(!buf.getARGB8NoEx(&scolor_data));

	UASSERT(!buf.getV2S16NoEx(&v2s16_data));
	UASSERT(!buf.getV3S16NoEx(&v3s16_data));
	UASSERT(!buf.getV2S32NoEx(&v2s32_data));
	UASSERT(!buf.getV3S32NoEx(&v3s32_data));
	UASSERT(!buf.getV2F1000NoEx(&v2f_data));
	UASSERT(!buf.getV3F1000NoEx(&v3f_data));

	UASSERT(!buf.getStringNoEx(&string_data));
	UASSERT(!buf.getWideStringNoEx(&widestring_data));
	UASSERT(!buf.getLongStringNoEx(&longstring_data));
	UASSERT(!buf.getRawDataNoEx(raw_data, sizeof(raw_data)));
}

const u8 TestSerialization::test_serialized_data[12 * 13] = {
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
	0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e,
	0x5b, 0xc0, 0x80, 0x00, 0x02, 0x80, 0x7F, 0xFF, 0xFD, 0x80, 0x00, 0x07,
	0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10,
	0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04,
	0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70,
	0x5f, 0x00, 0x07, 0xa3, 0xb0, 0x00, 0x05, 0x58, 0x89, 0x00, 0x08, 0x00,
	0x02, 0x00, 0x7e, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x66, 0x00,
	0x7e, 0x54, 0x55, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff,
	0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73,
	0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73,
	0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D,
};


code:
/*
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 "settings.h"
#include "noise.h"

class TestSettings : public TestBase {
public:
	TestSettings() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestSettings"; }

	void runTests(IGameDef *gamedef);

	void testAllSettings();

	static const char *config_text_before;
	static const std::string config_text_after;
};

static TestSettings g_test_instance;

void TestSettings::runTests(IGameDef *gamedef)
{
	TEST(testAllSettings);
}

////////////////////////////////////////////////////////////////////////////////

const char *TestSettings::config_text_before =
	"leet = 1337\n"
	"leetleet = 13371337\n"
	"leetleet_neg = -13371337\n"
	"floaty_thing = 1.1\n"
	"stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n"
	"coord = (1, 2, 4.5)\n"
	"      # this is just a comment\n"
	"this is an invalid line\n"
	"asdf = {\n"
	"	a   = 5\n"
	"	bb  = 2.5\n"
	"	ccc = \"\"\"\n"
	"testy\n"
	"   testa   \n"
	"\"\"\"\n"
	"\n"
	"}\n"
	"blarg = \"\"\" \n"
	"some multiline text\n"
	"     with leading whitespace!\n"
	"\"\"\"\n"
	"np_terrain = 5, 40, (250, 250, 250), 12341, 5, 0.7, 2.4\n"
	"zoop = true";

const std::string TestSettings::config_text_after =
	"leet = 1337\n"
	"leetleet = 13371337\n"
	"leetleet_neg = -13371337\n"
	"floaty_thing = 1.1\n"
	"stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n"
	"coord = (1, 2, 4.5)\n"
	"      # this is just a comment\n"
	"this is an invalid line\n"
	"asdf = {\n"
	"	a   = 5\n"
	"	bb  = 2.5\n"
	"	ccc = \"\"\"\n"
	"testy\n"
	"   testa   \n"
	"\"\"\"\n"
	"\n"
	"}\n"
	"blarg = \"\"\" \n"
	"some multiline text\n"
	"     with leading whitespace!\n"
	"\"\"\"\n"
	"np_terrain = {\n"
	"	flags = defaults\n"
	"	lacunarity = 2.4\n"
	"	octaves = 6\n"
	"	offset = 3.5\n"
	"	persistence = 0.7\n"
	"	scale = 40\n"
	"	seed = 12341\n"
	"	spread = (250,250,250)\n"
	"}\n"
	"zoop = true\n"
	"coord2 = (1,2,3.3)\n"
	"floaty_thing_2 = 1.2\n"
	"groupy_thing = {\n"
	"	animals = cute\n"
	"	num_apples = 4\n"
	"	num_oranges = 53\n"
	"}\n";

void TestSettings::testAllSettings()
{
	try {
	Settings s;

	// Test reading of settings
	std::istringstream is(config_text_before);
	s.parseConfigLines(is);

	UASSERT(s.getS32("leet") == 1337);
	UASSERT(s.getS16("leetleet") == 32767);
	UASSERT(s.getS16("leetleet_neg") == -32768);

	// Not sure if 1.1 is an exact value as a float, but doesn't matter
	UASSERT(fabs(s.getFloat("floaty_thing") - 1.1) < 0.001);
	UASSERT(s.get("stringy_thing") == "asd /( ¤%&(/\" BLÖÄRP");
	UASSERT(fabs(s.getV3F("coord").X - 1.0) < 0.001);
	UASSERT(fabs(s.getV3F("coord").Y - 2.0) < 0.001);
	UASSERT(fabs(s.getV3F("coord").Z - 4.5) < 0.001);

	// Test the setting of settings too
	s.setFloat("floaty_thing_2", 1.2);
	s.setV3F("coord2", v3f(1, 2, 3.3));
	UASSERT(s.get("floaty_thing_2").substr(0,3) == "1.2");
	UASSERT(fabs(s.getFloat("floaty_thing_2") - 1.2) < 0.001);
	UASSERT(fabs(s.getV3F("coord2").X - 1.0) < 0.001);
	UASSERT(fabs(s.getV3F("coord2").Y - 2.0) < 0.001);
	UASSERT(fabs(s.getV3F("coord2").Z - 3.3) < 0.001);

	// Test settings groups
	Settings *group = s.getGroup("asdf");
	UASSERT(group != NULL);
	UASSERT(s.getGroupNoEx("zoop", group) == false);
	UASSERT(group->getS16("a") == 5);
	UASSERT(fabs(group->getFloat("bb") - 2.5) < 0.001);

	Settings *group3 = new Settings;
	group3->set("cat", "meow");
	group3->set("dog", "woof");

	Settings *group2 = new Settings;
	group2->setS16("num_apples", 4);
	group2->setS16("num_oranges", 53);
	group2->setGroup("animals", group3);
	group2->set("animals", "cute"); //destroys group 3
	s.setGroup("groupy_thing", group2);

	// Test set failure conditions
	UASSERT(s.set("Zoop = Poop\nsome_other_setting", "false") == false);
	UASSERT(s.set("sneaky", "\"\"\"\njabberwocky = false") == false);
	UASSERT(s.set("hehe", "asdfasdf\n\"\"\"\nsomething = false") == false);

	// Test multiline settings
	UASSERT(group->get("ccc") == "testy\n   testa   ");

	UASSERT(s.get("blarg") ==
		"some multiline text\n"
		"     with leading whitespace!");

	// Test NoiseParams
	UASSERT(s.getEntry("np_terrain").is_group == false);

	NoiseParams np;
	UASSERT(s.getNoiseParams("np_terrain", np) == true);
	UASSERT(fabs(np.offset - 5) < 0.001);
	UASSERT(fabs(np.scale - 40) < 0.001);
	UASSERT(fabs(np.spread.X - 250) < 0.001);
	UASSERT(fabs(np.spread.Y - 250) < 0.001);
	UASSERT(fabs(np.spread.Z - 250) < 0.001);
	UASSERT(np.seed == 12341);
	UASSERT(np.octaves == 5);
	UASSERT(fabs(np.persist - 0.7) < 0.001);

	np.offset  = 3.5;
	np.octaves = 6;
	s.setNoiseParams("np_terrain", np);

	UASSERT(s.getEntry("np_terrain").is_group == true);

	// Test writing
	std::ostringstream os(std::ios_base::binary);
	is.clear();
	is.seekg(0);

	UASSERT(s.updateConfigObject(is, os, "", 0) == true);
	//printf(">>>> expected config:\n%s\n", TEST_CONFIG_TEXT_AFTER);
	//printf(">>>> actual config:\n%s\n", os.str().c_str());
#if __cplusplus < 201103L
	// This test only works in older C++ versions than C++11 because we use unordered_map
	UASSERT(os.str() == config_text_after);
#endif
	} catch (SettingNotFoundException &e) {
		UASSERT(!"Setting not found!");
	}
}


code:
/*
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 "log.h"
#include "socket.h"
#include "settings.h"

class TestSocket : public TestBase {
public:
	TestSocket()
	{
		if (INTERNET_SIMULATOR == false)
			TestManager::registerTestModule(this);
	}

	const char *getName() { return "TestSocket"; }

	void runTests(IGameDef *gamedef);

	void testIPv4Socket();
	void testIPv6Socket();

	static const int port = 30003;
};

static TestSocket g_test_instance;

void TestSocket::runTests(IGameDef *gamedef)
{
	TEST(testIPv4Socket);

	if (g_settings->getBool("enable_ipv6"))
		TEST(testIPv6Socket);
}

////////////////////////////////////////////////////////////////////////////////

void TestSocket::testIPv4Socket()
{
	Address address(0, 0, 0, 0, port);
	Address bind_addr(0, 0, 0, 0, port);

	/*
	 * Try to use the bind_address for servers with no localhost address
	 * For example: FreeBSD jails
	 */
	std::string bind_str = g_settings->get("bind_address");
	try {
		bind_addr.Resolve(bind_str.c_str());

		if (!bind_addr.isIPv6()) {
			address = bind_addr;
		}
	} catch (ResolveError &e) {
	}

	UDPSocket socket(false);
	socket.Bind(address);

	const char sendbuffer[] = "hello world!";
	/*
	 * If there is a bind address, use it.
	 * It's useful in container environments
	 */
	if (address != Address(0, 0, 0, 0, port))
		socket.Send(address, sendbuffer, sizeof(sendbuffer));
	else
		socket.Send(Address(127, 0, 0, 1, port), sendbuffer, sizeof(sendbuffer));

	sleep_ms(50);

	char rcvbuffer[256] = { 0 };
	Address sender;
	for (;;) {
		if (socket.Receive(sender, rcvbuffer, sizeof(rcvbuffer)) < 0)
			break;
	}
	//FIXME: This fails on some systems
	UASSERT(strncmp(sendbuffer, rcvbuffer, sizeof(sendbuffer)) == 0);

	if (address != Address(0, 0, 0, 0, port)) {
		UASSERT(sender.getAddress().sin_addr.s_addr ==
				address.getAddress().sin_addr.s_addr);
	} else {
		UASSERT(sender.getAddress().sin_addr.s_addr ==
				Address(127, 0, 0, 1, 0).getAddress().sin_addr.s_addr);
	}
}

void TestSocket::testIPv6Socket()
{
	Address address6((IPv6AddressBytes *)NULL, port);
	UDPSocket socket6;

	if (!socket6.init(true, true)) {
		/* Note: Failing to create an IPv6 socket is not technically an
		   error because the OS may not support IPv6 or it may
		   have been disabled. IPv6 is not /required/ by
		   minetest and therefore this should not cause the unit
		   test to fail
		*/
		dstream << "WARNING: IPv6 socket creation failed (unit test)"
			<< std::endl;
		return;
	}

	const char sendbuffer[] = "hello world!";
	IPv6AddressBytes bytes;
	bytes.bytes[15] = 1;

	socket6.Bind(address6);

	try {
		socket6.Send(Address(&bytes, port), sendbuffer, sizeof(sendbuffer));

		sleep_ms(50);

		char rcvbuffer[256] = { 0 };
		Address sender;

		for(;;) {
			if (socket6.Receive(sender, rcvbuffer, sizeof(rcvbuffer)) < 0)
				break;
		}
		//FIXME: This fails on some systems
		UASSERT(strncmp(sendbuffer, rcvbuffer, sizeof(sendbuffer)) == 0);
		UASSERT(memcmp(sender.getAddress6().sin6_addr.s6_addr,
				Address(&bytes, 0).getAddress6().sin6_addr.s6_addr, 16) == 0);
	} catch (SendFailedException &e) {
		errorstream << "IPv6 support enabled but not available!"
					<< std::endl;
	}
}


code:
/*
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 "threading/atomic.h"
#include "threading/semaphore.h"
#include "threading/thread.h"

class TestThreading : public TestBase {
public:
	TestThreading() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestThreading"; }
	void runTests(IGameDef *gamedef);

	void testStartStopWait();
	void testThreadKill();
	void testAtomicSemaphoreThread();
};

static TestThreading g_test_instance;

void TestThreading::runTests(IGameDef *gamedef)
{
	TEST(testStartStopWait);
	TEST(testThreadKill);
	TEST(testAtomicSemaphoreThread);
}

class SimpleTestThread : public Thread {
public:
	SimpleTestThread(unsigned int interval) :
		Thread("SimpleTest"),
		m_interval(interval)
	{
	}

private:
	void *run()
	{
		void *retval = this;

		if (isCurrentThread() == false)
			retval = (void *)0xBAD;

		while (!stopRequested())
			sleep_ms(m_interval);

		return retval;
	}

	unsigned int m_interval;
};

void TestThreading::testStartStopWait()
{
	void *thread_retval;
	SimpleTestThread *thread = new SimpleTestThread(25);

	// Try this a couple times, since a Thread should be reusable after waiting
	for (size_t i = 0; i != 5; i++) {
		// Can't wait() on a joined, stopped thread
		UASSERT(thread->wait() == false);

		// start() should work the first time, but not the second.
		UASSERT(thread->start() == true);
		UASSERT(thread->start() == false);

		UASSERT(thread->isRunning() == true);
		UASSERT(thread->isCurrentThread() == false);

		// Let it loop a few times...
		sleep_ms(70);

		// It's still running, so the return value shouldn't be available to us.
		UASSERT(thread->getReturnValue(&thread_retval) == false);

		// stop() should always succeed
		UASSERT(thread->stop() == true);

		// wait() only needs to wait the first time - the other two are no-ops.
		UASSERT(thread->wait() == true);
		UASSERT(thread->wait() == false);
		UASSERT(thread->wait() == false);

		// Now that the thread is stopped, we should be able to get the
		// return value, and it should be the object itself.
		thread_retval = NULL;
		UASSERT(thread->getReturnValue(&thread_retval) == true);
		UASSERT(thread_retval == thread);
	}

	delete thread;
}

void TestThreading::testThreadKill()
{
	SimpleTestThread *thread = new SimpleTestThread(300);

	UASSERT(thread->start() == true);

	// kill()ing is quite violent, so let's make sure our victim is sleeping
	// before we do this... so we don't corrupt the rest of the program's state
	sleep_ms(100);
	UASSERT(thread->kill() == true);

	// The state of the thread object should be reset if all went well
	UASSERT(thread->isRunning() == false);
	UASSERT(thread->start() == true);
	UASSERT(thread->stop() == true);
	UASSERT(thread->wait() == true);

	// kill() after already waiting should fail.
	UASSERT(thread->kill() == false);

	delete thread;
}

class AtomicTestThread : public Thread {
public:
	AtomicTestThread(Atomic<u32> &v, Semaphore &trigger) :
		Thread("AtomicTest"),
		val(v),
		trigger(trigger)
	{
	}

private:
	void *run()
	{
		trigger.wait();
		for (u32 i = 0; i < 0x10000; ++i)
			++val;
		return NULL;
	}

	Atomic<u32> &val;
	Semaphore &trigger;
};

void TestThreading::testAtomicSemaphoreThread()
{
	Atomic<u32> val;
	val = 0;
	Semaphore trigger;
	static const u8 num_threads = 4;

	AtomicTestThread *threads[num_threads];
	for (u8 i = 0; i < num_threads; ++i) {
		threads[i] = new AtomicTestThread(val, trigger);
		UASSERT(threads[i]->start());
	}

	trigger.post(num_threads);

	for (u8 i = 0; i < num_threads; ++i) {
		threads[i]->wait();
		delete threads[i];
	}

	UASSERT(val == num_threads * 0x10000);
}