diff --git a/tests/auto/gui/image/qimageiohandler/CMakeLists.txt b/tests/auto/gui/image/qimageiohandler/CMakeLists.txt index 9fbd9c9b9f4..6f9f1ce7ea6 100644 --- a/tests/auto/gui/image/qimageiohandler/CMakeLists.txt +++ b/tests/auto/gui/image/qimageiohandler/CMakeLists.txt @@ -11,9 +11,22 @@ if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) endif() +file(GLOB_RECURSE test_data_glob + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + images/*) + +list(APPEND test_data ${test_data_glob}) + +add_subdirectory(plugin) + qt_internal_add_test(tst_qimageiohandler SOURCES tst_qimageiohandler.cpp + pluginlog.h LIBRARIES Qt::Gui + TestImagePlugin + TESTDATA ${test_data} ) + +add_dependencies(tst_qimageiohandler TestImagePlugin) diff --git a/tests/auto/gui/image/qimageiohandler/images/black.bar b/tests/auto/gui/image/qimageiohandler/images/black.bar new file mode 100644 index 00000000000..41e34fd7798 Binary files /dev/null and b/tests/auto/gui/image/qimageiohandler/images/black.bar differ diff --git a/tests/auto/gui/image/qimageiohandler/images/black.bmp b/tests/auto/gui/image/qimageiohandler/images/black.bmp new file mode 100644 index 00000000000..41e34fd7798 Binary files /dev/null and b/tests/auto/gui/image/qimageiohandler/images/black.bmp differ diff --git a/tests/auto/gui/image/qimageiohandler/images/blue b/tests/auto/gui/image/qimageiohandler/images/blue new file mode 100644 index 00000000000..a6c5d80d5aa --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/images/blue @@ -0,0 +1 @@ +BLUE diff --git a/tests/auto/gui/image/qimageiohandler/images/cyan.gif b/tests/auto/gui/image/qimageiohandler/images/cyan.gif new file mode 100644 index 00000000000..840da8ceb62 Binary files /dev/null and b/tests/auto/gui/image/qimageiohandler/images/cyan.gif differ diff --git a/tests/auto/gui/image/qimageiohandler/images/green.foo b/tests/auto/gui/image/qimageiohandler/images/green.foo new file mode 100644 index 00000000000..a5b73ed2e94 --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/images/green.foo @@ -0,0 +1 @@ +green diff --git a/tests/auto/gui/image/qimageiohandler/images/red.png b/tests/auto/gui/image/qimageiohandler/images/red.png new file mode 100644 index 00000000000..f24fc29aa68 --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/images/red.png @@ -0,0 +1 @@ +RED diff --git a/tests/auto/gui/image/qimageiohandler/images/white.foo b/tests/auto/gui/image/qimageiohandler/images/white.foo new file mode 100644 index 00000000000..8085a0d4aa7 Binary files /dev/null and b/tests/auto/gui/image/qimageiohandler/images/white.foo differ diff --git a/tests/auto/gui/image/qimageiohandler/images/yellow.jpg b/tests/auto/gui/image/qimageiohandler/images/yellow.jpg new file mode 100644 index 00000000000..d1ed081dfb9 --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/images/yellow.jpg @@ -0,0 +1 @@ +yellow diff --git a/tests/auto/gui/image/qimageiohandler/plugin/CMakeLists.txt b/tests/auto/gui/image/qimageiohandler/plugin/CMakeLists.txt new file mode 100644 index 00000000000..e0aaddf2406 --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/plugin/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## TestImagePlugin: +##################################################################### + +qt_add_plugin(TestImagePlugin + STATIC + OUTPUT_NAME qtestimageplugin + PLUGIN_TYPE imageformats + main.cpp + ../pluginlog.h +) + +qt_autogen_tools_initial_setup(TestImagePlugin) +target_link_libraries(TestImagePlugin PRIVATE Qt::Core Qt::Gui) diff --git a/tests/auto/gui/image/qimageiohandler/plugin/main.cpp b/tests/auto/gui/image/qimageiohandler/plugin/main.cpp new file mode 100644 index 00000000000..5a1289c2abd --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/plugin/main.cpp @@ -0,0 +1,89 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include +#include +#include "../pluginlog.h" + +class TestImagePlugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "plugin.json") + +public: + QImageIOPlugin::Capabilities capabilities(QIODevice *device, const QByteArray &format) const override; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override; +}; + +class MyPngHandler : public QImageIOHandler +{ +public: + bool canRead() const override + { + if (canRead(device())) { + setFormat(device()->peek(1).isUpper() ? "png" : "foo"); + return true; + } + return false; + } + + bool read(QImage *image) override + { + QColor col = QColor::fromString(device()->readAll().simplified()); + if (col.isValid()) { + QImage img(32, 32, QImage::Format_RGB32); + img.fill(col); + *image = img; + return true; + } else { + return false; + } + } + + static bool canRead(QIODevice *device) + { + return device ? QColor::isValidColorName(device->peek(16).simplified()) : false; + } +}; + + +QImageIOPlugin::Capabilities TestImagePlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "png" || format == "foo" || format == "gif") { + PluginLog::append("formatname-matched"); + return (format == "gif") ? CanWrite : CanRead; + } + + Capabilities cap; + if (!format.isEmpty()) { + PluginLog::append("formatname-unmatched"); + return cap; + } + + if (!device->isOpen()) + return cap; + + if (device->isReadable()) { + if (MyPngHandler::canRead(device)) { + PluginLog::append("contents-matched"); + cap |= CanRead; + } else { + PluginLog::append("contents-unmatched"); + } + } + + if (device->isWritable()) + cap |= CanWrite; + + return cap; +} + +QImageIOHandler *TestImagePlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new MyPngHandler; + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +#include "main.moc" diff --git a/tests/auto/gui/image/qimageiohandler/plugin/plugin.json b/tests/auto/gui/image/qimageiohandler/plugin/plugin.json new file mode 100644 index 00000000000..be5282af2fa --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/plugin/plugin.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "png", "foo", "gif" ], + "MimeTypes": [ "image/png", "image/foo", "image/gif" ] +} diff --git a/tests/auto/gui/image/qimageiohandler/pluginlog.h b/tests/auto/gui/image/qimageiohandler/pluginlog.h new file mode 100644 index 00000000000..140a54c4029 --- /dev/null +++ b/tests/auto/gui/image/qimageiohandler/pluginlog.h @@ -0,0 +1,22 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef PLUGINLOG_H +#define PLUGINLOG_H + +#include +#include + +class PluginLog { +public: + static QStringList get() { return qApp->property(testProp).toStringList(); } + static void set(const QStringList &log) { qApp->setProperty(testProp, log); } + static void clear() { set({}); } + static void append(const QString &msg) { set(get() << msg); } + static int size() { return get().size(); } + static QString item(int index) { return get().value(index); } +private: + static constexpr auto testProp = "_q_testimageplugin"; +}; + +#endif // PLUGINLOG_H diff --git a/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp b/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp index bd325e185c6..aefc427cac5 100644 --- a/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp +++ b/tests/auto/gui/image/qimageiohandler/tst_qimageiohandler.cpp @@ -6,8 +6,11 @@ #include #include +#include #include #include +#include +#include "pluginlog.h" class tst_QImageIOHandler : public QObject { @@ -16,9 +19,27 @@ Q_OBJECT public: tst_QImageIOHandler(); virtual ~tst_QImageIOHandler(); + static void initMain() + { + QHashSeed::setDeterministicGlobalSeed(); + } private slots: void getSetCheck(); + void hasCustomPluginFormat(); + void pluginRead_data(); + void pluginRead(); + void pluginNoAutoDetection_data(); + void pluginNoAutoDetection(); + void pluginDecideFromContent_data(); + void pluginDecideFromContent(); + void pluginDetectedFormat_data(); + void pluginDetectedFormat(); + void pluginWrongFormat_data(); + void pluginWrongFormat(); +private: + QString m_prefix; + QList m_supportedReadFormats; }; class MyImageIOHandler : public QImageIOHandler @@ -31,6 +52,8 @@ public: tst_QImageIOHandler::tst_QImageIOHandler() { + m_prefix = QFINDTESTDATA("images/"); + m_supportedReadFormats = QImageReader::supportedImageFormats(); } tst_QImageIOHandler::~tst_QImageIOHandler() @@ -51,5 +74,185 @@ void tst_QImageIOHandler::getSetCheck() delete var1; } +Q_IMPORT_PLUGIN(TestImagePlugin) + +void tst_QImageIOHandler::hasCustomPluginFormat() +{ + QVERIFY(m_supportedReadFormats.contains("foo")); +} + +void tst_QImageIOHandler::pluginRead_data() +{ + QTest::addColumn("color"); + QTest::addColumn("suffix"); + QTest::addColumn("expectedPluginLog"); + + QTest::newRow("testplugin") << "green" << "foo" << QStringList({ "formatname-matched" }); + QTest::newRow("overridden-builtin") << "red" << "png" << QStringList({ "formatname-matched" }); + QTest::newRow("builtin") << "black" << "bmp" << QStringList({ "formatname-unmatched" }); //### Should be null + QTest::newRow("no-suffix") << "blue" << "" << QStringList({ "contents-matched" }); + QTest::newRow("wrong-suffix") << "yellow" << "jpg" << QStringList({ "contents-matched" }); + QTest::newRow("unknown-suffix") << "black" << "bar" << QStringList({ "formatname-unmatched", "contents-unmatched" }); + if (m_supportedReadFormats.contains("jpeg")) + QTest::newRow("wrong-suffix2") << "white" << "foo" << QStringList({ "formatname-matched" }); + if (m_supportedReadFormats.contains("gif")) + QTest::newRow("plugin-writeonly") << "cyan" << "gif" << QStringList(); +} + +void tst_QImageIOHandler::pluginRead() +{ + QFETCH(QString, color); + QFETCH(QString, suffix); + QFETCH(QStringList, expectedPluginLog); + + PluginLog::clear(); + QString filePath = m_prefix + color + (suffix.isEmpty() ? "" : "." + suffix); + QImage img(filePath); + QCOMPARE(PluginLog::size(), expectedPluginLog.size()); + for (int i = 0; i < PluginLog::size(); i++) + QCOMPARE(PluginLog::item(i), expectedPluginLog.value(i)); + QVERIFY(!img.isNull()); + QCOMPARE(img.pixelColor(0, 0), QColor(color)); +} + +void tst_QImageIOHandler::pluginNoAutoDetection_data() +{ + QTest::addColumn("color"); + QTest::addColumn("suffix"); + QTest::addColumn("expectedPluginLog"); + QTest::addColumn("expectedSuccess"); + + QTest::newRow("testplugin") << "green" << "foo" << QStringList({ "formatname-matched" }) << true; + QTest::newRow("overridden-builtin") << "red" << "png" << QStringList({ "formatname-matched" }) << true; + QTest::newRow("builtin") << "black" << "bmp" << QStringList() << true; + QTest::newRow("no-suffix") << "blue" << "" << QStringList() << false; + QTest::newRow("wrong-suffix") << "yellow" << "jpg" << QStringList() << false; + QTest::newRow("unknown-suffix") << "black" << "bar" << QStringList() << false; + QTest::newRow("wrong-suffix2") << "white" << "foo" << QStringList({ "formatname-matched" }) << false; + QTest::newRow("plugin-writeonly") << "cyan" << "gif" << QStringList() << true; +} + +void tst_QImageIOHandler::pluginNoAutoDetection() +{ + QFETCH(QString, color); + QFETCH(QString, suffix); + QFETCH(QStringList, expectedPluginLog); + QFETCH(bool, expectedSuccess); + + QString filePath = m_prefix + color + (suffix.isEmpty() ? "" : "." + suffix); + { + // Confirm that the file suffix is ignored, i.e. nothing happens if no format is set + PluginLog::clear(); + QImageReader r(filePath); + r.setAutoDetectImageFormat(false); + QImage img = r.read(); + QVERIFY(img.isNull()); + QCOMPARE(PluginLog::size(), 0); + } + + PluginLog::clear(); + QImageReader r(filePath, suffix.toLatin1()); + r.setAutoDetectImageFormat(false); + QImage img = r.read(); + + QCOMPARE(PluginLog::size(), expectedPluginLog.size()); + for (int i = 0; i < PluginLog::size(); i++) + QCOMPARE(PluginLog::item(i), expectedPluginLog.value(i)); + QCOMPARE(!img.isNull(), expectedSuccess); + if (expectedSuccess) + QCOMPARE(img.pixelColor(0, 0), QColor(color)); +} + +void tst_QImageIOHandler::pluginDecideFromContent_data() +{ + QTest::addColumn("color"); + QTest::addColumn("suffix"); + QTest::addColumn("expectedPluginLog"); + + QTest::newRow("testplugin") << "green" << "foo" << QStringList({ "contents-matched" }); + QTest::newRow("overridden-builtin") << "red" << "png" << QStringList({ "contents-matched" }); + QTest::newRow("builtin") << "black" << "bmp" << QStringList({ "contents-unmatched" }); + QTest::newRow("no-suffix") << "blue" << "" << QStringList({ "contents-matched" }); + QTest::newRow("wrong-suffix") << "yellow" << "jpg" << QStringList({ "contents-matched" }); + QTest::newRow("unknown-suffix") << "black" << "bar" << QStringList({ "contents-unmatched" }); + if (m_supportedReadFormats.contains("jpeg")) + QTest::newRow("wrong-suffix2") << "white" << "foo" << QStringList(); +} + +void tst_QImageIOHandler::pluginDecideFromContent() +{ + QFETCH(QString, color); + QFETCH(QString, suffix); + QFETCH(QStringList, expectedPluginLog); + + QString filePath = m_prefix + color + (suffix.isEmpty() ? "" : "." + suffix); + PluginLog::clear(); + QImageReader r(filePath, suffix.toLatin1()); + r.setDecideFormatFromContent(true); + QImage img = r.read(); + + QCOMPARE(PluginLog::size(), expectedPluginLog.size()); + for (int i = 0; i < PluginLog::size(); i++) + QCOMPARE(PluginLog::item(i), expectedPluginLog.value(i)); + QVERIFY(!img.isNull()); + QCOMPARE(img.pixelColor(0, 0), QColor(color)); +} + +void tst_QImageIOHandler::pluginDetectedFormat_data() +{ + QTest::addColumn("color"); + QTest::addColumn("suffix"); + QTest::addColumn("fileFormat"); + + QTest::newRow("testplugin") << "green" << "foo" << "foo"; + QTest::newRow("overridden-builtin") << "red" << "png" << "png"; + QTest::newRow("builtin") << "black" << "bmp" << "bmp"; + QTest::newRow("no-suffix") << "blue" << "" << "png"; + QTest::newRow("wrong-suffix") << "yellow" << "jpg" << "foo"; + QTest::newRow("unknown-suffix") << "black" << "bar" << "bmp"; + if (m_supportedReadFormats.contains("jpeg")) + QTest::newRow("wrong-suffix2") << "white" << "foo" << "jpeg"; + if (m_supportedReadFormats.contains("gif")) + QTest::newRow("plugin-writeonly") << "cyan" << "gif" << "gif"; +} + +void tst_QImageIOHandler::pluginDetectedFormat() +{ + QFETCH(QString, color); + QFETCH(QString, suffix); + QFETCH(QString, fileFormat); + + QString filePath = m_prefix + color + (suffix.isEmpty() ? "" : "." + suffix); + QCOMPARE(QImageReader::imageFormat(filePath), fileFormat.toLatin1()); +} + +void tst_QImageIOHandler::pluginWrongFormat_data() +{ + QTest::addColumn("color"); + QTest::addColumn("suffix"); + QTest::addColumn("forceFormat"); + + QTest::newRow("testplugin") << "green" << "foo" << "bmp"; + QTest::newRow("overridden-builtin") << "red" << "png" << "bmp"; + QTest::newRow("builtin") << "black" << "bmp" << "foo"; + QTest::newRow("no-suffix") << "blue" << "" << "bmp"; + QTest::newRow("wrong-suffix") << "yellow" << "jpg" << "bmp"; + QTest::newRow("unknown-suffix") << "black" << "bar" << "foo"; + QTest::newRow("wrong-suffix2") << "white" << "foo" << "bmp"; +} + +void tst_QImageIOHandler::pluginWrongFormat() +{ + QFETCH(QString, color); + QFETCH(QString, suffix); + QFETCH(QString, forceFormat); + + PluginLog::clear(); + QString filePath = m_prefix + color + (suffix.isEmpty() ? "" : "." + suffix); + QImage img(filePath, forceFormat.toLatin1()); + QVERIFY(img.isNull()); +} + QTEST_MAIN(tst_QImageIOHandler) + #include "tst_qimageiohandler.moc"