From b45df9d6a73d97671cbdd38d77e9b153a80fb458 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Tue, 5 May 2015 11:36:40 -0400 Subject: [PATCH] Tests: Add NodeResolver unittests Minor misc. NodeResolver cleanups Prefix faux content type constants for testing with t_ to avoid confusion or name collisions --- build/android/jni/Android.mk | 1 + src/nodedef.cpp | 19 +- src/unittest/CMakeLists.txt | 1 + src/unittest/test.cpp | 77 +++++++- src/unittest/test.h | 9 +- src/unittest/test_noderesolver.cpp | 253 +++++++++++++++++++++++++ src/unittest/test_voxelalgorithms.cpp | 8 +- src/unittest/test_voxelmanipulator.cpp | 6 +- 8 files changed, 349 insertions(+), 25 deletions(-) create mode 100644 src/unittest/test_noderesolver.cpp diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index e58cb82c8..17c6fa7dd 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -219,6 +219,7 @@ LOCAL_SRC_FILES := \ jni/src/unittest/test_inventory.cpp \ jni/src/unittest/test_mapnode.cpp \ jni/src/unittest/test_nodedef.cpp \ + jni/src/unittest/test_noderesolver.cpp \ jni/src/unittest/test_noise.cpp \ jni/src/unittest/test_objdef.cpp \ jni/src/unittest/test_profiler.cpp \ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 58566200e..48207da1f 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1346,7 +1346,8 @@ void CNodeDefManager::runNodeResolveCallbacks() //// NodeResolver //// -NodeResolver::NodeResolver() { +NodeResolver::NodeResolver() +{ m_ndef = NULL; m_nodenames_idx = 0; m_nnlistsizes_idx = 0; @@ -1379,14 +1380,14 @@ void NodeResolver::nodeResolveInternal() const std::string &NodeResolver::getNodeName(content_t c) const { - if (m_nodenames.size() == 0) { + if (c < m_nodenames.size()) + return m_nodenames[c]; + + if (m_ndef) return m_ndef->get(c).name; - } else { - if (c < m_nodenames.size()) - return m_nodenames[c]; - else - return m_ndef->get(CONTENT_UNKNOWN).name; - } + + static const std::string unknown_str("unknown"); + return unknown_str; } @@ -1395,7 +1396,7 @@ bool NodeResolver::getIdFromNrBacklog(content_t *result_out, { if (m_nodenames_idx == m_nodenames.size()) { *result_out = c_fallback; - errorstream << "Resolver: no more nodes in list" << std::endl; + errorstream << "NodeResolver: no more nodes in list" << std::endl; return false; } diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index ccb046bb9..6ec5eaea5 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -7,6 +7,7 @@ set (UNITTEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_inventory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mapnode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_nodedef.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_noderesolver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_noise.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_objdef.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_profiler.cpp diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 6fea0b8a0..57843da5e 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -25,9 +25,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #include "gamedef.h" -content_t CONTENT_STONE; -content_t CONTENT_GRASS; -content_t CONTENT_TORCH; +content_t t_CONTENT_STONE; +content_t t_CONTENT_GRASS; +content_t t_CONTENT_TORCH; +content_t t_CONTENT_WATER; +content_t t_CONTENT_LAVA; +content_t t_CONTENT_BRICK; //////////////////////////////////////////////////////////////////////////////// @@ -111,7 +114,7 @@ void TestGameDef::defineSomeNodes() f.tiledef[i].name = "default_stone.png"; f.is_ground_content = true; idef->registerItem(itemdef); - CONTENT_STONE = ndef->set(f.name, f); + t_CONTENT_STONE = ndef->set(f.name, f); //// Grass itemdef = ItemDefinition(); @@ -131,7 +134,7 @@ void TestGameDef::defineSomeNodes() f.tiledef[i].name = "default_dirt.png^default_grass_side.png"; f.is_ground_content = true; idef->registerItem(itemdef); - CONTENT_GRASS = ndef->set(f.name, f); + t_CONTENT_GRASS = ndef->set(f.name, f); //// Torch (minimal definition for lighting tests) itemdef = ItemDefinition(); @@ -144,7 +147,69 @@ void TestGameDef::defineSomeNodes() f.sunlight_propagates = true; f.light_source = LIGHT_MAX-1; idef->registerItem(itemdef); - CONTENT_TORCH = ndef->set(f.name, f); + t_CONTENT_TORCH = ndef->set(f.name, f); + + //// Water + itemdef = ItemDefinition(); + itemdef.type = ITEM_NODE; + itemdef.name = "default:water"; + itemdef.description = "Water"; + itemdef.inventory_image = "[inventorycube" + "{default_water.png" + "{default_water.png" + "{default_water.png"; + f = ContentFeatures(); + f.name = itemdef.name; + f.alpha = 128; + f.liquid_type = LIQUID_SOURCE; + f.liquid_viscosity = 4; + f.is_ground_content = true; + f.groups["liquids"] = 3; + for(int i = 0; i < 6; i++) + f.tiledef[i].name = "default_water.png"; + idef->registerItem(itemdef); + t_CONTENT_WATER = ndef->set(f.name, f); + + //// Lava + itemdef = ItemDefinition(); + itemdef.type = ITEM_NODE; + itemdef.name = "default:lava"; + itemdef.description = "Lava"; + itemdef.inventory_image = "[inventorycube" + "{default_lava.png" + "{default_lava.png" + "{default_lava.png"; + f = ContentFeatures(); + f.name = itemdef.name; + f.alpha = 128; + f.liquid_type = LIQUID_SOURCE; + f.liquid_viscosity = 7; + f.light_source = LIGHT_MAX-1; + f.is_ground_content = true; + f.groups["liquids"] = 3; + for(int i = 0; i < 6; i++) + f.tiledef[i].name = "default_lava.png"; + idef->registerItem(itemdef); + t_CONTENT_LAVA = ndef->set(f.name, f); + + + //// Brick + itemdef = ItemDefinition(); + itemdef.type = ITEM_NODE; + itemdef.name = "default:brick"; + itemdef.description = "Brick"; + itemdef.groups["cracky"] = 3; + itemdef.inventory_image = "[inventorycube" + "{default_brick.png" + "{default_brick.png" + "{default_brick.png"; + f = ContentFeatures(); + f.name = itemdef.name; + for(int i = 0; i < 6; i++) + f.tiledef[i].name = "default_brick.png"; + f.is_ground_content = true; + idef->registerItem(itemdef); + t_CONTENT_BRICK = ndef->set(f.name, f); } //// diff --git a/src/unittest/test.h b/src/unittest/test.h index c9a5a2e9a..8219e30fc 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -124,9 +124,12 @@ public: }; // A few item and node definitions for those tests that need them -extern content_t CONTENT_STONE; -extern content_t CONTENT_GRASS; -extern content_t CONTENT_TORCH; +extern content_t t_CONTENT_STONE; +extern content_t t_CONTENT_GRASS; +extern content_t t_CONTENT_TORCH; +extern content_t t_CONTENT_WATER; +extern content_t t_CONTENT_LAVA; +extern content_t t_CONTENT_BRICK; void run_tests(); diff --git a/src/unittest/test_noderesolver.cpp b/src/unittest/test_noderesolver.cpp new file mode 100644 index 000000000..c440b2399 --- /dev/null +++ b/src/unittest/test_noderesolver.cpp @@ -0,0 +1,253 @@ +/* +Minetest +Copyright (C) 2010-2014 kwolekr, Ryan Kwolek + +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 "gamedef.h" +#include "nodedef.h" + + +class TestNodeResolver : public TestBase { +public: + TestNodeResolver() { TestManager::registerTestModule(this); } + const char *getName() { return "TestNodeResolver"; } + + void runTests(IGameDef *gamedef); + + void testNodeResolving(INodeDefManager *ndef); + void testPendingResolveCancellation(INodeDefManager *ndef); + void testDirectResolveMethod(INodeDefManager *ndef); + void testNoneResolveMethod(INodeDefManager *ndef); +}; + +static TestNodeResolver g_test_instance; + +void TestNodeResolver::runTests(IGameDef *gamedef) +{ + IWritableNodeDefManager *parent_ndef; + INodeDefManager *ndef; + + parent_ndef = (IWritableNodeDefManager *)gamedef->getNodeDefManager(); + + ndef = parent_ndef->clone(); + TEST(testNodeResolving, ndef); + delete ndef; + + ndef = parent_ndef->clone(); + TEST(testPendingResolveCancellation, ndef); + delete ndef; + + ndef = parent_ndef->clone(); + TEST(testDirectResolveMethod, ndef); + delete ndef; + + ndef = parent_ndef->clone(); + TEST(testNoneResolveMethod, ndef); + delete 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 test_nr_list; + std::vector test_nr_list_group; + std::vector test_nr_list_required; + std::vector 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(INodeDefManager *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, NODE_RESOLVE_DEFERRED); + 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(INodeDefManager *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, NODE_RESOLVE_DEFERRED); + + 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, NODE_RESOLVE_DEFERRED); + + 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); +} + + +void TestNodeResolver::testDirectResolveMethod(INodeDefManager *ndef) +{ + Foobaz foobaz; + + foobaz.m_nodenames.push_back("default:dirt_with_grass"); + foobaz.m_nodenames.push_back("default:abloobloobloo"); + + UASSERTEQ(std::string, foobaz.getNodeName(1), "default:abloobloobloo"); + + ndef->pendNodeResolve(&foobaz, NODE_RESOLVE_DIRECT); + + UASSERTEQ(content_t, foobaz.test_content1, t_CONTENT_GRASS); + UASSERTEQ(content_t, foobaz.test_content2, CONTENT_IGNORE); + + // We expect this to be *different* because the resolution of this node had + // failed. The internal nodename buffer is cleared and lookups should now + // use the nodedef manager. + UASSERT(foobaz.getNodeName(1) != "default:abloobloobloo"); +} + + +void TestNodeResolver::testNoneResolveMethod(INodeDefManager *ndef) +{ + Foobaz foobaz; + + foobaz.m_nodenames.push_back("default:dirt_with_grass"); + foobaz.m_nodenames.push_back("default:abloobloobloo"); + + ndef->pendNodeResolve(&foobaz, NODE_RESOLVE_NONE); + + UASSERTEQ(std::string, foobaz.getNodeName(1), "default:abloobloobloo"); +} diff --git a/src/unittest/test_voxelalgorithms.cpp b/src/unittest/test_voxelalgorithms.cpp index 2093ff5f8..31b9cadd5 100644 --- a/src/unittest/test_voxelalgorithms.cpp +++ b/src/unittest/test_voxelalgorithms.cpp @@ -69,7 +69,7 @@ void TestVoxelAlgorithms::testPropogateSunlight(INodeDefManager *ndef) == LIGHT_SUN); } - v.setNodeNoRef(v3s16(0,0,0), MapNode(CONTENT_STONE)); + v.setNodeNoRef(v3s16(0,0,0), MapNode(t_CONTENT_STONE)); { std::set light_sources; @@ -91,7 +91,7 @@ void TestVoxelAlgorithms::testPropogateSunlight(INodeDefManager *ndef) == 0); } - v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_STONE)); + v.setNodeNoRef(v3s16(1,3,2), MapNode(t_CONTENT_STONE)); { std::set light_sources; @@ -180,8 +180,8 @@ void TestVoxelAlgorithms::testClearLightAndCollectSources(INodeDefManager *ndef) } VoxelArea a(v3s16(0,0,0), v3s16(2,2,2)); - v.setNodeNoRef(v3s16(0,0,0), MapNode(CONTENT_STONE)); - v.setNodeNoRef(v3s16(1,1,1), MapNode(CONTENT_TORCH)); + v.setNodeNoRef(v3s16(0,0,0), MapNode(t_CONTENT_STONE)); + v.setNodeNoRef(v3s16(1,1,1), MapNode(t_CONTENT_TORCH)); { MapNode n(CONTENT_AIR); diff --git a/src/unittest/test_voxelmanipulator.cpp b/src/unittest/test_voxelmanipulator.cpp index d219e62e0..7f8ba3849 100644 --- a/src/unittest/test_voxelmanipulator.cpp +++ b/src/unittest/test_voxelmanipulator.cpp @@ -87,10 +87,10 @@ void TestVoxelManipulator::testVoxelManipulator(INodeDefManager *nodedef) v.print(infostream, nodedef); infostream << "*** Setting (-1,0,-1)=2 ***" << std::endl; - v.setNodeNoRef(v3s16(-1,0,-1), MapNode(CONTENT_GRASS)); + v.setNodeNoRef(v3s16(-1,0,-1), MapNode(t_CONTENT_GRASS)); v.print(infostream, nodedef); - UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == CONTENT_GRASS); + UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == t_CONTENT_GRASS); infostream << "*** Reading from inexistent (0,0,-1) ***" << std::endl; @@ -103,6 +103,6 @@ void TestVoxelManipulator::testVoxelManipulator(INodeDefManager *nodedef) v.addArea(a); v.print(infostream, nodedef); - UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == CONTENT_GRASS); + UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == t_CONTENT_GRASS); EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,1,1))); }