210 lines
8.8 KiB
C++
210 lines
8.8 KiB
C++
#include "translationstore.h"
|
|
|
|
#include <QFile>
|
|
#include <QTemporaryDir>
|
|
#include <QTest>
|
|
|
|
using namespace Qt::Literals::StringLiterals;
|
|
|
|
namespace
|
|
{
|
|
void writeText(const QString &path, const QByteArray &data)
|
|
{
|
|
QFile file(path);
|
|
QVERIFY(file.open(QIODevice::WriteOnly));
|
|
file.write(data);
|
|
}
|
|
|
|
QByteArray readText(const QString &path)
|
|
{
|
|
QFile file(path);
|
|
return file.open(QIODevice::ReadOnly) ? file.readAll() : QByteArray();
|
|
}
|
|
}
|
|
|
|
class TranslationStoreTest : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private:
|
|
QTemporaryDir m_dir;
|
|
QString m_videos;
|
|
QString m_cache;
|
|
QString m_video;
|
|
QUrl m_videoUrl;
|
|
|
|
// The translation as TranslationService leaves it in the cache before publishing.
|
|
QString cached(const QString &key, const QByteArray &data)
|
|
{
|
|
const QString path = TranslationStore::cachePath(m_cache, m_videoUrl, key, u"vi"_s);
|
|
writeText(path, data);
|
|
return path;
|
|
}
|
|
|
|
private Q_SLOTS:
|
|
void init()
|
|
{
|
|
QVERIFY(m_dir.isValid());
|
|
QDir root(m_dir.path());
|
|
root.removeRecursively();
|
|
QVERIFY(root.mkpath(u"videos"_s));
|
|
QVERIFY(root.mkpath(u"cache"_s));
|
|
m_videos = m_dir.filePath(u"videos"_s);
|
|
m_cache = m_dir.filePath(u"cache"_s);
|
|
m_video = m_videos + u"/Show.S01E01.mkv"_s;
|
|
writeText(m_video, "video");
|
|
m_videoUrl = QUrl::fromLocalFile(m_video);
|
|
}
|
|
|
|
void namesFiles()
|
|
{
|
|
QCOMPARE(TranslationStore::languageSuffix(u"Vietnamese"_s), u"vi"_s);
|
|
QCOMPARE(TranslationStore::languageSuffix(u"vietnamese"_s), u"vi"_s);
|
|
QCOMPARE(TranslationStore::languageSuffix(u"Klingon!"_s), u"klingon"_s);
|
|
QCOMPARE(TranslationStore::sidecarPaths(u"/films/Show.S01E01.mkv"_s, u"vi"_s),
|
|
QStringList({u"/films/Show.S01E01.vi.srt"_s, u"/films/Show.S01E01.qtpotato.vi.srt"_s}));
|
|
QCOMPARE(TranslationStore::cachePath(u"/cache"_s, QUrl::fromLocalFile(u"/films/Show.S01E01.mkv"_s), u"abc"_s, u"vi"_s),
|
|
u"/cache/Show.S01E01.abc.vi.srt"_s);
|
|
QCOMPARE(TranslationStore::cachePath(u"/cache"_s, QUrl(u"https://example.com/live.m3u8"_s), u"abc"_s, u"vi"_s),
|
|
u"/cache/subtitles.abc.vi.srt"_s);
|
|
QCOMPARE(TranslationStore::videoKey(QUrl(u"https://example.com/live.m3u8"_s)), u"https://example.com/live.m3u8"_s);
|
|
}
|
|
|
|
void writesNextToVideo()
|
|
{
|
|
TranslationStore::Registry registry;
|
|
const QString cache = cached(u"k1"_s, "one");
|
|
const auto result = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cache);
|
|
|
|
const QString sidecar = m_videos + u"/Show.S01E01.vi.srt"_s;
|
|
QCOMPARE(result.path, sidecar);
|
|
QVERIFY(result.nextToVideo);
|
|
QCOMPARE(readText(sidecar), "one");
|
|
QVERIFY(TranslationStore::isOwned(registry, sidecar));
|
|
QCOMPARE(TranslationStore::savedTranslation(registry, m_videoUrl, u"vi"_s), sidecar);
|
|
QCOMPARE(TranslationStore::savedTranslation(registry, m_videoUrl, u"ja"_s), QString());
|
|
QCOMPARE(registry.find(TranslationStore::videoKey(m_videoUrl), u"vi"_s)->configKey, u"k1"_s);
|
|
}
|
|
|
|
// Different settings give a new translation, which replaces QtPotato's own file.
|
|
void replacesOwnFileWithNewSettings()
|
|
{
|
|
TranslationStore::Registry registry;
|
|
TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cached(u"k1"_s, "one"));
|
|
const auto result = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k2"_s, "two", cached(u"k2"_s, "two"));
|
|
|
|
QCOMPARE(result.path, m_videos + u"/Show.S01E01.vi.srt"_s);
|
|
QCOMPARE(readText(result.path), "two");
|
|
QCOMPARE(registry.records.size(), 1);
|
|
QCOMPARE(registry.records.constFirst().configKey, u"k2"_s);
|
|
QVERIFY(!QFile::exists(m_videos + u"/Show.S01E01.qtpotato.vi.srt"_s));
|
|
}
|
|
|
|
void keepsUserSubtitle()
|
|
{
|
|
const QString userFile = m_videos + u"/Show.S01E01.vi.srt"_s;
|
|
writeText(userFile, "mine");
|
|
TranslationStore::Registry registry;
|
|
QVERIFY(!TranslationStore::isOwned(registry, userFile));
|
|
|
|
const auto result = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cached(u"k1"_s, "one"));
|
|
QCOMPARE(result.path, m_videos + u"/Show.S01E01.qtpotato.vi.srt"_s);
|
|
QCOMPARE(readText(userFile), "mine");
|
|
QCOMPARE(readText(result.path), "one");
|
|
}
|
|
|
|
// A file QtPotato wrote and the user then edited counts as the user's.
|
|
void keepsEditedTranslation()
|
|
{
|
|
TranslationStore::Registry registry;
|
|
const auto first = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cached(u"k1"_s, "one"));
|
|
writeText(first.path, "one, fixed by hand");
|
|
QVERIFY(!TranslationStore::isOwned(registry, first.path));
|
|
|
|
const auto second = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k2"_s, "two", cached(u"k2"_s, "two"));
|
|
QCOMPARE(second.path, m_videos + u"/Show.S01E01.qtpotato.vi.srt"_s);
|
|
QCOMPARE(readText(first.path), "one, fixed by hand");
|
|
}
|
|
|
|
// Once on ".qtpotato.vi.srt", stay there instead of leaving a second copy behind.
|
|
void staysOnPreviousFile()
|
|
{
|
|
const QString userFile = m_videos + u"/Show.S01E01.vi.srt"_s;
|
|
writeText(userFile, "mine");
|
|
TranslationStore::Registry registry;
|
|
const auto first = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cached(u"k1"_s, "one"));
|
|
QFile::remove(userFile);
|
|
|
|
const auto second = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k2"_s, "two", cached(u"k2"_s, "two"));
|
|
QCOMPARE(second.path, first.path);
|
|
QCOMPARE(readText(second.path), "two");
|
|
QVERIFY(!QFile::exists(userFile));
|
|
}
|
|
|
|
void fallsBackToCacheWhenFolderIsReadOnly()
|
|
{
|
|
QFile::setPermissions(m_videos, QFileDevice::ReadOwner | QFileDevice::ExeOwner);
|
|
auto restore = qScopeGuard([this] {
|
|
QFile::setPermissions(m_videos, QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
|
|
});
|
|
if (QFileInfo(m_videos).isWritable()) {
|
|
QSKIP("Permissions are not enforced here (running as root?)");
|
|
}
|
|
|
|
TranslationStore::Registry registry;
|
|
const QString cache = cached(u"k1"_s, "one");
|
|
const auto result = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cache);
|
|
QCOMPARE(result.path, cache);
|
|
QVERIFY(!result.nextToVideo);
|
|
QCOMPARE(TranslationStore::savedTranslation(registry, m_videoUrl, u"vi"_s), cache);
|
|
}
|
|
|
|
void fallsBackToCacheForStreams()
|
|
{
|
|
const QUrl stream(u"https://example.com/live.m3u8"_s);
|
|
const QString cache = TranslationStore::cachePath(m_cache, stream, u"k1"_s, u"vi"_s);
|
|
writeText(cache, "one");
|
|
TranslationStore::Registry registry;
|
|
const auto result = TranslationStore::publish(registry, stream, u"vi"_s, u"k1"_s, "one", cache);
|
|
QCOMPARE(result.path, cache);
|
|
QCOMPARE(TranslationStore::savedTranslation(registry, stream, u"vi"_s), cache);
|
|
}
|
|
|
|
void glossaryRewriteKeepsOwnership()
|
|
{
|
|
TranslationStore::Registry registry;
|
|
const auto result = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cached(u"k1"_s, "one"));
|
|
writeText(result.path, "one, new glossary");
|
|
TranslationStore::recordRewrite(registry, result.path, "one, new glossary");
|
|
QVERIFY(TranslationStore::isOwned(registry, result.path));
|
|
}
|
|
|
|
void savesAndLoadsRegistry()
|
|
{
|
|
const QString file = m_dir.filePath(u"data/translations.json"_s);
|
|
TranslationStore::Registry registry;
|
|
const auto result = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cached(u"k1"_s, "one"));
|
|
// A record whose file is gone is not worth keeping.
|
|
registry.put({u"/gone.mkv"_s, u"vi"_s, u"/gone.vi.srt"_s, u"k0"_s, u"00"_s});
|
|
QVERIFY(TranslationStore::save(file, registry));
|
|
|
|
const TranslationStore::Registry loaded = TranslationStore::load(file);
|
|
QCOMPARE(loaded.records.size(), 1);
|
|
QVERIFY(TranslationStore::isOwned(loaded, result.path));
|
|
QCOMPARE(TranslationStore::savedTranslation(loaded, m_videoUrl, u"vi"_s), result.path);
|
|
QVERIFY(TranslationStore::load(m_dir.filePath(u"missing.json"_s)).records.isEmpty());
|
|
}
|
|
|
|
// The registry is no longer right about a file that disappeared.
|
|
void forgetsDeletedTranslation()
|
|
{
|
|
TranslationStore::Registry registry;
|
|
const auto result = TranslationStore::publish(registry, m_videoUrl, u"vi"_s, u"k1"_s, "one", cached(u"k1"_s, "one"));
|
|
QFile::remove(result.path);
|
|
QCOMPARE(TranslationStore::savedTranslation(registry, m_videoUrl, u"vi"_s), QString());
|
|
}
|
|
};
|
|
|
|
QTEST_GUILESS_MAIN(TranslationStoreTest)
|
|
#include "translationstoretest.moc"
|