Add unittests for item movement code (#11885)

This commit is contained in:
savilli 2022-09-27 22:22:11 +02:00 committed by GitHub
parent 3f801bc096
commit 907dcdcf7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 382 additions and 81 deletions

@ -482,6 +482,7 @@ void Server::init()
m_startup_server_map = nullptr; // Ownership moved to ServerEnvironment
m_env = new ServerEnvironment(servermap, m_script, this,
m_path_world, m_metrics_backend.get());
m_env->init();
m_inventory_mgr->setEnv(m_env);
m_clients.setEnv(m_env);

@ -389,17 +389,29 @@ void ActiveBlockList::update(std::vector<PlayerSAO*> &active_players,
static std::random_device seed;
ServerEnvironment::ServerEnvironment(ServerMap *map,
ServerScripting *scriptIface, Server *server,
ServerScripting *script_iface, Server *server,
const std::string &path_world, MetricsBackend *mb):
Environment(server),
m_map(map),
m_script(scriptIface),
m_script(script_iface),
m_server(server),
m_path_world(path_world),
m_rgen(seed())
{
m_step_time_counter = mb->addCounter(
"minetest_env_step_time", "Time spent in environment step (in microseconds)");
m_active_block_gauge = mb->addGauge(
"minetest_env_active_blocks", "Number of active blocks");
m_active_object_gauge = mb->addGauge(
"minetest_env_active_objects", "Number of active objects");
}
void ServerEnvironment::init()
{
// Determine which database backend to use
std::string conf_path = path_world + DIR_DELIM + "world.mt";
std::string conf_path = m_path_world + DIR_DELIM + "world.mt";
Settings conf;
std::string player_backend_name = "sqlite3";
@ -455,17 +467,8 @@ ServerEnvironment::ServerEnvironment(ServerMap *map,
<< "please read http://wiki.minetest.net/Database_backends." << std::endl;
}
m_player_database = openPlayerDatabase(player_backend_name, path_world, conf);
m_auth_database = openAuthDatabase(auth_backend_name, path_world, conf);
m_step_time_counter = mb->addCounter(
"minetest_env_step_time", "Time spent in environment step (in microseconds)");
m_active_block_gauge = mb->addGauge(
"minetest_env_active_blocks", "Number of active blocks");
m_active_object_gauge = mb->addGauge(
"minetest_env_active_objects", "Number of active objects");
m_player_database = openPlayerDatabase(player_backend_name, m_path_world, conf);
m_auth_database = openAuthDatabase(auth_backend_name, m_path_world, conf);
}
ServerEnvironment::~ServerEnvironment()
@ -478,7 +481,8 @@ ServerEnvironment::~ServerEnvironment()
deactivateFarObjects(true);
// Drop/delete map
m_map->drop();
if (m_map)
m_map->drop();
// Delete ActiveBlockModifiers
for (ABMWithState &m_abm : m_abms) {

@ -203,13 +203,15 @@ enum ClearObjectsMode {
CLEAR_OBJECTS_MODE_QUICK,
};
class ServerEnvironment : public Environment
class ServerEnvironment final : public Environment
{
public:
ServerEnvironment(ServerMap *map, ServerScripting *scriptIface,
ServerEnvironment(ServerMap *map, ServerScripting *script_iface,
Server *server, const std::string &path_world, MetricsBackend *mb);
~ServerEnvironment();
void init();
Map & getMap();
ServerMap & getServerMap();

@ -17,6 +17,7 @@ set (UNITTEST_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/test_mapnode.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_modchannels.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_modmetadatadatabase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_moveaction.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_nodedef.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_noderesolver.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test_noise.cpp
@ -48,6 +49,7 @@ set (UNITTEST_CLIENT_SRCS
set (TEST_WORLDDIR ${CMAKE_CURRENT_SOURCE_DIR}/test_world)
set (TEST_SUBGAME_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../../games/devtest)
set (TEST_MOD_PATH ${CMAKE_CURRENT_SOURCE_DIR}/test_mod)
set (HELPERS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/helpers)
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/test_config.h.in"

@ -0,0 +1,11 @@
minetest.register_allow_player_inventory_action(function(player, action, inventory, info)
if info.stack:get_name() == "default:water" then
return 0
end
if info.stack:get_name() == "default:lava" then
return 5
end
return info.stack:get_count()
end)

@ -0,0 +1,31 @@
/*
Minetest
Copyright (C) 2022 Minetest core developers & community
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 <activeobject.h>
class MockActiveObject : public ActiveObject
{
public:
MockActiveObject(u16 id) : ActiveObject(id) {}
virtual ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_TEST; }
virtual bool getCollisionBox(aabb3f *toset) const { return false; }
virtual bool getSelectionBox(aabb3f *toset) const { return false; }
virtual bool collideWithObjects() const { return false; }
};

@ -0,0 +1,41 @@
/*
Minetest
Copyright (C) 2022 Minetest core developers & community
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 <gamedef.h>
#include <inventorymanager.h>
class MockInventoryManager : public InventoryManager
{
public:
MockInventoryManager(IGameDef *gamedef) :
p1(gamedef->getItemDefManager()),
p2(gamedef->getItemDefManager())
{};
virtual Inventory* getInventory(const InventoryLocation &loc){
if (loc.type == InventoryLocation::PLAYER && loc.name == "p1")
return &p1;
if (loc.type == InventoryLocation::PLAYER && loc.name == "p2")
return &p2;
return nullptr;
}
Inventory p1;
Inventory p2;
};

@ -0,0 +1,31 @@
/*
Minetest
Copyright (C) 2022 Minetest core developers & community
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 <server.h>
class MockServer : public Server
{
public:
MockServer() : Server(TEST_WORLDDIR, SubgameSpec("fakespec", "fakespec"), true,
Address(), true, nullptr)
{}
private:
void SendChatMessage(session_t peer_id, const ChatMessage &message) {}
};

@ -0,0 +1,32 @@
/*
Minetest
Copyright (C) 2022 Minetest core developers & community
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 <server/serveractiveobject.h>
class MockServerActiveObject : public ServerActiveObject
{
public:
MockServerActiveObject(ServerEnvironment *env = nullptr, const v3f &p = v3f()) :
ServerActiveObject(env, p) {}
virtual ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_TEST; }
virtual bool getCollisionBox(aabb3f *toset) const { return false; }
virtual bool getSelectionBox(aabb3f *toset) const { return false; }
virtual bool collideWithObjects() const { return false; }
};

@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "test.h"
#include "activeobject.h"
#include "mock_activeobject.h"
class TestActiveObject : public TestBase
{
@ -39,20 +39,9 @@ void TestActiveObject::runTests(IGameDef *gamedef)
TEST(testAOAttributes);
}
class TestAO : public ActiveObject
{
public:
TestAO(u16 id) : ActiveObject(id) {}
virtual ActiveObjectType getType() const { return ACTIVEOBJECT_TYPE_TEST; }
virtual bool getCollisionBox(aabb3f *toset) const { return false; }
virtual bool getSelectionBox(aabb3f *toset) const { return false; }
virtual bool collideWithObjects() const { return false; }
};
void TestActiveObject::testAOAttributes()
{
TestAO ao(44);
MockActiveObject ao(44);
UASSERT(ao.getId() == 44);
ao.setId(558);

@ -5,3 +5,4 @@
#define TEST_WORLDDIR "@TEST_WORLDDIR@"
#define TEST_SUBGAME_PATH "@TEST_SUBGAME_PATH@"
#define TEST_MOD_PATH "@TEST_MOD_PATH@"
#define HELPERS_PATH "@HELPERS_PATH@"

@ -0,0 +1,177 @@
/*
Minetest
Copyright (C) 2022 Minetest core developers & community
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 "test_config.h"
#include "mock_inventorymanager.h"
#include "mock_server.h"
#include "mock_serveractiveobject.h"
#include <scripting_server.h>
class TestMoveAction : public TestBase
{
public:
TestMoveAction() { TestManager::registerTestModule(this); }
const char *getName() { return "TestMoveAction"; }
void runTests(IGameDef *gamedef);
void testMove(ServerActiveObject *obj, IGameDef *gamedef);
void testMoveSomewhere(ServerActiveObject *obj, IGameDef *gamedef);
void testMoveUnallowed(ServerActiveObject *obj, IGameDef *gamedef);
void testMovePartial(ServerActiveObject *obj, IGameDef *gamedef);
void testSwap(ServerActiveObject *obj, IGameDef *gamedef);
void testSwapFromUnallowed(ServerActiveObject *obj, IGameDef *gamedef);
void testSwapToUnallowed(ServerActiveObject *obj, IGameDef *gamedef);
};
static TestMoveAction g_test_instance;
void TestMoveAction::runTests(IGameDef *gamedef)
{
MockServer server;
ServerScripting server_scripting(&server);
server_scripting.loadMod(Server::getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
server_scripting.loadMod(std::string(HELPERS_PATH) + DIR_DELIM "helper_moveaction.lua", BUILTIN_MOD_NAME);
MetricsBackend mb;
ServerEnvironment server_env(nullptr, &server_scripting, &server, "", &mb);
MockServerActiveObject obj(&server_env);
TEST(testMove, &obj, gamedef);
TEST(testMoveSomewhere, &obj, gamedef);
TEST(testMoveUnallowed, &obj, gamedef);
TEST(testMovePartial, &obj, gamedef);
TEST(testSwap, &obj, gamedef);
TEST(testSwapFromUnallowed, &obj, gamedef);
TEST(testSwapToUnallowed, &obj, gamedef);
}
static ItemStack parse_itemstack(const char *s)
{
ItemStack item;
item.deSerialize(s, nullptr);
return item;
}
static void apply_action(const char *s, InventoryManager *inv, ServerActiveObject *obj, IGameDef *gamedef)
{
std::istringstream str(s);
InventoryAction *action = InventoryAction::deSerialize(str);
action->apply(inv, obj, gamedef);
delete action;
}
void TestMoveAction::testMove(ServerActiveObject *obj, IGameDef *gamedef)
{
MockInventoryManager inv(gamedef);
inv.p1.addList("main", 10)->addItem(0, parse_itemstack("default:stone 50"));
inv.p2.addList("main", 10);
apply_action("Move 20 player:p1 main 0 player:p2 main 0", &inv, obj, gamedef);
UASSERT(inv.p1.getList("main")->getItem(0).getItemString() == "default:stone 30");
UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:stone 20");
}
void TestMoveAction::testMoveSomewhere(ServerActiveObject *obj, IGameDef *gamedef)
{
MockInventoryManager inv(gamedef);
inv.p1.addList("main", 10)->addItem(0, parse_itemstack("default:stone 50"));
InventoryList *list = inv.p2.addList("main", 10);
list->addItem(0, parse_itemstack("default:brick 10"));
list->addItem(2, parse_itemstack("default:stone 85"));
apply_action("MoveSomewhere 50 player:p1 main 0 player:p2 main", &inv, obj, gamedef);
UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:brick 10");
UASSERT(inv.p2.getList("main")->getItem(1).getItemString() == "default:stone 36");
UASSERT(inv.p2.getList("main")->getItem(2).getItemString() == "default:stone 99");
}
void TestMoveAction::testMoveUnallowed(ServerActiveObject *obj, IGameDef *gamedef)
{
MockInventoryManager inv(gamedef);
inv.p1.addList("main", 10)->addItem(0, parse_itemstack("default:water 50"));
inv.p2.addList("main", 10);
apply_action("Move 20 player:p1 main 0 player:p2 main 0", &inv, obj, gamedef);
UASSERT(inv.p1.getList("main")->getItem(0).getItemString() == "default:water 50");
UASSERT(inv.p2.getList("main")->getItem(0).empty())
}
void TestMoveAction::testMovePartial(ServerActiveObject *obj, IGameDef *gamedef)
{
MockInventoryManager inv(gamedef);
inv.p1.addList("main", 10)->addItem(0, parse_itemstack("default:lava 50"));
inv.p2.addList("main", 10);
apply_action("Move 20 player:p1 main 0 player:p2 main 0", &inv, obj, gamedef);
UASSERT(inv.p1.getList("main")->getItem(0).getItemString() == "default:lava 45");
UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:lava 5");
}
void TestMoveAction::testSwap(ServerActiveObject *obj, IGameDef *gamedef)
{
MockInventoryManager inv(gamedef);
inv.p1.addList("main", 10)->addItem(0, parse_itemstack("default:stone 50"));
inv.p2.addList("main", 10)->addItem(0, parse_itemstack("default:brick 60"));
apply_action("Move 50 player:p1 main 0 player:p2 main 0", &inv, obj, gamedef);
UASSERT(inv.p1.getList("main")->getItem(0).getItemString() == "default:brick 60");
UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:stone 50");
}
void TestMoveAction::testSwapFromUnallowed(ServerActiveObject *obj, IGameDef *gamedef)
{
MockInventoryManager inv(gamedef);
inv.p1.addList("main", 10)->addItem(0, parse_itemstack("default:water 50"));
inv.p2.addList("main", 10)->addItem(0, parse_itemstack("default:brick 60"));
apply_action("Move 50 player:p1 main 0 player:p2 main 0", &inv, obj, gamedef);
UASSERT(inv.p1.getList("main")->getItem(0).getItemString() == "default:water 50");
UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:brick 60");
}
void TestMoveAction::testSwapToUnallowed(ServerActiveObject *obj, IGameDef *gamedef)
{
MockInventoryManager inv(gamedef);
inv.p1.addList("main", 10)->addItem(0, parse_itemstack("default:stone 50"));
inv.p2.addList("main", 10)->addItem(0, parse_itemstack("default:water 60"));
apply_action("Move 50 player:p1 main 0 player:p2 main 0", &inv, obj, gamedef);
UASSERT(inv.p1.getList("main")->getItem(0).getItemString() == "default:stone 50");
UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:water 60");
}

@ -17,27 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <server.h>
#include "test.h"
#include "test_config.h"
#include "mock_server.h"
#include "util/string.h"
#include "util/serialize.h"
class FakeServer : public Server
{
public:
FakeServer() : Server("fakeworld", SubgameSpec("fakespec", "fakespec"), true,
Address(), true, nullptr)
{
}
private:
void SendChatMessage(session_t peer_id, const ChatMessage &message)
{
// NOOP
}
};
class TestServerShutdownState : public TestBase
{
public:
@ -93,10 +80,10 @@ void TestServerShutdownState::testTrigger()
void TestServerShutdownState::testTick()
{
auto fakeServer = std::make_unique<FakeServer>();
auto server = std::make_unique<MockServer>();
Server::ShutdownState ss;
ss.trigger(28.0f, "testtrigger", true);
ss.tick(0.0f, fakeServer.get());
ss.tick(0.0f, server.get());
// Tick with no time should not change anything
UASSERT(!ss.is_requested);
@ -105,14 +92,14 @@ void TestServerShutdownState::testTick()
UASSERT(ss.m_timer == 28.0f);
// Tick 2 seconds
ss.tick(2.0f, fakeServer.get());
ss.tick(2.0f, server.get());
UASSERT(!ss.is_requested);
UASSERT(ss.should_reconnect);
UASSERT(ss.message == "testtrigger");
UASSERT(ss.m_timer == 26.0f);
// Tick remaining seconds + additional expire
ss.tick(26.1f, fakeServer.get());
ss.tick(26.1f, server.get());
UASSERT(ss.is_requested);
UASSERT(ss.should_reconnect);
UASSERT(ss.message == "testtrigger");

@ -17,23 +17,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "server/activeobjectmgr.h"
#include "test.h"
#include "mock_serveractiveobject.h"
#include <algorithm>
#include <queue>
#include "test.h"
#include "server/activeobjectmgr.h"
#include "profiler.h"
class TestServerActiveObject : public ServerActiveObject
{
public:
TestServerActiveObject(const v3f &p = v3f()) : ServerActiveObject(nullptr, p) {}
~TestServerActiveObject() = default;
ActiveObjectType getType() const override { return ACTIVEOBJECT_TYPE_TEST; }
bool getCollisionBox(aabb3f *toset) const override { return false; }
bool getSelectionBox(aabb3f *toset) const override { return false; }
bool collideWithObjects() const override { return false; }
};
class TestServerActiveObjectMgr : public TestBase
{
@ -86,9 +78,9 @@ void TestServerActiveObjectMgr::testFreeID()
// Register basic objects, ensure we never found
for (u8 i = 0; i < UINT8_MAX; i++) {
// Register an object
auto tsao = new TestServerActiveObject();
saomgr.registerObject(tsao);
aoids.push_back(tsao->getId());
auto sao = new MockServerActiveObject();
saomgr.registerObject(sao);
aoids.push_back(sao->getId());
// Ensure next id is not in registered list
UASSERT(std::find(aoids.begin(), aoids.end(), saomgr.getFreeId()) ==
@ -101,19 +93,19 @@ void TestServerActiveObjectMgr::testFreeID()
void TestServerActiveObjectMgr::testRegisterObject()
{
server::ActiveObjectMgr saomgr;
auto tsao = new TestServerActiveObject();
UASSERT(saomgr.registerObject(tsao));
auto sao = new MockServerActiveObject();
UASSERT(saomgr.registerObject(sao));
u16 id = tsao->getId();
u16 id = sao->getId();
auto tsaoToCompare = saomgr.getActiveObject(id);
UASSERT(tsaoToCompare->getId() == id);
UASSERT(tsaoToCompare == tsao);
auto saoToCompare = saomgr.getActiveObject(id);
UASSERT(saoToCompare->getId() == id);
UASSERT(saoToCompare == sao);
tsao = new TestServerActiveObject();
UASSERT(saomgr.registerObject(tsao));
UASSERT(saomgr.getActiveObject(tsao->getId()) == tsao);
UASSERT(saomgr.getActiveObject(tsao->getId()) != tsaoToCompare);
sao = new MockServerActiveObject();
UASSERT(saomgr.registerObject(sao));
UASSERT(saomgr.getActiveObject(sao->getId()) == sao);
UASSERT(saomgr.getActiveObject(sao->getId()) != saoToCompare);
clearSAOMgr(&saomgr);
}
@ -121,13 +113,13 @@ void TestServerActiveObjectMgr::testRegisterObject()
void TestServerActiveObjectMgr::testRemoveObject()
{
server::ActiveObjectMgr saomgr;
auto tsao = new TestServerActiveObject();
UASSERT(saomgr.registerObject(tsao));
auto sao = new MockServerActiveObject();
UASSERT(saomgr.registerObject(sao));
u16 id = tsao->getId();
u16 id = sao->getId();
UASSERT(saomgr.getActiveObject(id) != nullptr)
saomgr.removeObject(tsao->getId());
saomgr.removeObject(sao->getId());
UASSERT(saomgr.getActiveObject(id) == nullptr);
clearSAOMgr(&saomgr);
@ -145,7 +137,7 @@ void TestServerActiveObjectMgr::testGetObjectsInsideRadius()
};
for (const auto &p : sao_pos) {
saomgr.registerObject(new TestServerActiveObject(p));
saomgr.registerObject(new MockServerActiveObject(nullptr, p));
}
std::vector<ServerActiveObject *> result;
@ -183,7 +175,7 @@ void TestServerActiveObjectMgr::testGetAddedActiveObjectsAroundPos()
};
for (const auto &p : sao_pos) {
saomgr.registerObject(new TestServerActiveObject(p));
saomgr.registerObject(new MockServerActiveObject(nullptr, p));
}
std::queue<u16> result;