Reload a project changed on disk, fingerprinted by its manifest and image sizes (12.13a)

This commit is contained in:
雷电芽衣 committed 2026-09-29 20:13:20 -04:00
1 parent aa253d9715
commit 838d5b1459
20 files changed
+1388 -8

No files matched your search

+72
View File
@@ -0,0 +1,72 @@
#include "IO/ProjectDigest.h"
#include <QDir>
#include <QFile>
#include <QTemporaryDir>
#include <QtTest>
#include <dirent.h>
#include <dlfcn.h>
#include <unistd.h>
// An image gone between listing and looking: no digest.
namespace {
// The file to remove once its folder's listing ends.
QByteArray vanishing;
bool vanished = false;
bool listsImages(DIR *directory)
{
char path[4096];
const QByteArray link = "/proc/self/fd/" + QByteArray::number(dirfd(directory));
const ssize_t size = readlink(link.constData(), path, sizeof path - 1);
return size > 0 && QByteArray(path, int(size)).endsWith("/images");
}
// At the listing's end the image goes, before any lookup.
template <typename Entry> Entry *listed(DIR *directory, Entry *entry)
{
if (!entry && !vanishing.isEmpty() && !vanished && listsImages(directory))
vanished = ::unlink(vanishing.constData()) == 0;
return entry;
}
}
// These take libc's place in the test executable.
extern "C" struct dirent *readdir(DIR *directory)
{
static const auto real = reinterpret_cast<struct dirent *(*)(DIR *)>(dlsym(RTLD_NEXT, "readdir"));
return listed(directory, real(directory));
}
extern "C" struct dirent64 *readdir64(DIR *directory)
{
static const auto real = reinterpret_cast<struct dirent64 *(*)(DIR *)>(dlsym(RTLD_NEXT, "readdir64"));
return listed(directory, real(directory));
}
class ProjectDigestRaceTests : public QObject {
Q_OBJECT
private slots:
void anImageGoneMidListingGivesNoDigest();
};
void ProjectDigestRaceTests::anImageGoneMidListingGivesNoDigest()
{
QTemporaryDir root;
const QString path = root.filePath(QStringLiteral("Race.comp"));
QVERIFY(QDir().mkpath(path + QStringLiteral("/images")));
for (const QString &name : {QStringLiteral("manifest.json"), QStringLiteral("images/A.png"), QStringLiteral("images/B.png")}) {
QFile file(path + QLatin1Char('/') + name);
QVERIFY(file.open(QIODevice::WriteOnly) && file.write("bytes") == 5);
}
// Listed whole, one image looked up after it is gone.
const QByteArray whole = ProjectDigest::compute(path);
vanishing = QFile::encodeName(path + QStringLiteral("/images/A.png"));
QVERIFY_THROWS_EXCEPTION(std::runtime_error, ProjectDigest::compute(path));
QVERIFY(vanished);
// Settled, the package fingerprints again, without it.
vanishing.clear();
QVERIFY(ProjectDigest::compute(path) != whole);
}
QTEST_GUILESS_MAIN(ProjectDigestRaceTests)
#include "ProjectDigestRaceTests.moc"