File indexing completed on 2025-02-02 04:44:10
0001 /* 0002 SPDX-FileCopyrightText: 2022 Jean-Baptiste Mardelle <jb@kdenlive.org> 0003 SPDX-FileCopyrightText: 2022 Eric Jiang 0004 SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 #include "catch.hpp" 0007 #include "test_utils.hpp" 0008 // test specific headers 0009 #include "core.h" 0010 #include "definitions.h" 0011 #include "doc/docundostack.hpp" 0012 #include "doc/kdenlivedoc.h" 0013 0014 using namespace fakeit; 0015 0016 TEST_CASE("Read subtitle file", "[Subtitles]") 0017 { 0018 // Create timeline 0019 auto binModel = pCore->projectItemModel(); 0020 binModel->clean(); 0021 std::shared_ptr<DocUndoStack> undoStack = std::make_shared<DocUndoStack>(nullptr); 0022 0023 // Here we do some trickery to enable testing. 0024 // We mock the project class so that the undoStack function returns our undoStack 0025 KdenliveDoc document(undoStack); 0026 Mock<KdenliveDoc> docMock(document); 0027 KdenliveDoc &mockedDoc = docMock.get(); 0028 0029 // We mock the project class so that the undoStack function returns our undoStack, and our mocked document 0030 Mock<ProjectManager> pmMock; 0031 When(Method(pmMock, undoStack)).AlwaysReturn(undoStack); 0032 When(Method(pmMock, cacheDir)).AlwaysReturn(QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation))); 0033 When(Method(pmMock, current)).AlwaysReturn(&mockedDoc); 0034 ProjectManager &mocked = pmMock.get(); 0035 pCore->m_projectManager = &mocked; 0036 0037 mocked.m_project = &mockedDoc; 0038 QDateTime documentDate = QDateTime::currentDateTime(); 0039 mocked.updateTimeline(false, QString(), QString(), documentDate, 0); 0040 auto timeline = mockedDoc.getTimeline(mockedDoc.uuid()); 0041 mocked.m_activeTimelineModel = timeline; 0042 mocked.testSetActiveDocument(&mockedDoc, timeline); 0043 QString documentId = QString::number(QDateTime::currentMSecsSinceEpoch()); 0044 mockedDoc.setDocumentProperty(QStringLiteral("documentid"), documentId); 0045 0046 // Initialize subtitle model 0047 std::shared_ptr<SubtitleModel> subtitleModel = timeline->createSubtitleModel(); 0048 0049 SECTION("Load a subtitle file") 0050 { 0051 QString subtitleFile = sourcesPath + "/dataset/01.srt"; 0052 bool ok; 0053 QByteArray guessedEncoding = SubtitleModel::guessFileEncoding(subtitleFile, &ok); 0054 CHECK(guessedEncoding == "UTF-8"); 0055 subtitleModel->importSubtitle(subtitleFile, 0, false, 30.00, 30.00, guessedEncoding); 0056 // Ensure the 3 dialogues are loaded 0057 REQUIRE(subtitleModel->rowCount() == 3); 0058 QList<SubtitledTime> allSubs = subtitleModel->getAllSubtitles(); 0059 QList<GenTime> sTime; 0060 QList<GenTime> controleTime; 0061 controleTime << GenTime(140, 25) << GenTime(265, 25) << GenTime(503, 25) << GenTime(628, 25) << GenTime(628, 25) << GenTime(875, 25); 0062 QStringList subtitlesText; 0063 QStringList control = {QStringLiteral("J'hésite à vérifier"), QStringLiteral("Ce test de sous-titres"), QStringLiteral("!! Quand même !!")}; 0064 for (const auto &s : qAsConst(allSubs)) { 0065 subtitlesText << s.subtitle(); 0066 sTime << s.start(); 0067 sTime << s.end(); 0068 } 0069 // Ensure the texts are read correctly 0070 REQUIRE(subtitlesText == control); 0071 // Ensure timeing is correct 0072 REQUIRE(sTime == controleTime); 0073 subtitleModel->removeAllSubtitles(); 0074 REQUIRE(subtitleModel->rowCount() == 0); 0075 } 0076 0077 // TODO: qt6 fix 0078 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0079 SECTION("Load a non-UTF-8 subtitle") 0080 { 0081 QString subtitleFile = sourcesPath + "/dataset/01-iso-8859-1.srt"; 0082 bool ok; 0083 QByteArray guessedEncoding = SubtitleModel::guessFileEncoding(subtitleFile, &ok); 0084 qDebug() << "Guessed encoding: " << guessedEncoding; 0085 subtitleModel->importSubtitle(subtitleFile, 0, false, 30.00, 30.00, guessedEncoding); 0086 // Ensure the 3 dialogues are loaded 0087 REQUIRE(subtitleModel->rowCount() == 3); 0088 QList<SubtitledTime> allSubs = subtitleModel->getAllSubtitles(); 0089 QStringList subtitlesText; 0090 QStringList control = {QStringLiteral("J'hésite à vérifier"), QStringLiteral("Ce test de sous-titres"), QStringLiteral("!! Quand même !!")}; 0091 for (const auto &s : qAsConst(allSubs)) { 0092 subtitlesText << s.subtitle(); 0093 } 0094 // Ensure that non-ASCII characters are read correctly 0095 CHECK(subtitlesText == control); 0096 subtitleModel->removeAllSubtitles(); 0097 REQUIRE(subtitleModel->rowCount() == 0); 0098 } 0099 #endif 0100 0101 SECTION("Load ASS file with commas") 0102 { 0103 QString subtitleFile = sourcesPath + "/dataset/subs-with-commas.ass"; 0104 bool ok; 0105 QByteArray guessedEncoding = SubtitleModel::guessFileEncoding(subtitleFile, &ok); 0106 qDebug() << "Guessed encoding: " << guessedEncoding; 0107 subtitleModel->importSubtitle(subtitleFile, 0, false, 30.00, 30.00, guessedEncoding); 0108 // Ensure all 2 lines are loaded 0109 REQUIRE(subtitleModel->rowCount() == 2); 0110 QList<SubtitledTime> allSubs = subtitleModel->getAllSubtitles(); 0111 QStringList subtitlesText; 0112 QStringList control = {QStringLiteral("Line with one comma, second part."), QStringLiteral("Line with two commas, second part, third part.")}; 0113 for (const auto &s : qAsConst(allSubs)) { 0114 subtitlesText << s.subtitle(); 0115 } 0116 // Ensure that non-ASCII characters are read correctly 0117 CHECK(subtitlesText == control); 0118 subtitleModel->removeAllSubtitles(); 0119 REQUIRE(subtitleModel->rowCount() == 0); 0120 } 0121 0122 SECTION("Load a broken subtitle file") 0123 { 0124 QString subtitleFile = sourcesPath + "/dataset/02.srt"; 0125 subtitleModel->importSubtitle(subtitleFile); 0126 // Ensure the 2 dialogues are loaded 0127 REQUIRE(subtitleModel->rowCount() == 2); 0128 QList<SubtitledTime> allSubs = subtitleModel->getAllSubtitles(); 0129 QList<GenTime> sTime; 0130 QList<GenTime> controleTime; 0131 controleTime << GenTime(140, 25) << GenTime(265, 25) << GenTime(628, 25) << GenTime(875, 25); 0132 QStringList subtitlesText; 0133 QStringList control = {QStringLiteral("J'hésite à vérifier"), QStringLiteral("!! Quand même !!")}; 0134 for (const auto &s : allSubs) { 0135 subtitlesText << s.subtitle(); 0136 sTime << s.start(); 0137 sTime << s.end(); 0138 } 0139 // Ensure the texts are read correctly 0140 REQUIRE(subtitlesText == control); 0141 // Ensure timeing is correct 0142 REQUIRE(sTime == controleTime); 0143 subtitleModel->removeAllSubtitles(); 0144 REQUIRE(subtitleModel->rowCount() == 0); 0145 } 0146 0147 SECTION("Preserve multiple spaces in subtitles") 0148 { 0149 QString subtitleFile = sourcesPath + "/dataset/multiple-spaces.srt"; 0150 subtitleModel->importSubtitle(subtitleFile); 0151 const QList<SubtitledTime> allSubs = subtitleModel->getAllSubtitles(); 0152 CHECK(allSubs.at(0).subtitle().toStdString() == "three spaces"); 0153 subtitleModel->removeAllSubtitles(); 0154 REQUIRE(subtitleModel->rowCount() == 0); 0155 } 0156 0157 SECTION("Load SBV subtitle file") 0158 { 0159 QString subtitleFile = sourcesPath + "/dataset/01.sbv"; 0160 subtitleModel->importSubtitle(subtitleFile); 0161 // Ensure the 3 dialogues are loaded 0162 REQUIRE(subtitleModel->rowCount() == 3); 0163 subtitleModel->removeAllSubtitles(); 0164 REQUIRE(subtitleModel->rowCount() == 0); 0165 } 0166 0167 SECTION("Load VTT subtitle file") 0168 { 0169 QString subtitleFile = sourcesPath + "/dataset/01.vtt"; 0170 subtitleModel->importSubtitle(subtitleFile); 0171 // Ensure the 2 dialogues are loaded 0172 REQUIRE(subtitleModel->rowCount() == 2); 0173 subtitleModel->removeAllSubtitles(); 0174 REQUIRE(subtitleModel->rowCount() == 0); 0175 } 0176 0177 SECTION("Load SRT subtitle file with two dots") 0178 { 0179 QString subtitleFile = sourcesPath + "/dataset/subs-with-two-dots.srt"; 0180 subtitleModel->importSubtitle(subtitleFile); 0181 // Ensure the 2 dialogues are loaded 0182 REQUIRE(subtitleModel->rowCount() == 2); 0183 subtitleModel->removeAllSubtitles(); 0184 REQUIRE(subtitleModel->rowCount() == 0); 0185 } 0186 0187 SECTION("Ensure 2 subtitles cannot be place at same frame position") 0188 { 0189 // In our current implementation, having 2 subtitles at same start time is not allowed 0190 int subId = TimelineModel::getNextId(); 0191 int subId2 = TimelineModel::getNextId(); 0192 int subId3 = TimelineModel::getNextId(); 0193 double fps = pCore->getCurrentFps(); 0194 REQUIRE(subtitleModel->addSubtitle(subId, GenTime(50, fps), GenTime(70, fps), QStringLiteral("Hello"), false, false)); 0195 REQUIRE(subtitleModel->addSubtitle(subId2, GenTime(50, fps), GenTime(90, fps), QStringLiteral("Hello2"), false, false) == false); 0196 REQUIRE(subtitleModel->addSubtitle(subId3, GenTime(100, fps), GenTime(140, fps), QStringLiteral("Second"), false, false)); 0197 REQUIRE(subtitleModel->rowCount() == 2); 0198 REQUIRE(subtitleModel->moveSubtitle(subId, GenTime(100, fps), false, false) == false); 0199 REQUIRE(subtitleModel->moveSubtitle(subId, GenTime(300, fps), false, false)); 0200 subtitleModel->removeAllSubtitles(); 0201 REQUIRE(subtitleModel->rowCount() == 0); 0202 } 0203 0204 SECTION("Ensure we cannot cut overlapping subtitles (it would create 2 subtitles at same frame position") 0205 { 0206 // In our current implementation, having 2 subtitles at same start time is not allowed 0207 int subId = TimelineModel::getNextId(); 0208 int subId2 = TimelineModel::getNextId(); 0209 double fps = pCore->getCurrentFps(); 0210 REQUIRE(subtitleModel->addSubtitle(subId, GenTime(50, fps), GenTime(70, fps), QStringLiteral("Hello"), false, false)); 0211 REQUIRE(subtitleModel->addSubtitle(subId2, GenTime(60, fps), GenTime(90, fps), QStringLiteral("Hello2"), false, false)); 0212 REQUIRE(subtitleModel->rowCount() == 2); 0213 REQUIRE(timeline->requestClipsGroup({subId, subId2})); 0214 REQUIRE_FALSE(TimelineFunctions::requestClipCut(timeline, subId, 65)); 0215 subtitleModel->removeAllSubtitles(); 0216 REQUIRE(subtitleModel->rowCount() == 0); 0217 } 0218 0219 binModel->clean(); 0220 pCore->m_projectManager = nullptr; 0221 }