mirror of
https://github.com/Mudlet/Mudlet
synced 2026-08-13 18:26:27 -04:00
#### Brief overview of PR changes/additions - Renames the six test executables whose filenames trip Windows' UAC installer detection: `UpdaterChecksumTest` to `ReleaseChecksumPairingTest`, `UpdaterPlatformAssetTest` to `ReleasePlatformAssetTest`, `UpdaterTeardownTest` to `NewReleaseDialogTeardownTest`, `PackageSelfUninstallTest` to `PackageSelfRemovalTest`, `PackageUninstallSaveTeardownTest` to `PackageRemovalSaveTeardownTest`, `ActionSelfUninstallTest` to `ActionSelfRemovalTest`. Assertions are untouched. - Adds a configure-time gate in `test/CMakeLists.txt` that fails with an actionable message if any test executable name contains install, setup, update or patch. It checks both the targets a configuration builds and the test source filenames, so conditionally registered tests cannot slip past it. - Documents the naming rule in `test/README.md`. #### Motivation for adding to Mudlet Windows treats an unsigned executable named that way as an installer and refuses to start it, so those six tests reported `BAD_COMMAND` for anyone running the suite from an ordinary Windows shell - and because CI runners are elevated, nothing caught it as more tests were added. #### Other info (issues closed, discussion etc) Fixes #9748 The gate was verified to fire on a target named after the guard statement, on one in a subdirectory, on `EventDispatcherTest` (the message names the offending substring, since "dispatch" contains "patch"), on a test registered only under `USE_UPDATER` when configuring with the updater off, and to fail loudly if the walk ever stops finding executables. **Test case:** `cmake --build build && ctest --test-dir build` - 92/92 pass; adding a test named e.g. `FooUpdateTest` fails the configure with an explanation. Assisted-by: Claude:claude-opus-5
225 lines
10 KiB
C++
225 lines
10 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2026 by Vadim Peretokin - vadim.peretokin@mudlet.org *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
#include "../src/updater/Feed.h"
|
|
#include "../src/updater/Release.h"
|
|
|
|
#include <QtTest/QtTest>
|
|
|
|
#include <QJsonArray>
|
|
#include <QJsonObject>
|
|
|
|
/*
|
|
* Covers which releases the updater is willing to offer as an update.
|
|
*
|
|
* The 2026-08-07 PTB published a Windows installer and nothing else: the Linux
|
|
* and macOS build jobs failed, so their assets were never attached. Linux PTB
|
|
* users got a red "no download available for your platform" error on the
|
|
* console on every check, because the release was offered as an update and the
|
|
* download then had nothing to fetch. A partly published release is valid and
|
|
* will happen again, so it has to be passed over instead - as does one that
|
|
* published a binary but no SHA256SUMS.txt, since the download refuses to
|
|
* install what it cannot verify.
|
|
*/
|
|
|
|
namespace {
|
|
// Only windowsOnlyTag is a real release; the complete, unverifiable and
|
|
// installed ones are constructed around it
|
|
const auto windowsOnlyTag = QStringLiteral("Mudlet-4.22.0-ptb-2026-08-07-eaa991b9");
|
|
const auto unverifiableTag = QStringLiteral("Mudlet-4.22.0-ptb-2026-08-07-5e4d3c2b");
|
|
const auto completeTag = QStringLiteral("Mudlet-4.22.0-ptb-2026-08-06-1a2b3c4d");
|
|
const auto windowsOnlyVersion = QStringLiteral("4.22.0-ptb-2026-08-07-eaa991b9");
|
|
const auto completeVersion = QStringLiteral("4.22.0-ptb-2026-08-06-1a2b3c4d");
|
|
const auto installedVersion = QStringLiteral("4.22.0-ptb-2026-08-05-9f8e7d6c");
|
|
|
|
const auto windowsSuffix = QStringLiteral("-windows-64.exe");
|
|
const auto linuxSuffix = QStringLiteral("-linux-x64.AppImage.tar");
|
|
const auto intelMacSuffix = QStringLiteral("-x86_64.dmg");
|
|
const auto appleSiliconSuffix = QStringLiteral("-arm64.dmg");
|
|
|
|
QJsonObject makeAsset(const QString& tag, const QString& name)
|
|
{
|
|
QJsonObject asset;
|
|
asset.insert(QStringLiteral("name"), name);
|
|
asset.insert(QStringLiteral("browser_download_url"), QStringLiteral("https://github.com/Mudlet/Mudlet/releases/download/%1/%2").arg(tag, name));
|
|
asset.insert(QStringLiteral("size"), 137252032);
|
|
return asset;
|
|
}
|
|
|
|
QJsonObject releaseJson(const QString& tag, const QString& publishedAt, const QStringList& assetSuffixes, bool withChecksums = true)
|
|
{
|
|
QJsonArray assets;
|
|
for (const auto& suffix : assetSuffixes) {
|
|
assets.append(makeAsset(tag, QStringLiteral("%1%2").arg(tag, suffix)));
|
|
}
|
|
if (withChecksums) {
|
|
assets.append(makeAsset(tag, QStringLiteral("SHA256SUMS.txt")));
|
|
}
|
|
|
|
QJsonObject release;
|
|
release.insert(QStringLiteral("tag_name"), tag);
|
|
release.insert(QStringLiteral("published_at"), publishedAt);
|
|
release.insert(QStringLiteral("prerelease"), true);
|
|
release.insert(QStringLiteral("draft"), false);
|
|
release.insert(QStringLiteral("body"), QStringLiteral("- fixed a thing\n"));
|
|
release.insert(QStringLiteral("assets"), assets);
|
|
return release;
|
|
}
|
|
|
|
QJsonObject windowsOnlyRelease()
|
|
{
|
|
return releaseJson(windowsOnlyTag, QStringLiteral("2026-08-07T02:14:11Z"), {windowsSuffix});
|
|
}
|
|
|
|
// Published its Linux binary, but not the checksums that binary is verified against
|
|
QJsonObject unverifiableRelease()
|
|
{
|
|
return releaseJson(unverifiableTag, QStringLiteral("2026-08-07T04:22:09Z"), {linuxSuffix}, /*withChecksums=*/false);
|
|
}
|
|
|
|
QJsonObject completeRelease()
|
|
{
|
|
return releaseJson(completeTag, QStringLiteral("2026-08-06T02:11:47Z"), {windowsSuffix, linuxSuffix, intelMacSuffix, appleSiliconSuffix});
|
|
}
|
|
|
|
// Newest first, the order Feed sorts its releases into
|
|
QList<dblsqd::Release> feedReleases(const QString& os, const QString& arch)
|
|
{
|
|
return {dblsqd::Release(windowsOnlyRelease(), os, arch), dblsqd::Release(completeRelease(), os, arch)};
|
|
}
|
|
|
|
dblsqd::Release installedRelease()
|
|
{
|
|
return dblsqd::Release(installedVersion, QDateTime::fromString(QStringLiteral("2026-08-05T02:09:03Z"), Qt::ISODate));
|
|
}
|
|
} // namespace
|
|
|
|
class ReleasePlatformAssetTest : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
void linuxIsNotOfferedTheReleaseWithoutALinuxAsset();
|
|
void windowsIsStillOfferedIt();
|
|
void intelMacIsNotOfferedTheReleaseWithoutADmg();
|
|
void appleSiliconIsNotOfferedTheReleaseWithoutADmg();
|
|
void aReleaseWithOnlyItsChecksumsFileIsNotOffered();
|
|
void aReleaseWithoutChecksumsIsNotOffered();
|
|
void theVerifiableReleaseIsOfferedInsteadOfTheNewerUnverifiableOne();
|
|
void aPlatformWithNoAssetsAtAllIsOfferedNothing();
|
|
void nothingIsOfferedOnceTheOnlyNewerReleaseIsIncomplete();
|
|
void thePassedOverReleaseStaysReadableForTheChangelog();
|
|
};
|
|
|
|
// The regression: this is the update that produced a red console error twice a day
|
|
void ReleasePlatformAssetTest::linuxIsNotOfferedTheReleaseWithoutALinuxAsset()
|
|
{
|
|
const auto updates = dblsqd::Feed::selectUpdates(feedReleases(QStringLiteral("linux"), QStringLiteral("x86_64")), installedRelease());
|
|
|
|
QCOMPARE(updates.size(), 1);
|
|
QCOMPARE(updates.first().getVersion(), completeVersion);
|
|
QVERIFY(updates.first().getDownloadUrl().fileName().endsWith(linuxSuffix));
|
|
}
|
|
|
|
void ReleasePlatformAssetTest::windowsIsStillOfferedIt()
|
|
{
|
|
const auto updates = dblsqd::Feed::selectUpdates(feedReleases(QStringLiteral("win"), QStringLiteral("x86_64")), installedRelease());
|
|
|
|
QCOMPARE(updates.size(), 2);
|
|
QCOMPARE(updates.first().getVersion(), windowsOnlyVersion);
|
|
QCOMPARE(updates.last().getVersion(), completeVersion);
|
|
}
|
|
|
|
void ReleasePlatformAssetTest::intelMacIsNotOfferedTheReleaseWithoutADmg()
|
|
{
|
|
const auto updates = dblsqd::Feed::selectUpdates(feedReleases(QStringLiteral("mac"), QStringLiteral("x86_64")), installedRelease());
|
|
|
|
QCOMPARE(updates.size(), 1);
|
|
QCOMPARE(updates.first().getVersion(), completeVersion);
|
|
QVERIFY(updates.first().getDownloadUrl().fileName().endsWith(intelMacSuffix));
|
|
}
|
|
|
|
void ReleasePlatformAssetTest::appleSiliconIsNotOfferedTheReleaseWithoutADmg()
|
|
{
|
|
const auto updates = dblsqd::Feed::selectUpdates(feedReleases(QStringLiteral("mac"), QStringLiteral("arm64")), installedRelease());
|
|
|
|
QCOMPARE(updates.size(), 1);
|
|
QCOMPARE(updates.first().getVersion(), completeVersion);
|
|
QVERIFY(updates.first().getDownloadUrl().fileName().endsWith(appleSiliconSuffix));
|
|
}
|
|
|
|
// A release whose binaries are still uploading looks the same as one that lost a build job
|
|
void ReleasePlatformAssetTest::aReleaseWithOnlyItsChecksumsFileIsNotOffered()
|
|
{
|
|
const QList<dblsqd::Release> releases{dblsqd::Release(releaseJson(windowsOnlyTag, QStringLiteral("2026-08-07T02:14:11Z"), {}), QStringLiteral("linux"), QStringLiteral("x86_64"))};
|
|
|
|
QVERIFY(dblsqd::Feed::selectUpdates(releases, installedRelease()).isEmpty());
|
|
}
|
|
|
|
// The download refuses to install what it cannot verify, so a release whose
|
|
// SHA256SUMS.txt is missing is as uninstallable as one missing its binary
|
|
void ReleasePlatformAssetTest::aReleaseWithoutChecksumsIsNotOffered()
|
|
{
|
|
const QList<dblsqd::Release> releases{dblsqd::Release(unverifiableRelease(), QStringLiteral("linux"), QStringLiteral("x86_64"))};
|
|
|
|
QVERIFY(dblsqd::Feed::selectUpdates(releases, installedRelease()).isEmpty());
|
|
}
|
|
|
|
void ReleasePlatformAssetTest::theVerifiableReleaseIsOfferedInsteadOfTheNewerUnverifiableOne()
|
|
{
|
|
const QList<dblsqd::Release> releases{dblsqd::Release(unverifiableRelease(), QStringLiteral("linux"), QStringLiteral("x86_64")),
|
|
dblsqd::Release(completeRelease(), QStringLiteral("linux"), QStringLiteral("x86_64"))};
|
|
|
|
const auto updates = dblsqd::Feed::selectUpdates(releases, installedRelease());
|
|
|
|
QCOMPARE(updates.size(), 1);
|
|
QCOMPARE(updates.first().getVersion(), completeVersion);
|
|
}
|
|
|
|
// Mudlet publishes no binaries for the platforms it is packaged for by others,
|
|
// so those builds are told there is no update rather than shown a failure they
|
|
// can do nothing about, twice a day, forever
|
|
void ReleasePlatformAssetTest::aPlatformWithNoAssetsAtAllIsOfferedNothing()
|
|
{
|
|
QVERIFY(dblsqd::Feed::selectUpdates(feedReleases(QStringLiteral("freebsd"), QStringLiteral("x86_64")), installedRelease()).isEmpty());
|
|
}
|
|
|
|
// What a Linux user on the previous PTB sees: no update, rather than an error
|
|
void ReleasePlatformAssetTest::nothingIsOfferedOnceTheOnlyNewerReleaseIsIncomplete()
|
|
{
|
|
const QList<dblsqd::Release> releases{dblsqd::Release(windowsOnlyRelease(), QStringLiteral("linux"), QStringLiteral("x86_64"))};
|
|
const dblsqd::Release installed(completeVersion, QDateTime::fromString(QStringLiteral("2026-08-06T02:11:47Z"), Qt::ISODate));
|
|
|
|
QVERIFY(dblsqd::Feed::selectUpdates(releases, installed).isEmpty());
|
|
}
|
|
|
|
// A release passed over here still has to carry its version and notes: the
|
|
// changelog dialogs render the unfiltered release list
|
|
// (UpdateDialog::generateChangelogDocument)
|
|
void ReleasePlatformAssetTest::thePassedOverReleaseStaysReadableForTheChangelog()
|
|
{
|
|
const dblsqd::Release release(windowsOnlyRelease(), QStringLiteral("linux"), QStringLiteral("x86_64"));
|
|
|
|
QVERIFY(release.getDownloadUrl().isEmpty());
|
|
QCOMPARE(release.getVersion(), windowsOnlyVersion);
|
|
QCOMPARE(release.getChangelog(), QStringLiteral("- fixed a thing\n"));
|
|
}
|
|
|
|
#include "ReleasePlatformAssetTest.moc"
|
|
QTEST_MAIN(ReleasePlatformAssetTest)
|