Skip to content

Commit

Permalink
Improve tests for extension metadata.
Browse files Browse the repository at this point in the history
  • Loading branch information
Holt59 committed Aug 9, 2024
1 parent 0367e32 commit e2216d9
Show file tree
Hide file tree
Showing 15 changed files with 207 additions and 58 deletions.
5 changes: 5 additions & 0 deletions include/uibase/extensions/extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ class QDLLEXPORT IExtension
class QDLLEXPORT ExtensionFactory
{
public:
// load metadata from the given file, throws InvalidExtensionMetaDataException if the
// file does not exist or is invalid
//
static ExtensionMetaData loadMetaData(std::filesystem::path const& path);

// load an extension from the given directory, return a null-pointer if the extension
// could not be load
//
Expand Down
32 changes: 17 additions & 15 deletions src/extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ ExtensionMetaData::ExtensionMetaData(std::filesystem::path const& path,

// TODO: name of the key
// translation context
m_TranslationContext = jsonData["translationContext"].toString("");
m_TranslationContext = jsonData["translation-context"].toString("");

if (jsonData.contains("icon")) {
const QFileInfo icon{QDir(path), jsonData["icon"].toString()};
Expand Down Expand Up @@ -178,38 +178,40 @@ IExtension::IExtension(std::filesystem::path const& path, ExtensionMetaData&& me
: m_Path{path}, m_MetaData{std::move(metadata)}
{}

std::unique_ptr<IExtension>
ExtensionFactory::loadExtension(std::filesystem::path const& directory)
ExtensionMetaData ExtensionFactory::loadMetaData(std::filesystem::path const& path)
{
const auto metadataPath = directory / METADATA_FILENAME;

if (!exists(metadataPath)) {
log::warn("missing extension metadata in '{}'", directory.native());
return nullptr;
if (!exists(path)) {
throw InvalidExtensionMetaDataException(
std::format("metadata file '{}' not found", path));
}

// load the meta data
QJsonParseError jsonError;
QJsonDocument jsonMetaData;
{
QFile file(metadataPath);
QFile file(path);
if (!file.open(QFile::ReadOnly)) {
return {};
throw InvalidExtensionMetaDataException(
std::format("failed to open metadata file '{}'", path));
}

const auto jsonContent = file.readAll();
jsonMetaData = QJsonDocument::fromJson(jsonContent, &jsonError);
}

if (jsonMetaData.isNull()) {
log::warn("failed to read metadata from '{}': {}", metadataPath.native(),
jsonError.errorString());
return nullptr;
throw InvalidExtensionMetaDataException(
std::format("invalid metadata file '{}': {}", path, jsonError.errorString()));
}

return ExtensionMetaData(path.parent_path(), jsonMetaData.object());
}

std::unique_ptr<IExtension>
ExtensionFactory::loadExtension(std::filesystem::path const& directory)
{
try {
return loadExtension(directory,
ExtensionMetaData(directory, jsonMetaData.object()));
return loadExtension(directory, loadMetaData(directory / METADATA_FILENAME));
} catch (InvalidExtensionMetaDataException const& ex) {
log::warn("failed to load extension from '{}': invalid metadata ({})",
directory.native(), ex.what());
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.16)
add_executable(uibase-tests EXCLUDE_FROM_ALL)
target_sources(uibase-tests
PRIVATE
test_utils.h
test_utils.cpp
test_main.cpp
test_formatters.cpp
test_ifiletree.cpp
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions tests/data/extensions/mo2-example-extension/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"id": "mo2-example-extension",
"name": "Example Extension for UI Base Tests",
"version": "1.0.0",
"description": "ModOrganizer2 example extension for UI Base Tests.",
"author": {
"name": "Mod Organizer 2",
"homepage": "https://www.modorganizer.org/"
},
"icon": "icon.png",
"contributors": ["AL", "AnyOldName3", "Holt59", "Silarn"],
"type": "plugin",
"translation-context": "mo2-example-extension",
"content": {
"plugins": {
"autodetect": true
},
"translations": {
"autodetect": "translations"
}
}
}
52 changes: 15 additions & 37 deletions tests/test_extensions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,24 @@

#include <format>

using namespace MOBase;
#include "test_utils.h"

class TestMetaData : public ExtensionMetaData
{
public:
TestMetaData(std::filesystem::path const& path, QByteArray const& metadata)
: ExtensionMetaData(path, QJsonDocument::fromJson(metadata).object())
{}
};
using namespace MOBase;

TEST(ExtensionsTest, MetaData)
{
const auto metadata = TestMetaData({}, R"({
"id": "mo2-game-bethesda",
"name": "Elder Scrolls & Fallout Games",
"version": "1.0.0",
"description": "ModOrganizer2 support for The Elder Scrolls & Fallout games.",
"author": {
"name": "Mod Organizer 2",
"homepage": "https://www.modorganizer.org/"
},
"icon": "./tests/icon.png",
"contributors": [
"AL",
"AnyOldName3",
"Holt59",
"Silarn"
],
"type": "game",
"content": {
"plugins": {
"autodetect": true
},
"translations": {
"autodetect": "translations"
}
}
})");

EXPECT_EQ("mo2-game-bethesda", metadata.identifier());
EXPECT_EQ("Elder Scrolls & Fallout Games", metadata.name());
mo2::tests::TranslationHelper tr;

const auto metadata = ExtensionFactory::loadMetaData(
"./tests/data/extensions/mo2-example-extension/metadata.json");

tr.switchLanguage("en");
EXPECT_EQ("mo2-example-extension", metadata.identifier());
EXPECT_EQ("Example Extension for UI Base Tests", metadata.name());

tr.switchLanguage("fr");
EXPECT_EQ("mo2-example-extension", metadata.identifier());
EXPECT_EQ("Extension Démo pour les tests UI Base", metadata.name());

EXPECT_FALSE(metadata.icon().isNull());
}
11 changes: 7 additions & 4 deletions tests/test_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
#include <QCoreApplication>
#include <QTranslator>

#include <uibase/log.h>

int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
QTranslator translator;
if (translator.load("tests_fr", "tests/translations")) {
app.installTranslator(&translator);
}

MOBase::log::createDefault({.name = "./mo2-tests.logs",
.maxLevel = MOBase::log::Levels::Info,
.pattern = ""});

testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
7 changes: 7 additions & 0 deletions tests/test_strings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <format>

#include "test_utils.h"

using namespace MOBase;

TEST(StringsTest, IEquals)
Expand Down Expand Up @@ -37,6 +39,11 @@ TEST(StringsTest, IReplaceAll)
// this is more a tests of the tests
TEST(StringsTest, Translation)
{
mo2::tests::TranslationHelper tr;
ASSERT_EQ("Translate to French",
QCoreApplication::translate("uibase-tests", "Translate to French"));

tr.switchLanguage("fr");
ASSERT_EQ("Traduction en Français",
QCoreApplication::translate("uibase-tests", "Translate to French"));
}
31 changes: 31 additions & 0 deletions tests/test_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "test_utils.h"

#include <QCoreApplication>

namespace mo2::tests
{
TranslationHelper::TranslationHelper() {}

TranslationHelper::~TranslationHelper()
{
release();
}

void TranslationHelper::release()
{
if (m_Translator) {
QCoreApplication::removeTranslator(m_Translator.get());
m_Translator.reset();
}
}

void TranslationHelper::switchLanguage(const QString& lang)
{
m_Translator = std::make_unique<QTranslator>();
if (m_Translator->load("tests_" + lang, "tests/translations")) {
QCoreApplication::installTranslator(m_Translator.get());
} else {
m_Translator.reset();
}
}
} // namespace mo2::tests
27 changes: 27 additions & 0 deletions tests/test_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <memory>

#include <QString>
#include <QTranslator>

namespace mo2::tests
{

class TranslationHelper
{
public:
// create a new translation helper that can be used to switch language during tests
TranslationHelper();
~TranslationHelper();

// switch to the given language (should be available)
void switchLanguage(const QString& lang);

private:
std::unique_ptr<QTranslator> m_Translator;

void release();
};

} // namespace mo2::tests
46 changes: 46 additions & 0 deletions tests/translations/extract_translations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import json
from pathlib import Path

from PyQt6.lupdate.source_file import SourceFile
from PyQt6.lupdate.translation_file import TranslationFile
from PyQt6.lupdate.translations import Context, Message

folder = Path(__file__).parent

tr_files = [
TranslationFile(path, no_obsolete=False, no_summary=False, verbose=True)
for path in folder.glob("*.ts")
]

for metadata_path in folder.parent.joinpath("data", "extensions").glob("**/*.json"):
with open(metadata_path, "rb") as fp:
metadata = json.load(fp)

if "translation-context" not in metadata:
continue

source = SourceFile(filename=metadata_path)

context = Context(name=metadata["translation-context"])

for key in ("name", "description"):
if key not in metadata:
continue

context.messages.append(
Message(
filename=metadata_path,
line_nr=-1,
source=metadata[key],
comment=None,
numerus=None,
)
)

source.contexts.append(context)

for tr_file in tr_files:
tr_file.update(source)

for tr_file in tr_files:
tr_file.write()
Binary file modified tests/translations/tests_en.qm
Binary file not shown.
15 changes: 14 additions & 1 deletion tests/translations/tests_en.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>mo2-example-extension</name>
<message>
<location filename="../data/extensions/mo2-example-extension/metadata.json"/>
<source>Example Extension for UI Base Tests</source>
<translation></translation>
</message>
<message>
<location line="-2"/>
<source>ModOrganizer2 example extension for UI Base Tests.</source>
<translation></translation>
</message>
</context>
<context>
<name>uibase-tests</name>
<message>
<source>Translate to French</source>
<translation>Translate to French</translation>
<translation type="vanished">Translate to French</translation>
</message>
</context>
</TS>
Binary file modified tests/translations/tests_fr.qm
Binary file not shown.
15 changes: 14 additions & 1 deletion tests/translations/tests_fr.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="fr_FR">
<context>
<name>mo2-example-extension</name>
<message>
<location filename="../data/extensions/mo2-example-extension/metadata.json"/>
<source>Example Extension for UI Base Tests</source>
<translation>Extension Démo pour les tests UI Base</translation>
</message>
<message>
<location line="-2"/>
<source>ModOrganizer2 example extension for UI Base Tests.</source>
<translation type="unfinished">Exemple d&apos;extension ModOrganizer2 pour les tests UI Base.</translation>
</message>
</context>
<context>
<name>uibase-tests</name>
<message>
<source>Translate to French</source>
<translation>Traduction en Français</translation>
<translation type="vanished">Traduction en Français</translation>
</message>
</context>
</TS>

0 comments on commit e2216d9

Please sign in to comment.