157 lines
8.1 KiB
C++
157 lines
8.1 KiB
C++
#include "mainwindow.h"
|
|
#include <QApplication>
|
|
#include <QComboBox>
|
|
#include <QDir>
|
|
#include <QFile>
|
|
#include <QLineEdit>
|
|
#include <QMessageBox>
|
|
#include <QSignalBlocker>
|
|
#include <QTemporaryDir>
|
|
#include <QTimer>
|
|
#include <QTreeWidget>
|
|
|
|
static void require(bool condition, const char *message)
|
|
{
|
|
if (!condition) qFatal("%s", message);
|
|
}
|
|
|
|
static void writeFile(const QString &path, const QString &text)
|
|
{
|
|
QFile file(path);
|
|
require(file.open(QIODevice::WriteOnly), "Cannot create fixture");
|
|
require(file.write(text.toUtf8()) == text.toUtf8().size(), "Cannot write fixture");
|
|
}
|
|
|
|
static QString readFile(const QString &path)
|
|
{
|
|
QFile file(path);
|
|
require(file.open(QIODevice::ReadOnly), "Original file is missing");
|
|
return QString::fromUtf8(file.readAll());
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
QTemporaryDir home;
|
|
require(home.isValid(), "Cannot create test directory");
|
|
qputenv("HOME", home.path().toUtf8());
|
|
QApplication application(argc, argv);
|
|
const QString project = home.path() + "/project";
|
|
const QString directory = project + "/game/tl/german";
|
|
require(QDir().mkpath(directory), "Cannot create project");
|
|
const QString common = "translate german strings:\n\n # renpy/common/00action_file.rpy:42\n old \"Yes\"\n new \"Ja\"\n";
|
|
const QString unknown = "translate german strings:\n\n old \"Other\"\n new \"Anderes\"\n";
|
|
writeFile(directory + "/common.rpy", common);
|
|
writeFile(directory + "/missing.rpy", common);
|
|
writeFile(directory + "/unknown.rpy", unknown);
|
|
const QString quoted = "translate german strings:\n\n # renpy/common/test.rpy:50\n old \"A \\\"quote\\\" [name]\"\n new \"Ein \\\"Zitat\\\" [name]\"\n";
|
|
writeFile(directory + "/quoted.rpy", quoted);
|
|
const QString dialogue = "# game/test.rpy:55\ntranslate german quoted_dialogue:\n\n # e \"A \\\"quote\\\" [name]\"\n e \"Ein \\\"Zitat\\\" [name]\"\n";
|
|
writeFile(directory + "/dialogue.rpy", dialogue);
|
|
|
|
MainWindow window;
|
|
auto *tree = window.findChild<QTreeWidget *>("treeWidget");
|
|
auto *languages = window.findChild<QComboBox *>("languageCombo");
|
|
auto *editor = window.findChild<QLineEdit *>("translationEdit");
|
|
window.findChild<QLineEdit *>("projectDir")->setText(project);
|
|
{ const QSignalBlocker blocker(languages); languages->addItem("german"); }
|
|
require(QMetaObject::invokeMethod(&window, "on_languageCombo_currentTextChanged", Qt::DirectConnection,
|
|
Q_ARG(QString, "german")), "Cannot load project");
|
|
auto find = [tree](const QString &name) {
|
|
for (int i = 0; i < tree->topLevelItemCount(); ++i) {
|
|
if (tree->topLevelItem(i)->text(0) == name) return tree->topLevelItem(i);
|
|
}
|
|
return static_cast<QTreeWidgetItem *>(nullptr);
|
|
};
|
|
auto invoke = [&window](const char *slot) {
|
|
require(QMetaObject::invokeMethod(&window, slot, Qt::DirectConnection), "Cannot invoke action");
|
|
};
|
|
auto edit = [&](QTreeWidgetItem *file, const QString &text) {
|
|
require(file && file->childCount() == 1, "Fixture was not parsed");
|
|
tree->setCurrentItem(file->child(0));
|
|
editor->setText(text);
|
|
invoke("on_setTranslationButton_clicked");
|
|
};
|
|
auto save = [&](bool success) {
|
|
QString message;
|
|
QTimer::singleShot(0, [&] {
|
|
auto *box = qobject_cast<QMessageBox *>(QApplication::activeModalWidget());
|
|
require(box, "Save result dialog is missing");
|
|
require((box->icon() != QMessageBox::Critical) == success, "Unexpected save result");
|
|
message = box->text();
|
|
box->accept();
|
|
});
|
|
invoke("on_saveButton_clicked");
|
|
return message;
|
|
};
|
|
|
|
auto *commonItem = find("common.rpy");
|
|
require(commonItem && commonItem->child(0)->text(0) == "42", "Common source line was not parsed");
|
|
edit(commonItem, "Jawohl");
|
|
save(true);
|
|
const QString saved = QString(common).replace(QStringLiteral("new \"Ja\""), QStringLiteral("new \"Jawohl\""));
|
|
require(readFile(directory + "/common.rpy") == saved, "Common translation was not saved");
|
|
const auto firstBackups = QDir(directory).entryList({"common.rpy.bak.*"}, QDir::Files);
|
|
require(firstBackups.size() == 1, "Backup is missing");
|
|
require(readFile(directory + '/' + firstBackups.first()).contains("new \"Ja\""), "Backup was modified");
|
|
edit(commonItem, "Ja, bitte");
|
|
save(true);
|
|
require(QDir(directory).entryList({"common.rpy.bak.*"}, QDir::Files).size() == 2, "Repeated save collided with a backup");
|
|
|
|
auto *missing = find("missing.rpy");
|
|
require(QFile::remove(directory + "/missing.rpy"), "Cannot simulate missing source");
|
|
edit(missing, "Wiederhergestellt");
|
|
save(true);
|
|
require(readFile(directory + "/missing.rpy").contains("new \"Wiederhergestellt\""), "Loaded source was not recovered");
|
|
|
|
for (const auto &fixture : {qMakePair(QStringLiteral("quoted.rpy"), quoted),
|
|
qMakePair(QStringLiteral("dialogue.rpy"), dialogue)}) {
|
|
edit(find(fixture.first), QStringLiteral("Ein anderes \\\"Zitat\\\" [name]"));
|
|
save(true);
|
|
QString expected = fixture.second;
|
|
expected.replace(QStringLiteral("Ein \\\"Zitat\\\" [name]"), QStringLiteral("Ein anderes \\\"Zitat\\\" [name]"));
|
|
require(readFile(directory + '/' + fixture.first) == expected, "Escaped quotes corrupted a translation");
|
|
const QList<QPair<QString, QString>> inputs = {
|
|
{QStringLiteral(R"(Er sagt "Hallo" [name].)"), QStringLiteral(R"(Er sagt \"Hallo\" [name].)")},
|
|
{QStringLiteral(R"(Er sagt \"Hallo\" [name].)"), QStringLiteral(R"(Er sagt \"Hallo\" [name].)")},
|
|
{QStringLiteral(R"(Pfad\)"), QStringLiteral(R"(Pfad\\)")},
|
|
{QStringLiteral("Er sagt \"Hallo\"\nWeiter"), QStringLiteral(R"(Er sagt \"Hallo\"\nWeiter)")},
|
|
{QStringLiteral(R"(Zwei Backslashes: \\"Zitat")"), QStringLiteral(R"(Zwei Backslashes: \\\"Zitat\")")},
|
|
};
|
|
for (const auto &input : inputs) {
|
|
edit(find(fixture.first), input.first);
|
|
save(true);
|
|
expected = fixture.second;
|
|
expected.replace(QStringLiteral("Ein \\\"Zitat\\\" [name]"), input.second);
|
|
require(readFile(directory + '/' + fixture.first) == expected, "Literal quotes or backslashes produced invalid source");
|
|
// Repeated saves must not add another level of escaping.
|
|
edit(find(fixture.first), input.second);
|
|
save(true);
|
|
require(readFile(directory + '/' + fixture.first) == expected, "Repeated escaping changed the translation");
|
|
}
|
|
}
|
|
|
|
auto *unsupported = find("unknown.rpy");
|
|
edit(unsupported, "Geändert");
|
|
require(save(false).contains("Keine unterstützten Übersetzungsblöcke"), "Missing parser failure details");
|
|
require(readFile(directory + "/unknown.rpy") == unknown, "Unsupported original was changed or removed");
|
|
require(unsupported->data(0, Qt::UserRole).toBool(), "Failed save discarded dirty state");
|
|
unsupported->setData(0, Qt::UserRole, false);
|
|
|
|
// An unavailable destination must report the backup path and filesystem error.
|
|
edit(commonItem, "Noch einmal");
|
|
require(QDir().rename(directory, directory + ".held"), "Cannot move fixture directory");
|
|
const QString backupError = save(false);
|
|
require(QDir().rename(directory + ".held", directory), "Cannot restore fixture directory");
|
|
require(backupError.contains("Backup konnte nicht erstellt werden") && backupError.contains("Grund:")
|
|
&& backupError.contains("common.rpy.bak."), "Missing backup error details");
|
|
require(readFile(directory + "/common.rpy").contains("Ja, bitte"), "Failed backup changed the original");
|
|
commonItem->setData(0, Qt::UserRole, false);
|
|
|
|
auto *absent = new QTreeWidgetItem(tree, {"absent.rpy"});
|
|
absent->setData(0, Qt::UserRole + 1, directory + "/absent.rpy");
|
|
absent->setData(0, Qt::UserRole, true);
|
|
const QString missingError = save(false);
|
|
require(missingError.contains("Originaldatei fehlt") && missingError.contains("keine geladene Kopie"), "Missing source error is unclear");
|
|
qInfo("PASS: common sources, backups, repeated saves, cached recovery, original preservation, error details");
|
|
}
|