diff options
Diffstat (limited to 'tests/baseline/mapitems/tst_baseline_mapitems.cpp')
-rw-r--r-- | tests/baseline/mapitems/tst_baseline_mapitems.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/tests/baseline/mapitems/tst_baseline_mapitems.cpp b/tests/baseline/mapitems/tst_baseline_mapitems.cpp new file mode 100644 index 00000000..3ee8f90d --- /dev/null +++ b/tests/baseline/mapitems/tst_baseline_mapitems.cpp @@ -0,0 +1,240 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <qbaselinetest.h> + +#include <QtCore/QDirIterator> +#include <QtCore/QDebug> +#include <QtCore/QProcess> +#include <QtGui/QGuiApplication> +#include <QtGui/QImage> +#include <QtGui/QPalette> +#include <QtGui/QFont> + +#include <algorithm> + +QString blockify(const QByteArray& s) +{ + const char* indent = "\n | "; + QByteArray block = s.trimmed(); + block.replace('\n', indent); + block.prepend(indent); + block.append('\n'); + return block; +} + +class tst_Baseline_MapItems : public QObject +{ + Q_OBJECT + +public: + tst_Baseline_MapItems(); + +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + + void circle_data() { setupTestSuite(); } + void circle() { runTest(); } + +private: + void test(); + + void setupTestSuite(); + void runTest(const QString& style = QString()); + bool renderAndGrab(const QString& qmlFile, const QStringList& extraArgs, QImage *screenshot, QString *errMsg); + + QString testSuitePath; + QString grabberPath; + QStringList testFiles; + int consecutiveErrors; // Not test failures (image mismatches), but system failures (so no image at all) + bool aborted; // This run given up because of too many system failures +}; + + +tst_Baseline_MapItems::tst_Baseline_MapItems() + : consecutiveErrors(0), aborted(false) +{ +} + + +void tst_Baseline_MapItems::initTestCase() +{ + QString dataDir = QFINDTESTDATA("./data/."); + if (dataDir.isEmpty()) + dataDir = QStringLiteral("data"); + QFileInfo fi(dataDir); + if (!fi.exists() || !fi.isDir() || !fi.isReadable()) + QSKIP("Test suite data directory missing or unreadable: " + fi.canonicalFilePath().toLatin1()); + testSuitePath = fi.canonicalFilePath(); + +#if defined(Q_OS_WIN) + grabberPath = QFINDTESTDATA("../../../../qtdeclarative/tests/baseline/scenegraph/qmlscenegrabber.exe"); +#elif defined(Q_OS_DARWIN) + grabberPath = QFINDTESTDATA("../../../../qtdeclarative/tests/baseline/scenegraph/qmlscenegrabber.app/Contents/MacOS/qmlscenegrabber"); +#else + grabberPath = QFINDTESTDATA("../../../../qtdeclarative/tests/baseline/scenegraph/qmlscenegrabber"); +#endif + if (grabberPath.isEmpty()) + grabberPath = QCoreApplication::applicationDirPath() + "../../../../qtdeclarative/tests/baseline/scenegraph/qmlscenegrabber.exe"; + + QBaselineTest::setProject("MapItems"); + // Set key platform properties that are relevant for the appearance of controls + const QString platformName = QGuiApplication::platformName() + "-" + QSysInfo::productType(); + QBaselineTest::addClientProperty("PlatformName", platformName); + QBaselineTest::addClientProperty("OSVersion", QSysInfo::productVersion()); + + const char *backendVarName = "QT_QUICK_BACKEND"; + const QString backend = qEnvironmentVariable(backendVarName, QString::fromLatin1("default")); + QBaselineTest::addClientProperty(QString::fromLatin1(backendVarName), backend); + + QString stack = backend; + if (stack != QLatin1String("software")) { +#if defined(Q_OS_WIN) + const char *defaultRhiBackend = "d3d11"; +#elif defined(Q_OS_DARWIN) + const char *defaultRhiBackend = "metal"; +#else + const char *defaultRhiBackend = "opengl"; +#endif + const QString rhiBackend = qEnvironmentVariable("QSG_RHI_BACKEND", QString::fromLatin1(defaultRhiBackend)); + stack = QString::fromLatin1("RHI_%1").arg(rhiBackend); + } + + QBaselineTest::addClientProperty(QString::fromLatin1("GraphicsStack"), stack); + + // Assume that text that's darker than the background means we run in light mode + // See also qwidgetbaselinetest.cpp + QPalette palette; + QFont font; + QByteArray appearanceBytes; + { + QDataStream appearanceStream(&appearanceBytes, QIODevice::WriteOnly); + appearanceStream << palette << font; + const qreal screenDpr = QGuiApplication::primaryScreen()->devicePixelRatio(); + if (screenDpr != 1.0) { + qWarning() << "DPR is" << screenDpr << "- images will not be compared to 1.0 baseline!"; + appearanceStream << screenDpr; + } + } + const quint16 appearanceId = qChecksum(appearanceBytes); + + const QColor windowColor = palette.window().color(); + const QColor textColor = palette.text().color(); + const QString appearanceIdString = (windowColor.value() > textColor.value() + ? QString("light-%1") : QString("dark-%1")) + .arg(appearanceId, 0, 16); + QBaselineTest::addClientProperty("AppearanceID", appearanceIdString); + + QByteArray msg; + if (!QBaselineTest::connectToBaselineServer(&msg)) + QSKIP(msg); +} + +void tst_Baseline_MapItems::init() +{ + consecutiveErrors = 0; + aborted = false; +} + +void tst_Baseline_MapItems::cleanup() +{ + // Allow subsystems time to settle + if (!aborted) + QTest::qWait(20); +} + +void tst_Baseline_MapItems::setupTestSuite() +{ + QTest::addColumn<QString>("qmlFile"); + + const QStringView qmlExt(u".qml"); + if (testFiles.isEmpty()) { + QDirIterator it(testSuitePath, QDirIterator::Subdirectories); + while (it.hasNext()) { + const QString fp = it.next().toLatin1(); + if (fp.endsWith(qmlExt)) + testFiles.append(it.filePath()); + } + std::sort(testFiles.begin(), testFiles.end()); + } + + if (testFiles.isEmpty()) + QSKIP("No .qml test files found in " + testSuitePath.toLatin1()); + + for (const auto &filePath : std::as_const(testFiles)) { + QString itemName = filePath.sliced(testSuitePath.size() + 1); + itemName = itemName.left(itemName.size() - qmlExt.size()); + QBaselineTest::newRow(itemName.toLatin1()) << filePath; + } +} + + +void tst_Baseline_MapItems::runTest(const QString& style) +{ + if (aborted) + QSKIP("System too unstable."); + + QFETCH(QString, qmlFile); + + QImage screenShot; + QString errorMessage; + QStringList args; + if (!style.isEmpty()) + args.append({"-style", style}); + if (renderAndGrab(qmlFile, args, &screenShot, &errorMessage)) { + consecutiveErrors = 0; + } else { + if (++consecutiveErrors >= 3) + aborted = true; // Just give up if screen grabbing fails 3 times in a row + QFAIL(qPrintable("QuickView grabbing failed: " + errorMessage)); + } + + QBASELINE_TEST(screenShot); +} + + +bool tst_Baseline_MapItems::renderAndGrab(const QString& qmlFile, const QStringList& extraArgs, QImage *screenshot, QString *errMsg) +{ + bool usePipe = true; // Whether to transport the grabbed image using temp. file or pipe. TBD: cmdline option + QProcess grabber; + grabber.setProcessChannelMode(QProcess::ForwardedErrorChannel); + QStringList args = extraArgs; + QString tmpfile = usePipe ? QString("-") : QString("/tmp/qmlscenegrabber-%1-out.ppm").arg(QCoreApplication::applicationPid()); + args << qmlFile << "-o" << tmpfile; + grabber.start(grabberPath, args, QIODevice::ReadOnly); + grabber.waitForFinished(17000); //### hardcoded, must be larger than the scene timeout in qmlscenegrabber + if (grabber.state() != QProcess::NotRunning) { + grabber.terminate(); + grabber.waitForFinished(3000); + } + QImage img; + bool res = usePipe ? img.load(&grabber, "ppm") : img.load(tmpfile); + if (!res || img.isNull()) { + if (errMsg) { + QString s("Failed to grab screen. qmlscenegrabber exitcode: %1. Process error: %2. Stderr:%3"); + *errMsg = s.arg(grabber.exitCode()).arg(grabber.errorString()).arg(blockify(grabber.readAllStandardError())); + } + if (!usePipe) + QFile::remove(tmpfile); + return false; + } + if (screenshot) + *screenshot = img; + if (!usePipe) + QFile::remove(tmpfile); + return true; +} + +#define main _realmain +QTEST_MAIN(tst_Baseline_MapItems) +#undef main + +int main(int argc, char *argv[]) +{ + QBaselineTest::handleCmdLineArgs(&argc, &argv); + return _realmain(argc, argv); +} + +#include "tst_baseline_mapitems.moc" |