minetest/src/unittest/test_modmetadatadatabase.cpp

360 lines
10 KiB
C++

/*
Minetest
Copyright (C) 2018 bendeutsch, Ben Deutsch <ben@bendeutsch.de>
Copyright (C) 2021 TurkeyMcMac, Jude Melton-Houghton <jwmhjwmh@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.
*/
// This file is an edited copy of test_authdatabase.cpp
#include "cmake_config.h"
#include "test.h"
#include <algorithm>
#include <cstdlib>
#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<std::string> 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<std::string> 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<std::string> 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<std::string> 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"));
}