/* Minetest Copyright (C) 2018 bendeutsch, Ben Deutsch Copyright (C) 2021 TurkeyMcMac, Jude Melton-Houghton 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. */ // This file is an edited copy of test_authdatabase.cpp #include "cmake_config.h" #include "test.h" #include #include #include "database/database-dummy.h" #include "database/database-files.h" #include "database/database-sqlite3.h" #if USE_POSTGRESQL #include "database/database-postgresql.h" #endif #include "filesys.h" namespace { // Anonymous namespace to create classes that are only // visible to this file // // These are helpers that return a *ModMetadataDatabase and // allow us to run the same tests on different databases and // database acquisition strategies. class ModMetadataDatabaseProvider { public: virtual ~ModMetadataDatabaseProvider() = default; virtual ModMetadataDatabase *getModMetadataDatabase() = 0; }; class FixedProvider : public ModMetadataDatabaseProvider { public: FixedProvider(ModMetadataDatabase *mod_meta_db) : mod_meta_db(mod_meta_db){}; virtual ~FixedProvider(){}; virtual ModMetadataDatabase *getModMetadataDatabase() { return mod_meta_db; }; private: ModMetadataDatabase *mod_meta_db; }; class FilesProvider : public ModMetadataDatabaseProvider { public: FilesProvider(const std::string &dir) : dir(dir){}; virtual ~FilesProvider() { if (mod_meta_db) mod_meta_db->endSave(); delete mod_meta_db; } virtual ModMetadataDatabase *getModMetadataDatabase() { if (mod_meta_db) mod_meta_db->endSave(); delete mod_meta_db; mod_meta_db = new ModMetadataDatabaseFiles(dir); mod_meta_db->beginSave(); return mod_meta_db; }; private: std::string dir; ModMetadataDatabase *mod_meta_db = nullptr; }; class SQLite3Provider : public ModMetadataDatabaseProvider { public: SQLite3Provider(const std::string &dir) : dir(dir){}; virtual ~SQLite3Provider() { if (mod_meta_db) mod_meta_db->endSave(); delete mod_meta_db; } virtual ModMetadataDatabase *getModMetadataDatabase() { if (mod_meta_db) mod_meta_db->endSave(); delete mod_meta_db; mod_meta_db = new ModMetadataDatabaseSQLite3(dir); mod_meta_db->beginSave(); return mod_meta_db; }; private: std::string dir; ModMetadataDatabase *mod_meta_db = nullptr; }; #if USE_POSTGRESQL void clearPostgreSQLDatabase(const std::string &connect_string) { ModMetadataDatabasePostgreSQL db(connect_string); std::vector modnames; db.beginSave(); db.listMods(&modnames); for (const std::string &modname : modnames) db.removeModEntries(modname); db.endSave(); } class PostgreSQLProvider : public ModMetadataDatabaseProvider { public: PostgreSQLProvider(const std::string &connect_string): m_connect_string(connect_string) {} ~PostgreSQLProvider() { if (m_db) m_db->endSave(); delete m_db; } ModMetadataDatabase *getModMetadataDatabase() override { if (m_db) m_db->endSave(); delete m_db; m_db = new ModMetadataDatabasePostgreSQL(m_connect_string); m_db->beginSave(); return m_db; }; private: std::string m_connect_string; ModMetadataDatabase *m_db = nullptr; }; #endif // USE_POSTGRESQL } class TestModMetadataDatabase : public TestBase { public: TestModMetadataDatabase() { TestManager::registerTestModule(this); } const char *getName() { return "TestModMetadataDatabase"; } void runTests(IGameDef *gamedef); void runTestsForCurrentDB(); void testRecallFail(); void testCreate(); void testRecall(); void testChange(); void testRecallChanged(); void testListMods(); void testRemove(); private: ModMetadataDatabaseProvider *mod_meta_provider; }; static TestModMetadataDatabase g_test_instance; void TestModMetadataDatabase::runTests(IGameDef *gamedef) { // fixed directory, for persistence thread_local const std::string test_dir = getTestTempDirectory(); // Each set of tests is run twice for each database type except dummy: // one where we reuse the same ModMetadataDatabase object (to test local caching), // and one where we create a new ModMetadataDatabase object for each call // (to test actual persistence). // Since the dummy database is only in-memory, it has no persistence to test. ModMetadataDatabase *mod_meta_db; rawstream << "-------- Dummy database (same object only)" << std::endl; mod_meta_db = new Database_Dummy(); mod_meta_provider = new FixedProvider(mod_meta_db); runTestsForCurrentDB(); delete mod_meta_db; delete mod_meta_provider; rawstream << "-------- Files database (same object)" << std::endl; mod_meta_db = new ModMetadataDatabaseFiles(test_dir); mod_meta_provider = new FixedProvider(mod_meta_db); runTestsForCurrentDB(); delete mod_meta_db; delete mod_meta_provider; // reset database fs::RecursiveDelete(test_dir + DIR_DELIM + "mod_storage"); rawstream << "-------- Files database (new objects)" << std::endl; mod_meta_provider = new FilesProvider(test_dir); runTestsForCurrentDB(); delete mod_meta_provider; rawstream << "-------- SQLite3 database (same object)" << std::endl; mod_meta_db = new ModMetadataDatabaseSQLite3(test_dir); mod_meta_provider = new FixedProvider(mod_meta_db); runTestsForCurrentDB(); delete mod_meta_db; delete mod_meta_provider; // reset database fs::DeleteSingleFileOrEmptyDirectory(test_dir + DIR_DELIM + "mod_storage.sqlite"); rawstream << "-------- SQLite3 database (new objects)" << std::endl; mod_meta_provider = new SQLite3Provider(test_dir); runTestsForCurrentDB(); delete mod_meta_provider; #if USE_POSTGRESQL const char *env_postgresql_connect_string = getenv("MINETEST_POSTGRESQL_CONNECT_STRING"); if (env_postgresql_connect_string) { std::string connect_string(env_postgresql_connect_string); rawstream << "-------- PostgreSQL database (same object)" << std::endl; clearPostgreSQLDatabase(connect_string); mod_meta_db = new ModMetadataDatabasePostgreSQL(connect_string); mod_meta_provider = new FixedProvider(mod_meta_db); runTestsForCurrentDB(); delete mod_meta_db; delete mod_meta_provider; rawstream << "-------- PostgreSQL database (new objects)" << std::endl; clearPostgreSQLDatabase(connect_string); mod_meta_provider = new PostgreSQLProvider(connect_string); runTestsForCurrentDB(); delete mod_meta_provider; } #endif // USE_POSTGRESQL } //////////////////////////////////////////////////////////////////////////////// void TestModMetadataDatabase::runTestsForCurrentDB() { TEST(testRecallFail); TEST(testCreate); TEST(testRecall); TEST(testChange); TEST(testRecallChanged); TEST(testListMods); TEST(testRemove); TEST(testRecallFail); } void TestModMetadataDatabase::testRecallFail() { ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); StringMap recalled; std::vector recalled_keys; mod_meta_db->getModEntries("mod1", &recalled); mod_meta_db->getModKeys("mod1", &recalled_keys); UASSERT(recalled.empty()); UASSERT(recalled_keys.empty()); std::string key1_value; UASSERT(!mod_meta_db->getModEntry("mod1", "key1", &key1_value)); UASSERT(!mod_meta_db->hasModEntry("mod1", "key1")); } void TestModMetadataDatabase::testCreate() { ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); UASSERT(mod_meta_db->setModEntry("mod1", "key1", "value1")); } void TestModMetadataDatabase::testRecall() { ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); StringMap recalled; std::vector recalled_keys; mod_meta_db->getModEntries("mod1", &recalled); mod_meta_db->getModKeys("mod1", &recalled_keys); UASSERTCMP(std::size_t, ==, recalled.size(), 1); UASSERTCMP(std::size_t, ==, recalled_keys.size(), 1); UASSERTCMP(std::string, ==, recalled["key1"], "value1"); UASSERTCMP(std::string, ==, recalled_keys[0], "key1"); std::string key1_value; UASSERT(mod_meta_db->getModEntry("mod1", "key1", &key1_value)); UASSERTCMP(std::string, ==, key1_value, "value1"); UASSERT(mod_meta_db->hasModEntry("mod1", "key1")); } void TestModMetadataDatabase::testChange() { ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); UASSERT(mod_meta_db->setModEntry("mod1", "key1", "value2")); } void TestModMetadataDatabase::testRecallChanged() { ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); StringMap recalled; mod_meta_db->getModEntries("mod1", &recalled); UASSERTCMP(std::size_t, ==, recalled.size(), 1); UASSERTCMP(std::string, ==, recalled["key1"], "value2"); std::string key1_value; UASSERT(mod_meta_db->getModEntry("mod1", "key1", &key1_value)); UASSERTCMP(std::string, ==, key1_value, "value2"); UASSERT(mod_meta_db->hasModEntry("mod1", "key1")); } void TestModMetadataDatabase::testListMods() { ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); UASSERT(mod_meta_db->setModEntry("mod2", "key1", "value1")); UASSERT(mod_meta_db->setModEntry("mod2", "key2", "value1")); std::vector mod_list; mod_meta_db->listMods(&mod_list); UASSERTCMP(size_t, ==, mod_list.size(), 2); UASSERT(std::find(mod_list.cbegin(), mod_list.cend(), "mod1") != mod_list.cend()); UASSERT(std::find(mod_list.cbegin(), mod_list.cend(), "mod2") != mod_list.cend()); } void TestModMetadataDatabase::testRemove() { ModMetadataDatabase *mod_meta_db = mod_meta_provider->getModMetadataDatabase(); UASSERT(mod_meta_db->removeModEntry("mod1", "key1")); UASSERT(!mod_meta_db->removeModEntries("mod1")); UASSERT(mod_meta_db->removeModEntries("mod2")); }