test/config/test_configgenerator.cc
Rama master · réplica de lectura del repositorio original.
/*GRB*
Gerbera - https://gerbera.io/
test_configgenerator.cc - this file is part of Gerbera.
Copyright (C) 2016-2026 Gerbera Contributors
Gerbera is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
Gerbera 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Gerbera. If not, see <http://www.gnu.org/licenses/>.
$Id$
*/
#include "config/config_definition.h"
#include "config/config_generator.h"
#include <array>
#include <fstream>
#include <iterator>
#include <map>
#include <regex>
#include <string>
#include <vector>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using ::testing::MatchesRegex;
class ConfigGeneratorTest : public ::testing::Test {
public:
ConfigGeneratorTest() = default;
~ConfigGeneratorTest() override = default;
void SetUp() override
{
definition = std::make_shared<ConfigDefinition>();
definition->init(definition);
subject = new ConfigGenerator(definition, GERBERA_TEST, ConfigLevel::Base);
subject->init();
homePath = "/tmp";
configDir = ".config/gerbera";
prefixDir = "/usr/local/share/gerbera";
magicFile = "magic.file";
}
void TearDown() override
{
delete subject;
}
static std::string mockConfigXml(const std::string& mockFile)
{
std::ifstream t(mockFile);
std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
return str;
}
ConfigGenerator* subject;
std::shared_ptr<ConfigDefinition> definition;
std::string homePath;
std::string configDir;
std::string prefixDir;
std::string magicFile;
};
TEST_F(ConfigGeneratorTest, ConfigIncludesSelectedContainerIconsAndExistingCoverFallbacks)
{
const auto configs = std::array {
std::pair { ConfigLevel::Base, fs::path("/usr/share/gerbera") },
std::pair { ConfigLevel::Example, fs::path("/usr/share/gerbera") },
std::pair { ConfigLevel::Advanced, fs::path("/usr/share/gerbera") },
std::pair { ConfigLevel::Advanced, fs::path("/opt/music/share/gerbera") },
};
for (const auto& [level, dataDir] : configs) {
const std::map<ContainerIconTheme, fs::path> themes {
{ ContainerIconTheme::Gerbera, "container-icons/gerbera" }, { ContainerIconTheme::Techno, "container-icons/techno" }
};
for (const auto& [theme, themeDir] : themes) {
ConfigGenerator generator(definition, GERBERA_TEST, level, 0, 0, theme);
pugi::xml_document document;
ASSERT_TRUE(document.load_string(generator.generate(homePath, configDir, dataDir, magicFile).c_str()));
const auto resources = document.child("config").child("import").child("resources").child("container");
ASSERT_TRUE(resources);
EXPECT_EQ(resources.attribute("location").as_string(), (dataDir / themeDir).string());
std::vector<std::string> names;
for (const auto file : resources.children("add-file"))
names.emplace_back(file.attribute("name").as_string());
const std::vector<std::string> expected { "%upnpclass%-%filename%.png", "folder.jpg", "poster.jpg", "cover.jpg", "albumartsmall.jpg", "%upnpclass%.png" };
EXPECT_EQ(names, expected);
const auto containers = document.child("config").child("server").child("containers");
EXPECT_EQ(std::distance(containers.children("container").begin(), containers.children("container").end()), 3);
const std::map<std::string, std::string> images {
{ "/LastAdded", "recently-added.png" }, { "/LastModified", "recently-modified.png" }, { "/LastPlayed", "recently-played.png" }
};
for (const auto& [location, image] : images) {
const auto container = containers.find_child_by_attribute("container", "location", location.c_str());
ASSERT_TRUE(container) << location;
EXPECT_EQ(container.attribute("image").as_string(), (dataDir / themeDir / image).string());
}
const auto played = containers.find_child_by_attribute("container", "location", "/LastPlayed");
EXPECT_NE(played.attribute("image").as_string(), (dataDir / themeDir / "Audio.png").string());
EXPECT_EQ(fs::path(played.attribute("image").as_string()).extension(), ".png");
EXPECT_STREQ(played.attribute("upnp-shortcut").as_string(), "MUSIC_LAST_PLAYED");
EXPECT_STREQ(played.attribute("sort").as_string(), "-upnp:lastPlaybackTime");
EXPECT_STREQ(played.child_value("filter"), R"(upnp:class derivedfrom "object.item.audioItem" and upnp:lastPlaybackTime > "@last7")");
}
}
}
TEST_F(ConfigGeneratorTest, DefaultConfigKeepsCoverCandidatesWithoutATheme)
{
for (const auto level : std::array { ConfigLevel::Base, ConfigLevel::Example, ConfigLevel::Advanced }) {
ConfigGenerator generator(definition, GERBERA_TEST, level);
pugi::xml_document document;
ASSERT_TRUE(document.load_string(generator.generate(homePath, configDir, prefixDir, magicFile).c_str()));
const auto resources = document.child("config").child("import").child("resources").child("container");
EXPECT_STREQ(resources.attribute("location").as_string(), "");
for (const auto container : document.child("config").child("server").child("containers").children("container")) {
EXPECT_FALSE(container.attribute("image"));
}
std::vector<std::string> names;
for (const auto file : resources.children("add-file"))
names.emplace_back(file.attribute("name").as_string());
if (level == ConfigLevel::Advanced) {
const std::vector<std::string> covers { "folder.jpg", "poster.jpg", "cover.jpg", "albumartsmall.jpg" };
EXPECT_EQ(names, covers);
} else {
EXPECT_TRUE(names.empty());
}
}
}
TEST_F(ConfigGeneratorTest, AdvancedIconSettingsRespectSelectedSections)
{
for (const auto& section : std::array { "Import", "DynamicContainer" }) {
ConfigGenerator advanced(definition, GERBERA_TEST, ConfigLevel::Advanced, ConfigGenerator::makeSections(section), 0, ContainerIconTheme::Gerbera);
pugi::xml_document document;
ASSERT_TRUE(document.load_string(advanced.generate(homePath, configDir, prefixDir, magicFile).c_str()));
const auto resources = document.child("config").child("import").child("resources").child("container");
const auto containers = document.child("config").child("server").child("containers");
if (std::string(section) == "Import") {
EXPECT_EQ(resources.attribute("location").as_string(), (fs::path(prefixDir) / "container-icons" / "gerbera").string());
EXPECT_FALSE(containers);
} else {
EXPECT_FALSE(resources);
EXPECT_EQ(containers.first_child().attribute("image").as_string(), (fs::path(prefixDir) / "container-icons" / "gerbera" / "recently-added.png").string());
}
}
}
#if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER) && defined(HAVE_MYSQL) && defined(HAVE_MAGIC) && defined(HAVE_JS) && defined(HAVE_PGSQL)
TEST_F(ConfigGeneratorTest, GeneratesFullConfigXmlWithAllDefinitions)
{
#ifdef ONLINE_SERVICES
const std::string fileName = "fixtures/mock-config-all-os.xml";
#else
const std::string fileName = "fixtures/mock-config-all.xml";
#endif
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
std::string result = subject->generate(homePath, configDir, prefixDir, magicFile);
// remove UUID, for simple compare...TODO: mock UUID?
std::regex reg("<udn>uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}</udn>");
result = std::regex_replace(result, reg, "<udn/>");
auto outXml = std::pair { fileName, result };
EXPECT_EQ(mockXml, outXml);
}
#endif
#if !defined(HAVE_FFMPEG) && !defined(HAVE_FFMPEGTHUMBNAILER) && !defined(HAVE_MYSQL) && !defined(HAVE_MAGIC) && !defined(HAVE_JS) && !defined(ONLINE_SERVICES) && !defined(HAVE_PGSQL)
TEST_F(ConfigGeneratorTest, GeneratesConfigXmlWithDefaultDefinitions)
{
const std::string fileName = "fixtures/mock-config-minimal.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
std::string result = subject->generate(homePath, configDir, prefixDir, magicFile);
// remove UUID, for simple compare...TODO: mock UUID?
std::regex reg("<udn>uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}</udn>");
result = std::regex_replace(result, reg, "<udn/>");
auto outXml = std::pair { fileName, result };
EXPECT_EQ(mockXml, outXml);
}
#endif
#if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER) && defined(HAVE_MYSQL) && defined(HAVE_PGSQL)
TEST_F(ConfigGeneratorTest, GeneratesFullServerXmlWithAllDefinitions)
{
const std::string fileName = "fixtures/mock-server-all.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateServer(homePath, configDir, prefixDir);
auto config = subject->getNode("");
std::ostringstream result;
config->first_child().print(result, " ");
// remove UUID, for simple compare...TODO: mock UUID?
std::regex reg("<udn>uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}</udn>");
std::string resultStr = std::regex_replace(result.str(), reg, "<udn/>");
auto outXml = std::pair { fileName, resultStr };
EXPECT_EQ(mockXml, outXml);
}
#endif
TEST_F(ConfigGeneratorTest, GeneratesUiAllTheTime)
{
const std::string fileName = "fixtures/mock-ui.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateUi();
auto server = subject->getNode("/server");
std::ostringstream result;
server->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
TEST_F(ConfigGeneratorTest, GeneratesImportMappingsAllTheTime)
{
const std::string fileName = "fixtures/mock-import-mappings.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateMappings();
auto import = subject->getNode("/import");
std::ostringstream result;
import->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER)
TEST_F(ConfigGeneratorTest, GeneratesExtendedRuntimeXmlWithFFMPEG)
{
const std::string fileName = "fixtures/mock-extended-ffmpeg.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateExtendedRuntime();
auto server = subject->getNode("/server");
std::ostringstream result;
server->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#endif
#if !defined(HAVE_FFMPEG) || !defined(HAVE_FFMPEGTHUMBNAILER)
TEST_F(ConfigGeneratorTest, GeneratesExtendedRuntimeXmlWithoutFFMPEG)
{
const std::string fileName = "fixtures/mock-extended.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateExtendedRuntime();
auto server = subject->getNode("/server");
std::ostringstream result;
server->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#endif
#if defined(HAVE_MYSQL) && !defined(HAVE_PGSQL)
TEST_F(ConfigGeneratorTest, GeneratesDatabaseXmlWithMySQLAndSqlLite)
{
const std::string fileName = "fixtures/mock-database-mysql.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateDatabase(prefixDir);
auto server = subject->getNode("/server");
std::ostringstream result;
server->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#endif
#if !defined(HAVE_MYSQL) && !defined(HAVE_PGSQL)
TEST_F(ConfigGeneratorTest, GeneratesDatabaseXmlWithSqlLiteOnly)
{
const std::string fileName = "fixtures/mock-database-sqlite.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateDatabase(prefixDir);
auto server = subject->getNode("/server");
std::ostringstream result;
server->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#endif
#if defined(HAVE_MAGIC) && !defined(HAVE_JS) && !defined(ONLINE_SERVICES)
TEST_F(ConfigGeneratorTest, GeneratesImportWithMagicFile)
{
const std::string fileName = "fixtures/mock-import-magic.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateImport(prefixDir, configDir, magicFile);
auto config = subject->getNode("");
std::ostringstream result;
config->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#elif defined(HAVE_MAGIC) && defined(HAVE_JS) && !defined(ONLINE_SERVICES)
TEST_F(ConfigGeneratorTest, GeneratesImportWithMagicAndJS)
{
const std::string fileName = "fixtures/mock-import-magic-js.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateImport(prefixDir, configDir, magicFile);
auto config = subject->getNode("");
std::ostringstream result;
config->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#elif defined(HAVE_MAGIC) && defined(HAVE_JS) && defined(ONLINE_SERVICES)
TEST_F(ConfigGeneratorTest, GeneratesImportWithMagicJSandOnline)
{
const std::string fileName = "fixtures/mock-import-magic-js-online.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateImport(prefixDir, configDir, magicFile);
auto config = subject->getNode("");
std::ostringstream result;
config->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#elif !defined(HAVE_MAGIC) && !defined(HAVE_JS) && !defined(ONLINE_SERVICES)
TEST_F(ConfigGeneratorTest, GeneratesImportNoMagicJSnorOnline)
{
const std::string fileName = "fixtures/mock-import-none.xml";
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateImport(prefixDir, configDir, magicFile);
auto config = subject->getNode("");
std::ostringstream result;
config->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
#endif
#ifdef ONLINE_SERVICES
TEST_F(ConfigGeneratorTest, GeneratesOnlineContentEmpty)
{
std::string mockXml = "<online-services />\n";
subject->generateOnlineContent();
auto import = subject->getNode("/import");
std::ostringstream result;
import->first_child().print(result, " ");
EXPECT_EQ(mockXml, result.str());
}
#endif
TEST_F(ConfigGeneratorTest, GeneratesTranscodingProfilesAlways)
{
#ifdef HAVE_FFMPEG
const std::string fileName = "fixtures/mock-transcoding.xml";
#else
const std::string fileName = "fixtures/mock-transcoding-minimal.xml";
#endif
auto mockXml = std::pair { fileName, mockConfigXml(fileName) };
subject->generateTranscoding();
auto config = subject->getNode("");
std::ostringstream result;
config->first_child().print(result, " ");
auto outXml = std::pair { fileName, result.str() };
EXPECT_EQ(mockXml, outXml);
}
TEST_F(ConfigGeneratorTest, GeneratesUdnWithUUID)
{
subject->generateUdn();
auto server = subject->getNode("/server");
std::ostringstream result;
server->first_child().first_child().print(result, " ");
EXPECT_THAT(result.str(), MatchesRegex("^uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}"));
}
class ExampleConfigGeneratorTest : public ::testing::Test {
public:
ExampleConfigGeneratorTest() = default;
~ExampleConfigGeneratorTest() override = default;
static std::vector<std::string> pugixml116;
void SetUp() override
{
std::shared_ptr<ConfigDefinition> definition = std::make_shared<ConfigDefinition>();
definition->init(definition);
subject = new ConfigGenerator(definition, GERBERA_TEST, ConfigLevel::Example);
subject->init();
homePath = "/tmp";
configDir = ".config/gerbera";
prefixDir = "/usr/local/share/gerbera";
magicFile = "magic.file";
}
void TearDown() override
{
delete subject;
}
static std::string mockConfigXml(const std::string& mockFile)
{
std::ifstream t(mockFile);
std::string str((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
return str;
}
static std::string fixXML(const std::string& input)
{
// remove UUID, for simple compare...TODO: mock UUID?
std::regex reg("<udn>uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}</udn>");
auto result = std::regex_replace(input, reg, "<udn/>");
// pugixml 1.16 prints empty sections in short version
for (auto sec : pugixml116) {
std::regex reSec(fmt::format("\\>\\</{}\\>", sec), std::regex_constants::ECMAScript);
result = std::regex_replace(result, reSec, " />");
}
return result;
}
ConfigGenerator* subject;
std::string homePath;
std::string configDir;
std::string prefixDir;
std::string magicFile;
};
std::vector<std::string> ExampleConfigGeneratorTest::pugixml116 = {
"source-docs-link",
"ip",
"interface",
"modelURL",
"presentationURL",
"virtualURL",
"externalURL",
"socket",
"password",
"cache-dir",
"genre-map",
"auxdata",
"metadata",
"comment",
"visible-directories",
"order",
"metafile",
"resource",
"container",
"layout",
"directories",
};
#if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER) && defined(HAVE_MYSQL) && defined(HAVE_MAGIC) && defined(HAVE_JS) && defined(HAVE_EXIV2) && defined(HAVE_PGSQL) && !defined(HAVE_LASTFM)
TEST_F(ExampleConfigGeneratorTest, GeneratesFullConfigXmlWithExiv2AllDefinitions)
{
#ifdef ONLINE_SERVICES
std::string mockXml = mockConfigXml("fixtures/mock-example-exiv2-all-os.xml");
#else
std::string mockXml = mockConfigXml("fixtures/mock-example-exiv2-all.xml");
#endif
std::string result = subject->generate(homePath, configDir, prefixDir, magicFile);
#ifdef GERBERA_RELEASE
replaceAllString(mockXml, "https://docs.gerbera.io/en/latest/", "https://docs.gerbera.io/en/stable/");
#endif
mockXml = fixXML(mockXml);
result = fixXML(result);
EXPECT_STREQ(mockXml.c_str(), result.c_str());
}
#endif
#if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER) && defined(HAVE_MYSQL) && defined(HAVE_MAGIC) && defined(HAVE_JS) && defined(HAVE_EXIV2) && defined(HAVE_PGSQL) && defined(HAVE_LASTFM)
TEST_F(ExampleConfigGeneratorTest, GeneratesFullConfigXmlWithExiv2AndLastfmAllDefinitions)
{
#ifdef ONLINE_SERVICES
std::string mockXml = mockConfigXml("fixtures/mock-example-lastfm-all-os.xml");
#else
std::string mockXml = mockConfigXml("fixtures/mock-example-lastfm-all.xml");
#endif
std::string result = subject->generate(homePath, configDir, prefixDir, magicFile);
#ifdef GERBERA_RELEASE
replaceAllString(mockXml, "https://docs.gerbera.io/en/latest/", "https://docs.gerbera.io/en/stable/");
#endif
mockXml = fixXML(mockXml);
result = fixXML(result);
EXPECT_STREQ(mockXml.c_str(), result.c_str());
}
#endif
#if defined(HAVE_FFMPEG) && defined(HAVE_FFMPEGTHUMBNAILER) && defined(HAVE_MYSQL) && defined(HAVE_MAGIC) && defined(HAVE_JS) && !defined(HAVE_EXIV2) && defined(HAVE_PGSQL) && !defined(HAVE_LASTFM)
TEST_F(ExampleConfigGeneratorTest, GeneratesFullConfigXmlWithAllDefinitions)
{
#ifdef ONLINE_SERVICES
std::string mockXml = mockConfigXml("fixtures/mock-example-all-os.xml");
#else
std::string mockXml = mockConfigXml("fixtures/mock-example-all.xml");
#endif
#ifdef GERBERA_RELEASE
replaceAllString(mockXml, "https://docs.gerbera.io/en/latest/", "https://docs.gerbera.io/en/stable/");
#endif
std::string result = subject->generate(homePath, configDir, prefixDir, magicFile);
mockXml = fixXML(mockXml);
result = fixXML(result);
EXPECT_STREQ(mockXml.c_str(), result.c_str());
}
#endif
#if !defined(HAVE_FFMPEG) && !defined(HAVE_FFMPEGTHUMBNAILER) && !defined(HAVE_MYSQL) && !defined(HAVE_MAGIC) && !defined(HAVE_JS) && !defined(ONLINE_SERVICES) && !defined(HAVE_PGSQL) && !defined(HAVE_LASTFM)
TEST_F(ExampleConfigGeneratorTest, GeneratesConfigXmlWithDefaultDefinitions)
{
std::string mockXml = mockConfigXml("fixtures/mock-example-minimal.xml");
#ifdef GERBERA_RELEASE
replaceAllString(mockXml, "https://docs.gerbera.io/en/latest/", "https://docs.gerbera.io/en/stable/");
#endif
std::string result = subject->generate(homePath, configDir, prefixDir, magicFile);
mockXml = fixXML(mockXml);
result = fixXML(result);
EXPECT_STREQ(mockXml.c_str(), result.c_str());
}
#endif