File indexing completed on 2024-05-12 16:06:03
0001 /* 0002 SPDX-FileCopyrightText: 2014 Albert Astals Cid <aacid@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 // clazy:excludeall=qstring-allocations 0008 0009 #include <QTest> 0010 #include <config-okular.h> 0011 0012 #include <KConfigGroup> 0013 #include <KLineEdit> 0014 #include <KRecentFilesAction> 0015 0016 #if HAVE_DBUS 0017 #include <QDBusConnection> 0018 #include <QDBusConnectionInterface> 0019 #endif // HAVE_DBUS 0020 #include <QPrintDialog> 0021 #include <QStandardPaths> 0022 #include <QTabBar> 0023 #include <QTabWidget> 0024 #include <QTemporaryFile> 0025 #include <QTimer> 0026 #include <qwidget.h> 0027 0028 #include "../core/document_p.h" 0029 #include "../part/findbar.h" 0030 #include "../part/part.h" 0031 #include "../part/presentationwidget.h" 0032 #include "../settings.h" 0033 #include "../shell/okular_main.h" 0034 #include "../shell/shell.h" 0035 #include "../shell/shellutils.h" 0036 #include "closedialoghelper.h" 0037 0038 #include <sys/types.h> 0039 #ifndef Q_OS_WIN 0040 #include <unistd.h> 0041 #else 0042 #include <process.h> 0043 #endif 0044 0045 namespace Okular 0046 { 0047 class PartTest 0048 { 0049 public: 0050 Okular::Document *partDocument(Okular::Part *part) const 0051 { 0052 return part->m_document; 0053 } 0054 QWidget *presentationWidget(Okular::Part *part) const 0055 { 0056 return part->m_presentationWidget; 0057 } 0058 FindBar *findWidget(Okular::Part *part) const 0059 { 0060 return part->m_findBar; 0061 } 0062 }; 0063 } 0064 0065 class ClosePrintDialogHelper : public QObject 0066 { 0067 Q_OBJECT 0068 0069 public: 0070 explicit ClosePrintDialogHelper(int expectedTab) 0071 : foundDialog(false) 0072 , m_expectedTab(expectedTab) 0073 { 0074 } 0075 bool foundDialog; 0076 0077 public Q_SLOTS: 0078 void closePrintDialog(); 0079 0080 private: 0081 int m_expectedTab; 0082 }; 0083 0084 class MainShellTest : public QObject, public Okular::PartTest 0085 { 0086 Q_OBJECT 0087 0088 public: 0089 static QTabWidget *tabWidget(Shell *s) 0090 { 0091 return s->m_tabWidget; 0092 } 0093 0094 private Q_SLOTS: 0095 void initTestCase(); 0096 void cleanupTestCase(); 0097 void init(); 0098 void cleanup(); 0099 0100 void testShell_data(); 0101 void testShell(); 0102 void testFileRemembersPagePosition_data(); 0103 void testFileRemembersPagePosition(); 0104 void test2FilesError_data(); 0105 void test2FilesError(); 0106 void testMiddleButtonCloseUndo(); 0107 void testSessionRestore_data(); 0108 void testSessionRestore(); 0109 void testOpenInvalidFiles_data(); 0110 void testOpenInvalidFiles(); 0111 void testOpenTheSameFileSeveralTimes(); 0112 0113 private: 0114 }; 0115 0116 QList<Shell *> getShells() 0117 { 0118 QList<Shell *> shells; 0119 const QList<KMainWindow *> mainWindows = KMainWindow::memberList(); 0120 for (KMainWindow *kmw : mainWindows) { 0121 Shell *shell = qobject_cast<Shell *>(kmw); 0122 if (shell) { 0123 shells.append(shell); 0124 } 0125 } 0126 return shells; 0127 } 0128 0129 Shell *findShell(Shell *ignore = nullptr) 0130 { 0131 const QWidgetList wList = QApplication::topLevelWidgets(); 0132 for (QWidget *widget : wList) { 0133 Shell *s = qobject_cast<Shell *>(widget); 0134 if (s && s != ignore) { 0135 return s; 0136 } 0137 } 0138 return nullptr; 0139 } 0140 0141 void MainShellTest::initTestCase() 0142 { 0143 QStandardPaths::setTestModeEnabled(true); 0144 // Don't pollute people's okular settings 0145 Okular::Settings::instance(QStringLiteral("mainshelltest")); 0146 0147 // Register in bus as okular 0148 #if HAVE_DBUS 0149 QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); 0150 const QString myPid = QString::number(getpid()); 0151 const QString serviceName = QStringLiteral("org.kde.okular-") + myPid; 0152 QVERIFY(bus->registerService(serviceName) == QDBusConnectionInterface::ServiceRegistered); 0153 #endif 0154 // Tell the presentationWidget and queryClose to not be annoying 0155 KSharedConfigPtr c = KSharedConfig::openConfig(QStringLiteral("mainshelltest.kmessagebox")); 0156 KConfigGroup cg = c->group(QStringLiteral("General")); 0157 cg.writeEntry("presentationInfo", 4); 0158 cg.writeEntry("ShowTabWarning", 4); 0159 } 0160 0161 void MainShellTest::cleanupTestCase() 0162 { 0163 } 0164 0165 void MainShellTest::init() 0166 { 0167 // Default settings for every test 0168 Okular::Settings::self()->setDefaults(); 0169 0170 // Clean docdatas 0171 const QList<QUrl> urls = {QUrl::fromUserInput(QStringLiteral("file://" KDESRCDIR "data/file1.pdf")), 0172 QUrl::fromUserInput(QStringLiteral("file://" KDESRCDIR "data/tocreload.pdf")), 0173 QUrl::fromUserInput(QStringLiteral("file://" KDESRCDIR "data/contents.epub"))}; 0174 0175 for (const QUrl &url : urls) { 0176 QFileInfo fileReadTest(url.toLocalFile()); 0177 const QString docDataPath = Okular::DocumentPrivate::docDataFileName(url, fileReadTest.size()); 0178 QFile::remove(docDataPath); 0179 } 0180 } 0181 0182 void MainShellTest::cleanup() 0183 { 0184 Shell *s; 0185 while ((s = findShell())) { 0186 delete s; 0187 } 0188 } 0189 0190 void MainShellTest::testShell_data() 0191 { 0192 QTest::addColumn<QStringList>("paths"); 0193 QTest::addColumn<QString>("serializedOptions"); 0194 QTest::addColumn<bool>("useTabs"); 0195 QTest::addColumn<QString>("externalProcessPath"); 0196 QTest::addColumn<uint>("expectedPage"); 0197 QTest::addColumn<bool>("expectPresentation"); 0198 QTest::addColumn<bool>("expectPrintDialog"); 0199 QTest::addColumn<bool>("unique"); 0200 QTest::addColumn<uint>("externalProcessExpectedPage"); 0201 QTest::addColumn<bool>("externalProcessExpectPresentation"); 0202 QTest::addColumn<bool>("externalProcessExpectPrintDialog"); 0203 QTest::addColumn<QString>("externalProcessExpectFind"); 0204 0205 const QStringList contentsEpub = QStringList(QStringLiteral(KDESRCDIR "data/contents.epub")); 0206 const QStringList file1 = QStringList(QStringLiteral(KDESRCDIR "data/file1.pdf")); 0207 QStringList file1AndToc; 0208 file1AndToc << QStringLiteral(KDESRCDIR "data/file1.pdf"); 0209 file1AndToc << QStringLiteral(KDESRCDIR "data/tocreload.pdf"); 0210 const QString tocReload = QStringLiteral(KDESRCDIR "data/tocreload.pdf"); 0211 0212 const QString optionsPage2 = ShellUtils::serializeOptions(false, false, false, false, false, QStringLiteral("2"), QString(), QString()); 0213 const QString optionsPage2Presentation = ShellUtils::serializeOptions(true, false, false, false, false, QStringLiteral("2"), QString(), QString()); 0214 const QString optionsPrint = ShellUtils::serializeOptions(false, true, false, false, false, QString(), QString(), QString()); 0215 const QString optionsUnique = ShellUtils::serializeOptions(false, false, false, true, false, QString(), QString(), QString()); 0216 const QString optionsFind = ShellUtils::serializeOptions(false, false, false, false, false, QString(), QStringLiteral("si:next-testing parameters!"), QString()); 0217 0218 QTest::newRow("just show shell") << QStringList() << QString() << false << QString() << 0u << false << false << false << 0u << false << false << QString(); 0219 QTest::newRow("open file") << file1 << QString() << false << QString() << 0u << false << false << false << 0u << false << false << QString(); 0220 QTest::newRow("two files no tabs") << file1AndToc << QString() << false << QString() << 0u << false << false << false << 0u << false << false << QString(); 0221 QTest::newRow("two files with tabs") << file1AndToc << QString() << true << QString() << 0u << false << false << false << 0u << false << false << QString(); 0222 QTest::newRow("two files sequence no tabs") << file1 << QString() << false << tocReload << 0u << false << false << false << 0u << false << false << QString(); 0223 #if HAVE_DBUS 0224 QTest::newRow("two files sequence with tabs") << file1 << QString() << true << tocReload << 0u << false << false << false << 0u << false << false << QString(); 0225 #endif // HAVE_DBUS 0226 QTest::newRow("open file page number") << contentsEpub << optionsPage2 << false << QString() << 1u << false << false << false << 0u << false << false << QString(); 0227 QTest::newRow("open file page number and presentation") << contentsEpub << optionsPage2Presentation << false << QString() << 1u << true << false << false << 0u << false << false << QString(); 0228 QTest::newRow("open file find") << file1 << optionsFind << false << QString() << 0u << false << false << false << 0u << false << false << QStringLiteral("si:next-testing parameters!"); 0229 QTest::newRow("open file print") << file1 << optionsPrint << false << QString() << 0u << false << true << false << 0u << false << false << QString(); 0230 #if HAVE_DBUS 0231 QTest::newRow("open two files unique") << file1 << optionsUnique << false << tocReload << 0u << false << false << true << 0u << false << false << QString(); 0232 QTest::newRow("open two files unique tabs") << file1 << optionsUnique << true << tocReload << 0u << false << false << true << 0u << false << false << QString(); 0233 QTest::newRow("page number attach tabs") << file1 << QString() << true << contentsEpub[0] << 0u << false << false << false << 2u << false << false << QString(); 0234 QTest::newRow("presentation attach tabs") << file1 << QString() << true << contentsEpub[0] << 0u << false << false << false << 2u << true << false << QString(); 0235 QTest::newRow("print attach tabs") << file1 << QString() << true << contentsEpub[0] << 0u << false << true << false << 2u << false << true << QString(); 0236 QTest::newRow("page number attach unique") << file1 << optionsUnique << false << contentsEpub[0] << 0u << false << false << true << 3u << false << false << QString(); 0237 QTest::newRow("presentation attach unique") << file1 << optionsUnique << false << contentsEpub[0] << 0u << false << false << true << 2u << true << false << QString(); 0238 QTest::newRow("print attach unique") << file1 << optionsUnique << false << contentsEpub[0] << 0u << false << false << true << 2u << false << true << QString(); 0239 QTest::newRow("page number attach unique tabs") << file1 << optionsUnique << true << contentsEpub[0] << 0u << false << false << true << 3u << false << false << QString(); 0240 QTest::newRow("presentation attach unique tabs") << file1 << optionsUnique << true << contentsEpub[0] << 0u << false << false << true << 2u << true << false << QString(); 0241 QTest::newRow("print attach unique tabs") << file1 << optionsUnique << true << contentsEpub[0] << 0u << false << false << true << 2u << false << true << QString(); 0242 #endif // HAVE_DBUS 0243 } 0244 0245 void MainShellTest::testShell() 0246 { 0247 QFETCH(QStringList, paths); 0248 QFETCH(QString, serializedOptions); 0249 QFETCH(bool, useTabs); 0250 QFETCH(QString, externalProcessPath); 0251 QFETCH(uint, expectedPage); 0252 QFETCH(bool, expectPresentation); 0253 QFETCH(bool, expectPrintDialog); 0254 QFETCH(bool, unique); 0255 QFETCH(uint, externalProcessExpectedPage); 0256 QFETCH(bool, externalProcessExpectPresentation); 0257 QFETCH(bool, externalProcessExpectPrintDialog); 0258 QFETCH(QString, externalProcessExpectFind); 0259 0260 QScopedPointer<ClosePrintDialogHelper> helper; 0261 0262 Okular::Settings::self()->setShellOpenFileInTabs(useTabs); 0263 0264 if (expectPrintDialog || externalProcessExpectPrintDialog) { 0265 const int expectedTab = externalProcessExpectPrintDialog && !unique ? 1 : 0; 0266 helper.reset(new ClosePrintDialogHelper(expectedTab)); 0267 QTimer::singleShot(0, helper.data(), &ClosePrintDialogHelper::closePrintDialog); 0268 } 0269 0270 Okular::Status status = Okular::main(paths, serializedOptions); 0271 QCOMPARE(status, Okular::Success); 0272 Shell *s = findShell(); 0273 QVERIFY(s); 0274 0275 if (paths.count() == 1) { 0276 QCOMPARE(s->m_tabs.count(), 1); 0277 Okular::Part *part = s->findChild<Okular::Part *>(); 0278 QVERIFY(part); 0279 QCOMPARE(part->url().url(), QStringLiteral("file://%1").arg(paths[0])); 0280 QCOMPARE(partDocument(part)->currentPage(), expectedPage); 0281 // Testing if the bar is shown or hidden as expected 0282 QCOMPARE(findWidget(part)->isHidden(), externalProcessExpectFind.isEmpty()); 0283 QCOMPARE(findWidget(part)->findChild<KLineEdit *>()->text(), externalProcessExpectFind); 0284 // Checking if the encryption/decryption worked 0285 QCOMPARE(externalProcessExpectFind, ShellUtils::find(serializedOptions)); 0286 0287 } else if (paths.count() == 2) { 0288 if (useTabs) { 0289 Shell *s = findShell(); 0290 QVERIFY(s); 0291 Okular::Part *part = dynamic_cast<Okular::Part *>(s->m_tabs[0].part); 0292 Okular::Part *part2 = dynamic_cast<Okular::Part *>(s->m_tabs[1].part); 0293 QCOMPARE(s->m_tabs.count(), 2); 0294 QCOMPARE(part->url().url(), QStringLiteral("file://%1").arg(paths[0])); 0295 QCOMPARE(part2->url().url(), QStringLiteral("file://%1").arg(paths[1])); 0296 QCOMPARE(partDocument(part)->currentPage(), expectedPage); 0297 QCOMPARE(partDocument(part2)->currentPage(), expectedPage); 0298 } else { 0299 QSet<QString> openUrls; 0300 Shell *s = findShell(); 0301 QVERIFY(s); 0302 QCOMPARE(s->m_tabs.count(), 1); 0303 Okular::Part *part = s->findChild<Okular::Part *>(); 0304 QVERIFY(part); 0305 QCOMPARE(partDocument(part)->currentPage(), expectedPage); 0306 openUrls << part->url().url(); 0307 0308 Shell *s2 = findShell(s); 0309 QVERIFY(s2); 0310 QCOMPARE(s2->m_tabs.count(), 1); 0311 Okular::Part *part2 = s2->findChild<Okular::Part *>(); 0312 QVERIFY(part2); 0313 QCOMPARE(partDocument(part2)->currentPage(), expectedPage); 0314 openUrls << part2->url().url(); 0315 0316 for (const QString &path : std::as_const(paths)) { 0317 QVERIFY(openUrls.contains(QStringLiteral("file://%1").arg(path))); 0318 } 0319 } 0320 } 0321 0322 if (!externalProcessPath.isEmpty()) { 0323 Okular::Part *part = s->findChild<Okular::Part *>(); 0324 0325 QProcess p; 0326 QStringList args; 0327 args << externalProcessPath; 0328 if (unique) { 0329 args << QStringLiteral("-unique"); 0330 } 0331 if (externalProcessExpectedPage != 0) { 0332 args << QStringLiteral("-page") << QString::number(externalProcessExpectedPage + 1); 0333 } 0334 if (externalProcessExpectPresentation) { 0335 args << QStringLiteral("-presentation"); 0336 } 0337 if (externalProcessExpectPrintDialog) { 0338 args << QStringLiteral("-print"); 0339 } 0340 p.start(QStringLiteral(OKULAR_BINARY), args); 0341 p.waitForStarted(); 0342 QCOMPARE(p.state(), QProcess::Running); 0343 0344 if (useTabs || unique) { 0345 // It is attaching to us, so will eventually stop 0346 QTRY_COMPARE_WITH_TIMEOUT(p.state(), QProcess::NotRunning, 20000); 0347 QCOMPARE(p.exitStatus(), QProcess::NormalExit); 0348 QCOMPARE(p.exitCode(), 0); 0349 0350 if (unique) { 0351 // It is unique so part got "overwritten" 0352 QCOMPARE(s->m_tabs.count(), 1); 0353 QCOMPARE(part->url().url(), QStringLiteral("file://%1").arg(externalProcessPath)); 0354 QCOMPARE(partDocument(part)->currentPage(), externalProcessExpectedPage); 0355 } else { 0356 // It is attaching to us so a second tab is there 0357 QCOMPARE(s->m_tabs.count(), 2); 0358 Okular::Part *part2 = dynamic_cast<Okular::Part *>(s->m_tabs[1].part); 0359 QCOMPARE(part2->url().url(), QStringLiteral("file://%1").arg(externalProcessPath)); 0360 QCOMPARE(partDocument(part2)->currentPage(), externalProcessExpectedPage); 0361 } 0362 } else { 0363 QTest::qWait(750); 0364 0365 // It opened on a new process, so it is still running, we need to kill it 0366 QCOMPARE(p.state(), QProcess::Running); 0367 p.terminate(); 0368 p.waitForFinished(); 0369 QVERIFY(p.state() != QProcess::Running); 0370 // It opened on a new process, so no change for us 0371 QCOMPARE(s->m_tabs.count(), 1); 0372 QCOMPARE(part->url().url(), QStringLiteral("file://%1").arg(paths[0])); 0373 QCOMPARE(partDocument(part)->currentPage(), externalProcessExpectedPage); 0374 } 0375 } 0376 0377 if (expectPresentation) { 0378 QCOMPARE(paths.count(), 1); 0379 Okular::Part *part = s->findChild<Okular::Part *>(); 0380 QTRY_VERIFY(presentationWidget(part) != nullptr); 0381 } 0382 0383 if (externalProcessExpectPresentation) { 0384 Okular::Part *part; 0385 if (unique) { 0386 QCOMPARE(s->m_tabs.count(), 1); 0387 part = dynamic_cast<Okular::Part *>(s->m_tabs[0].part); 0388 } else { 0389 QCOMPARE(s->m_tabs.count(), 2); 0390 part = dynamic_cast<Okular::Part *>(s->m_tabs[1].part); 0391 } 0392 0393 QTRY_VERIFY(presentationWidget(part) != nullptr); 0394 } 0395 0396 if (helper) { 0397 QVERIFY(helper->foundDialog); 0398 } 0399 } 0400 0401 void ClosePrintDialogHelper::closePrintDialog() 0402 { 0403 Shell *s = findShell(); 0404 QPrintDialog *dialog = s->findChild<QPrintDialog *>(); 0405 if (!dialog) { 0406 QTimer::singleShot(0, this, &ClosePrintDialogHelper::closePrintDialog); 0407 return; 0408 } 0409 QVERIFY(dialog); 0410 QCOMPARE(MainShellTest::tabWidget(s)->currentIndex(), m_expectedTab); 0411 dialog->close(); 0412 foundDialog = true; 0413 } 0414 0415 void MainShellTest::testFileRemembersPagePosition_data() 0416 { 0417 QTest::addColumn<int>("mode"); 0418 0419 QTest::newRow("normal") << 1; 0420 QTest::newRow("unique") << 2; 0421 QTest::newRow("tabs") << 3; 0422 } 0423 0424 void MainShellTest::testFileRemembersPagePosition() 0425 { 0426 QFETCH(int, mode); 0427 0428 const QStringList paths = QStringList(QStringLiteral(KDESRCDIR "data/contents.epub")); 0429 QString serializedOptions; 0430 if (mode == 1 || mode == 3) { 0431 serializedOptions = ShellUtils::serializeOptions(false, false, false, false, false, QString(), QString(), QString()); 0432 } else { 0433 serializedOptions = ShellUtils::serializeOptions(false, false, false, true, false, QString(), QString(), QString()); 0434 } 0435 0436 Okular::Settings::self()->setShellOpenFileInTabs(mode == 3); 0437 0438 Okular::Status status = Okular::main(paths, serializedOptions); 0439 QCOMPARE(status, Okular::Success); 0440 Shell *s = findShell(); 0441 QVERIFY(s); 0442 Okular::Part *part = s->findChild<Okular::Part *>(); 0443 QVERIFY(part); 0444 QCOMPARE(part->url().url(), QStringLiteral("file://%1").arg(paths[0])); 0445 QCOMPARE(partDocument(part)->currentPage(), 0u); 0446 partDocument(part)->setViewportPage(3); 0447 QCOMPARE(partDocument(part)->currentPage(), 3u); 0448 s->closeUrl(); 0449 QCOMPARE(part->url().url(), QString()); 0450 0451 if (mode == 1) { 0452 delete s; 0453 status = Okular::main(paths, serializedOptions); 0454 QCOMPARE(status, Okular::Success); 0455 } else { 0456 QProcess p; 0457 QStringList args; 0458 args << paths[0]; 0459 if (mode == 2) { 0460 args << QStringLiteral("-unique"); 0461 } 0462 p.start(QStringLiteral(OKULAR_BINARY), args); 0463 p.waitForStarted(); 0464 QCOMPARE(p.state(), QProcess::Running); 0465 0466 // It is attaching to us, so will eventually stop 0467 QTRY_COMPARE_WITH_TIMEOUT((int)p.state(), (int)QProcess::NotRunning, 20000); 0468 QCOMPARE((int)p.exitStatus(), (int)QProcess::NormalExit); 0469 QCOMPARE(p.exitCode(), 0); 0470 } 0471 s = findShell(); 0472 QVERIFY(s); 0473 part = s->findChild<Okular::Part *>(); 0474 QVERIFY(part); 0475 QCOMPARE(part->url().url(), QStringLiteral("file://%1").arg(paths[0])); 0476 QCOMPARE(partDocument(part)->currentPage(), 3u); 0477 } 0478 0479 void MainShellTest::test2FilesError_data() 0480 { 0481 QTest::addColumn<QString>("serializedOptions"); 0482 0483 QTest::newRow("startInPresentation") << ShellUtils::serializeOptions(true, false, false, false, false, QString(), QString(), QString()); 0484 QTest::newRow("showPrintDialog") << ShellUtils::serializeOptions(false, true, false, false, false, QString(), QString(), QString()); 0485 #if HAVE_DBUS 0486 QTest::newRow("unique") << ShellUtils::serializeOptions(false, false, false, true, false, QString(), QString(), QString()); 0487 QTest::newRow("pageNumber") << ShellUtils::serializeOptions(false, false, false, false, false, QStringLiteral("3"), QString(), QString()); 0488 #endif // HAVE_DBUS 0489 QTest::newRow("find") << ShellUtils::serializeOptions(false, false, false, false, false, QString(), QStringLiteral("silly"), QString()); 0490 } 0491 0492 void MainShellTest::test2FilesError() 0493 { 0494 QFETCH(QString, serializedOptions); 0495 0496 QStringList paths; 0497 paths << QStringLiteral(KDESRCDIR "data/file1.pdf") << QStringLiteral(KDESRCDIR "data/tocreload.pdf"); 0498 Okular::Status status = Okular::main(paths, serializedOptions); 0499 QCOMPARE(status, Okular::Error); 0500 0501 Shell *s = findShell(); 0502 QVERIFY(!s); 0503 } 0504 0505 void MainShellTest::testSessionRestore_data() 0506 { 0507 QTest::addColumn<QStringList>("paths"); 0508 QTest::addColumn<QString>("options"); 0509 QTest::addColumn<bool>("useTabsOpen"); 0510 QTest::addColumn<bool>("useTabsRestore"); 0511 0512 QStringList oneDocPaths(QStringLiteral(KDESRCDIR "data/file1.pdf")); 0513 QStringList twoDocPaths(oneDocPaths); 0514 twoDocPaths << QStringLiteral(KDESRCDIR "data/formSamples.pdf"); 0515 0516 const QString options = ShellUtils::serializeOptions(false, false, false, false, false, QString(), QString(), QString()); 0517 0518 QTest::newRow("1 doc, 1 window, tabs") << oneDocPaths << options << true << true; 0519 QTest::newRow("2 docs, 1 window, tabs") << twoDocPaths << options << true << true; 0520 QTest::newRow("2 docs, 2 windows, tabs") << twoDocPaths << options << false << true; 0521 QTest::newRow("2 docs, 2 windows, no tabs") << twoDocPaths << options << false << false; 0522 QTest::newRow("2 docs, 1 window, no tabs") << twoDocPaths << options << true << false; 0523 } 0524 0525 void MainShellTest::testSessionRestore() 0526 { 0527 QFETCH(QStringList, paths); 0528 QFETCH(QString, options); 0529 QFETCH(bool, useTabsOpen); 0530 QFETCH(bool, useTabsRestore); 0531 0532 Okular::Settings::self()->setShellOpenFileInTabs(useTabsOpen); 0533 0534 Okular::Status status = Okular::main(paths, options); 0535 QCOMPARE(status, Okular::Success); 0536 0537 // Gather some information about the state 0538 // Verify that the correct number of windows/tabs were opened 0539 QList<Shell *> shells = getShells(); 0540 QVERIFY(!shells.isEmpty()); 0541 int numDocs = 0; 0542 for (Shell *shell : std::as_const(shells)) { 0543 QVERIFY(QTest::qWaitForWindowExposed(shell)); 0544 numDocs += shell->m_tabs.size(); 0545 } 0546 0547 QCOMPARE(numDocs, paths.size()); 0548 QCOMPARE(shells.size(), useTabsOpen ? 1 : paths.size()); 0549 QTest::qWait(100); 0550 0551 // Simulate session shutdown. The actual shutdown path comes through 0552 // QSessionManager XSMP handlers, then KApplication::commitData/saveState, 0553 // then KMWSessionManager::commitData/saveState. Without simulating an X 0554 // session manager, the best we can do here is to make a temporary Config 0555 // and call KMainWindows save functions directly. 0556 QTemporaryFile configFile; 0557 QVERIFY(configFile.open()); 0558 0559 int numWindows = 0; 0560 { // Scope for config so that we can reconstruct from file 0561 KConfig config(configFile.fileName(), KConfig::SimpleConfig); 0562 for (Shell *shell : std::as_const(shells)) { 0563 shell->savePropertiesInternal(&config, ++numWindows); 0564 // Windows aren't necessarily closed on shutdown, but we'll use 0565 // this as a way to trigger the destructor code, which is normally 0566 // connected to the aboutToQuit signal 0567 shell->close(); 0568 } 0569 } 0570 0571 // Wait for shells to delete themselves. QTest::qWait doesn't do deferred 0572 // deletions so we'll set up a full event loop to do that. 0573 QEventLoop eventLoop; 0574 QTimer::singleShot(100, &eventLoop, &QEventLoop::quit); 0575 eventLoop.exec(QEventLoop::AllEvents); 0576 // Sometimes the event loop is not enough, so try a bit more to get deferred delete happen 0577 QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); 0578 shells = getShells(); 0579 QCOMPARE(shells.size(), 0); 0580 0581 Okular::Settings::self()->setShellOpenFileInTabs(useTabsRestore); 0582 0583 // Simulate session restore. We can't call KMainWindow::restore() directly 0584 // because it asks for info from the session manager, which doesn't know 0585 // about our temporary config. But the logic here mostly mirrors restore(). 0586 KConfig config(configFile.fileName(), KConfig::SimpleConfig); 0587 for (int i = 1; i <= numWindows; ++i) { 0588 Shell *shell = new Shell; 0589 shell->readPropertiesInternal(&config, i); 0590 shell->show(); 0591 } 0592 0593 // Verify that the restore state is reasonable 0594 shells = getShells(); 0595 QVERIFY(!shells.isEmpty()); 0596 numDocs = 0; 0597 for (Shell *shell : std::as_const(shells)) { 0598 QVERIFY(QTest::qWaitForWindowExposed(shell)); 0599 numDocs += shell->m_tabs.size(); 0600 } 0601 0602 QCOMPARE(numDocs, paths.size()); 0603 QCOMPARE(shells.size(), useTabsRestore ? numWindows : paths.size()); 0604 } 0605 0606 void MainShellTest::testOpenInvalidFiles_data() 0607 { 0608 QTest::addColumn<QList<QUrl>>("files"); 0609 QTest::addColumn<QString>("options"); 0610 0611 QString options = ShellUtils::serializeOptions(false, false, false, false, false, QString(), QString(), QString()); 0612 QUrl validFile1 = ShellUtils::urlFromArg(QStringLiteral(KDESRCDIR "data/file1.pdf"), ShellUtils::qfileExistFunc(), QString()); 0613 QUrl validFile2 = ShellUtils::urlFromArg(QStringLiteral(KDESRCDIR "data/file2.pdf"), ShellUtils::qfileExistFunc(), QString()); 0614 QUrl invalidFile = ShellUtils::urlFromArg(QStringLiteral(KDESRCDIR "data/non-existing-doc.pdf"), ShellUtils::qfileExistFunc(), QString()); 0615 0616 QList<QUrl> firstCase {invalidFile, validFile1, validFile2}; 0617 QList<QUrl> secondCase {validFile1, validFile2, invalidFile}; 0618 0619 QTest::newRow("opening the invalid file first") << firstCase << options; 0620 QTest::newRow("opening the valids file first") << secondCase << options; 0621 } 0622 0623 void MainShellTest::testOpenInvalidFiles() 0624 { 0625 QFETCH(QList<QUrl>, files); 0626 QFETCH(QString, options); 0627 0628 /* 0629 * The purpose of this test is to verify that when we open an invalid file, no tab is created in the 0630 * shell. 0631 * 0632 */ 0633 0634 Okular::Settings::self()->setShellOpenFileInTabs(true); 0635 Okular::Status status = Okular::main(QStringList(), options); 0636 QCOMPARE(status, Okular::Success); 0637 0638 Shell *shell = findShell(); 0639 QVERIFY(shell); 0640 0641 /* 0642 * We need to make sure that the KrecentFilesAction is empty before starting, because we will also test that 0643 * the file gets removed from the recent documents 0644 * 0645 */ 0646 shell->m_recent->clear(); 0647 0648 QScopedPointer<TestingUtils::CloseDialogHelper> closeDialogHelper {new TestingUtils::CloseDialogHelper(QDialogButtonBox::StandardButton::Ok)}; 0649 0650 for (const QUrl &file : files) { 0651 shell->openUrl(file); 0652 } 0653 0654 QList<QUrl> recentFiles = shell->m_recent->urls(); 0655 0656 QVERIFY(shell->m_tabs.size() == 2); 0657 QVERIFY(shell->m_tabWidget->tabBar()->isVisible()); 0658 0659 QVERIFY(!shell->m_tabWidget->tabIcon(0).isNull()); 0660 QVERIFY(!shell->m_tabWidget->tabIcon(1).isNull()); 0661 0662 QVERIFY(recentFiles.size() == 2); 0663 } 0664 0665 void MainShellTest::testOpenTheSameFileSeveralTimes() 0666 { 0667 QString options = ShellUtils::serializeOptions(false, false, false, false, false, QString(), QString(), QString()); 0668 0669 Okular::Settings::self()->setShellOpenFileInTabs(true); 0670 Okular::Status status = Okular::main(QStringList(), options); 0671 QCOMPARE(status, Okular::Success); 0672 0673 Shell *shell = findShell(); 0674 QVERIFY(shell); 0675 0676 QUrl file1 = ShellUtils::urlFromArg(QStringLiteral(KDESRCDIR "data/file1.pdf"), ShellUtils::qfileExistFunc(), QString()); 0677 QUrl file2 = ShellUtils::urlFromArg(QStringLiteral(KDESRCDIR "data/file2.pdf"), ShellUtils::qfileExistFunc(), QString()); 0678 QUrl file3 = ShellUtils::urlFromArg(QStringLiteral(KDESRCDIR "data/formattest.pdf"), ShellUtils::qfileExistFunc(), QString()); 0679 0680 shell->openUrl(file1); 0681 shell->openUrl(file2); 0682 shell->openUrl(file2); 0683 0684 QVERIFY(shell->m_tabs.size() == 2); 0685 0686 QVERIFY(shell->m_tabWidget->currentIndex() == 1); 0687 0688 Okular::Settings::self()->setSwitchToTabIfOpen(false); 0689 0690 shell->openUrl(file2); 0691 0692 QVERIFY(shell->m_tabs.size() == 3); 0693 0694 shell->openUrl(file3); 0695 QVERIFY(shell->m_tabWidget->currentIndex() == 3); 0696 0697 QVERIFY(shell->m_tabs.size() == 4); 0698 0699 Okular::Settings::self()->setSwitchToTabIfOpen(true); 0700 } 0701 0702 void MainShellTest::testMiddleButtonCloseUndo() 0703 { 0704 const QStringList paths = {QStringLiteral(KDESRCDIR "data/file1.pdf"), QStringLiteral(KDESRCDIR "data/file2.pdf")}; 0705 QString serializedOptions; 0706 serializedOptions = ShellUtils::serializeOptions(false, false, false, false, false, QString(), QString(), QString()); 0707 0708 Okular::Settings::self()->setShellOpenFileInTabs(true); 0709 Okular::Status status = Okular::main(paths, serializedOptions); 0710 QCOMPARE(status, Okular::Success); 0711 Shell *s = findShell(); 0712 QVERIFY(s); 0713 0714 QCOMPARE(s->m_tabWidget->count(), paths.size()); 0715 // Close a tab using middle key 0716 QWidget *firstTab = s->m_tabWidget->tabBar()->tabButton(0, QTabBar::RightSide); 0717 QVERIFY(firstTab); 0718 QTest::mouseClick(firstTab, Qt::MiddleButton); 0719 QCOMPARE(s->m_tabWidget->count(), paths.size() - 1); 0720 // Undo tab close 0721 s->undoCloseTab(); 0722 QCOMPARE(s->m_tabWidget->count(), paths.size()); 0723 } 0724 0725 QTEST_MAIN(MainShellTest) 0726 #include "mainshelltest.moc"