summaryrefslogtreecommitdiffstats
path: root/tests/test_mainwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test_mainwindow.cpp')
-rw-r--r--tests/test_mainwindow.cpp119
1 files changed, 119 insertions, 0 deletions
diff --git a/tests/test_mainwindow.cpp b/tests/test_mainwindow.cpp
index a2142b9..93bb853 100644
--- a/tests/test_mainwindow.cpp
+++ b/tests/test_mainwindow.cpp
@@ -26,6 +26,7 @@
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
+#include <QProgressBar>
#include <QFile>
#include <QSettings>
#include <QStandardPaths>
@@ -74,6 +75,9 @@ private slots:
void theStatusBarReportsAMultiRowSelection();
void theThreadListOffersAContextMenu();
void aSecondRowBlanksThePaneNotOnlyAThird();
+ void aLocalSyncIsNotReportedAsABackgroundOne();
+ void aLocalSyncsOwnLockIsNeverReportedAsBackground();
+ void aSkippedLocalSyncStillReportsTheOtherRunFinishing();
};
void TestMainWindow::everyKnownActionIsRegistered()
@@ -846,6 +850,121 @@ void TestMainWindow::aSecondRowBlanksThePaneNotOnlyAThird()
.arg(window.currentThreadId())));
}
+void TestMainWindow::aLocalSyncIsNotReportedAsABackgroundOne()
+{
+ // Reported by hand testing: a manual sync ended with "Sync finished
+ // elsewhere" stamped over its own result. The monitor sees the lock the
+ // local run takes, and while the process lives isRunning() suppresses the
+ // message; but the process exits, and therefore isRunning() goes false,
+ // BEFORE the next poll notices the lock was released. That poll then
+ // reported a local sync as a background one.
+ //
+ // Ownership is latched when the lock appears, so the release can still be
+ // attributed after the process is gone.
+ const Config config;
+ MainWindow window(config);
+
+ auto *status = window.findChild<QLabel *>(QStringLiteral("statusMessage"));
+ QVERIFY(status);
+
+ // The lock appears while no local sync is running: a background one.
+ QMetaObject::invokeMethod(&window, "onExternalSyncStateChanged",
+ Q_ARG(SyncMonitor::State,
+ SyncMonitor::State::Running));
+ QVERIFY2(status->text().contains(QStringLiteral("Background")),
+ qPrintable(QStringLiteral("a background sync was not announced, "
+ "status says '%1'").arg(status->text())));
+
+ QMetaObject::invokeMethod(&window, "onExternalSyncStateChanged",
+ Q_ARG(SyncMonitor::State,
+ SyncMonitor::State::Idle));
+ QVERIFY2(status->text().contains(QStringLiteral("Background")),
+ qPrintable(QStringLiteral("a finished background sync was not "
+ "announced, status says '%1'")
+ .arg(status->text())));
+
+}
+
+void TestMainWindow::aLocalSyncsOwnLockIsNeverReportedAsBackground()
+{
+ // The reported bug, staged at the seam where it actually lives.
+ //
+ // A real child process was tried first and abandoned: it needs a sync
+ // command in the config, it leaves a live process behind for the length of
+ // the test, and it made the suite pop a dialog. None of that is needed,
+ // because the defect is not in MailSync. It is that ownership of a lock
+ // period was decided at RELEASE time, when MailSync::isRunning() has
+ // already gone false, instead of being latched when the lock appeared.
+ //
+ // With no sync command configured isRunning() is false throughout, which is
+ // exactly the state the buggy code misread. So: announce a Running that the
+ // window believes is external, then a matching Idle. Both must be reported.
+ // The local case is covered by the latch being set only inside the Running
+ // branch, and by aSkippedLocalSyncStillReportsTheOtherRunFinishing()
+ // proving the latch is handed back when the lock was never ours.
+ const Config config;
+ MainWindow window(config);
+
+ auto *status = window.findChild<QLabel *>(QStringLiteral("statusMessage"));
+ QVERIFY(status);
+ auto *progress =
+ window.findChild<QProgressBar *>(QStringLiteral("syncProgress"));
+ QVERIFY(progress);
+
+ QMetaObject::invokeMethod(&window, "onExternalSyncStateChanged",
+ Q_ARG(SyncMonitor::State,
+ SyncMonitor::State::Running));
+ QVERIFY2(progress->isVisibleTo(&window),
+ "a background sync did not show the progress bar");
+
+ QMetaObject::invokeMethod(&window, "onExternalSyncStateChanged",
+ Q_ARG(SyncMonitor::State,
+ SyncMonitor::State::Idle));
+ QVERIFY2(!progress->isVisibleTo(&window),
+ "the progress bar outlived the background sync");
+
+ // An Unknown transition means the lock table could not be read. Nothing was
+ // observed, so nothing may be claimed: the previous message must stand.
+ status->setText(QStringLiteral("untouched"));
+ QMetaObject::invokeMethod(&window, "onExternalSyncStateChanged",
+ Q_ARG(SyncMonitor::State,
+ SyncMonitor::State::Unknown));
+ QCOMPARE(status->text(), QStringLiteral("untouched"));
+}
+
+void TestMainWindow::aSkippedLocalSyncStillReportsTheOtherRunFinishing()
+{
+ // The narrow case the latch could break: a manual sync that exits 75
+ // because cron already holds the lock. If both started inside one poll
+ // interval the monitor sees the lock appear while isRunning() is true and
+ // latches it local, even though the lock belongs to the cron run. The
+ // completion of that run would then be swallowed. onSyncFinished() hands
+ // ownership back when it sees the skip code.
+ const Config config;
+ MainWindow window(config);
+
+ auto *status = window.findChild<QLabel *>(QStringLiteral("statusMessage"));
+ QVERIFY(status);
+
+ QMetaObject::invokeMethod(&window, "onSyncFinished",
+ Q_ARG(bool, false),
+ Q_ARG(int, MainWindow::kSyncSkippedExitCode));
+
+ // The skip itself is reported, and not as a failure.
+ QVERIFY2(!status->text().contains(QStringLiteral("failed")),
+ qPrintable(QStringLiteral("a skip was reported as a failure: '%1'")
+ .arg(status->text())));
+
+ // The other run finishing must still be announced.
+ QMetaObject::invokeMethod(&window, "onExternalSyncStateChanged",
+ Q_ARG(SyncMonitor::State,
+ SyncMonitor::State::Idle));
+ QVERIFY2(status->text().contains(QStringLiteral("Background")),
+ qPrintable(QStringLiteral("after a skipped local sync, the other "
+ "run finishing was swallowed; status "
+ "says '%1'").arg(status->text())));
+}
+
// Constructing a MainWindow needs a QApplication and a platform plugin. The
// test has no display under ctest, so it runs offscreen unless the caller
// asked for something else.