/* Minetest Copyright (C) 2024 sfan5 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/container.h" class TestDataStructures : public TestBase { public: TestDataStructures() { TestManager::registerTestModule(this); } const char *getName() { return "TestDataStructures"; } void runTests(IGameDef *gamedef); void testMap1(); void testMap2(); void testMap3(); void testMap4(); void testMap5(); }; static TestDataStructures g_test_instance; void TestDataStructures::runTests(IGameDef *gamedef) { rawstream << "-------- ModifySafeMap" << std::endl; TEST(testMap1); TEST(testMap2); TEST(testMap3); TEST(testMap4); TEST(testMap5); } namespace { struct TrackerState { bool copied = false; bool deleted = false; }; class Tracker { TrackerState *res = nullptr; inline void trackDeletion() { res && (res->deleted = true); } public: Tracker() {} Tracker(TrackerState &res) : res(&res) {} operator bool() const { return !!res; } Tracker(const Tracker &other) { *this = other; } Tracker &operator=(const Tracker &other) { trackDeletion(); res = other.res; res && (res->copied = true); return *this; } Tracker(Tracker &&other) { *this = std::move(other); } Tracker &operator=(Tracker &&other) { trackDeletion(); res = other.res; other.res = nullptr; return *this; } ~Tracker() { trackDeletion(); } }; } void TestDataStructures::testMap1() { ModifySafeMap map; TrackerState t0, t1; // no-copy put map.put(1, Tracker(t0)); UASSERT(!t0.copied); UASSERT(!t0.deleted); // overwrite during iter bool once = false; for (auto &it : map.iter()) { (void)it; map.put(1, Tracker(t1)); UASSERT(t0.deleted); UASSERT(!t1.copied); UASSERT(!t1.deleted); if ((once |= 1)) break; } UASSERT(once); map.clear(); // ASan complains about stack-use-after-scope otherwise } void TestDataStructures::testMap2() { ModifySafeMap map; TrackerState t0, t1; // overwrite map.put(1, Tracker(t0)); map.put(1, Tracker(t1)); UASSERT(t0.deleted); UASSERT(!t1.copied); UASSERT(!t1.deleted); map.clear(); } void TestDataStructures::testMap3() { ModifySafeMap map; TrackerState t0, t1; // take map.put(1, Tracker(t0)); { auto v = map.take(1); UASSERT(!t0.copied); UASSERT(!t0.deleted); } UASSERT(t0.deleted); // remove during iter map.put(1, Tracker(t1)); for (auto &it : map.iter()) { (void)it; map.remove(1); UASSERT(t1.deleted); break; } } void TestDataStructures::testMap4() { ModifySafeMap map; // overwrite + take during iter map.put(1, 100); for (auto &it : map.iter()) { (void)it; map.put(1, 200); u32 taken = map.take(1); UASSERTEQ(u32, taken, 200); break; } UASSERT(map.get(1) == u32()); UASSERTEQ(size_t, map.size(), 0); } void TestDataStructures::testMap5() { ModifySafeMap map; // overwrite 2x during iter map.put(9001, 9001); for (auto &it : map.iter()) { (void)it; map.put(1, 100); map.put(1, 200); UASSERTEQ(u32, map.get(1), 200); break; } }