Add autotest for QImageIO format handler plugins

To confirm the functionality of custom imageformats plugins in Qt
image reading, both for supporting new formats and for overriding Qt's
builtin formats.

Change-Id: I6abafdb47301ad1f35b19951e42b1f3b61fe6ec3
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
(cherry picked from commit 6eefe0ca59dc25f7953b93e8b80c62c78a6ccfde)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Eirik Aavitsland 2024-10-04 12:43:17 +02:00 committed by Qt Cherry-pick Bot
parent df743c2164
commit 5de95d3aba
14 changed files with 352 additions and 0 deletions

View File

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -0,0 +1 @@
BLUE

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 B

View File

@ -0,0 +1 @@
green

View File

@ -0,0 +1 @@
RED

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

View File

@ -0,0 +1 @@
yellow

View File

@ -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)

View File

@ -0,0 +1,89 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qimageiohandler.h>
#include <qcoreapplication.h>
#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"

View File

@ -0,0 +1,4 @@
{
"Keys": [ "png", "foo", "gif" ],
"MimeTypes": [ "image/png", "image/foo", "image/gif" ]
}

View File

@ -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 <QCoreApplication>
#include <QVariant>
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

View File

@ -6,8 +6,11 @@
#include <qcoreapplication.h>
#include <qdebug.h>
#include <qhash.h>
#include <qimageiohandler.h>
#include <qfile.h>
#include <qimagereader.h>
#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<QByteArray> 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<QString>("color");
QTest::addColumn<QString>("suffix");
QTest::addColumn<QStringList>("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<QString>("color");
QTest::addColumn<QString>("suffix");
QTest::addColumn<QStringList>("expectedPluginLog");
QTest::addColumn<bool>("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<QString>("color");
QTest::addColumn<QString>("suffix");
QTest::addColumn<QStringList>("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<QString>("color");
QTest::addColumn<QString>("suffix");
QTest::addColumn<QString>("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<QString>("color");
QTest::addColumn<QString>("suffix");
QTest::addColumn<QString>("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"