QFontEngine: add test coverage and benchmark for all engines
For some known fonts, confirm that we get the right glyph count and glyph index for specific unicode code points. Create QGuiApplication with a specific font engine, and test the fonts with all engines. On Windows, that's DirectWrite, GDI, and Freetype; on macOS CoreText and Freetype; and otherwise only Freetype. Not all fonts will be available with all engines, so test in each test function whether the font is a good enough match (family is enough, no need to do a deep test). Add a benchmark as well, using the same setup plumbing, but with different test functions. Change-Id: I2ed279965fc3f1dc3f283d0fe7b018fc3035c67d Reviewed-by: Anton Kudryavtsev <antkudr@mail.ru> Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
parent
51b584e606
commit
4db368d474
@ -4,6 +4,7 @@
|
||||
add_subdirectory(qabstracttextdocumentlayout)
|
||||
add_subdirectory(qfont)
|
||||
add_subdirectory(qfontdatabase)
|
||||
add_subdirectory(qfontengine)
|
||||
add_subdirectory(qfontmetrics)
|
||||
add_subdirectory(qglyphrun)
|
||||
add_subdirectory(qrawfont)
|
||||
|
40
tests/auto/gui/text/qfontengine/CMakeLists.txt
Normal file
40
tests/auto/gui/text/qfontengine/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
# Copyright (C) 2024 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qfontengine Test:
|
||||
#####################################################################
|
||||
|
||||
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(tst_qfontengine LANGUAGES CXX)
|
||||
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||
endif()
|
||||
|
||||
# Resources:
|
||||
set_source_files_properties("../../../shared/resources/test.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "test.ttf"
|
||||
)
|
||||
set_source_files_properties("../../../shared/resources/testfont.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont.ttf"
|
||||
)
|
||||
set_source_files_properties("../../../shared/resources/testfont_variable.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont_variable.ttf"
|
||||
)
|
||||
set(testfonts_resource_files
|
||||
"../../../shared/resources/test.ttf"
|
||||
"../../../shared/resources/testfont.ttf"
|
||||
"../../../shared/resources/testfont_variable.ttf"
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qfontengine
|
||||
SOURCES
|
||||
tst_qfontengine.cpp
|
||||
TESTDATA
|
||||
${testfonts_resource_files}
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
BUILTIN_TESTDATA
|
||||
)
|
257
tests/auto/gui/text/qfontengine/tst_qfontengine.cpp
Normal file
257
tests/auto/gui/text/qfontengine/tst_qfontengine.cpp
Normal file
@ -0,0 +1,257 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QtGui/qfont.h>
|
||||
#include <QtGui/qfontdatabase.h>
|
||||
#include <QtGui/qfontinfo.h>
|
||||
#include <QtGui/private/qfont_p.h>
|
||||
#include <QtGui/private/qfontengine_p.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class tst_QFontEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
struct Candidate {
|
||||
QLatin1StringView rowName;
|
||||
int expectedGlyphCount;
|
||||
int ucs4;
|
||||
glyph_t expectedGlyphIndex;
|
||||
|
||||
QString familyName() const
|
||||
{
|
||||
const auto split = rowName.indexOf('@');
|
||||
Q_ASSERT(split >= 0);
|
||||
return QString::fromUtf8(rowName.mid(split + 1));
|
||||
}
|
||||
|
||||
QFont font() const
|
||||
{
|
||||
QFont font(familyName());
|
||||
font.setStyleStrategy(QFont::NoFontMerging);
|
||||
return font;
|
||||
}
|
||||
|
||||
bool isFontAvailable() const
|
||||
{
|
||||
QFont theFont(font());
|
||||
return theFont.family() == QFontInfo(theFont).family();
|
||||
}
|
||||
|
||||
QFontEngine *fontEngine() const
|
||||
{
|
||||
return QFontPrivate::get(font())->engineForScript(QChar::Script_Common);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
tst_QFontEngine() = default;
|
||||
|
||||
private slots:
|
||||
void initTestCase_data();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void glyphCount_data() { data(); }
|
||||
void glyphCount();
|
||||
void glyphIndex_data() { data(); }
|
||||
void glyphIndex();
|
||||
|
||||
private:
|
||||
void setupApplication();
|
||||
void data();
|
||||
|
||||
// member variables to keep the arguments data alive
|
||||
QByteArray platformArgument;
|
||||
QList<const char *> theArguments;
|
||||
std::unique_ptr<QGuiApplication> theApp;
|
||||
|
||||
int QtsSpecialTestFont = -1;
|
||||
int QtBidiTestFont = -1;
|
||||
int QtTestVariableFont = -1;
|
||||
};
|
||||
|
||||
// The tst_bench_QFontEngine benchmark project shares the test class declaration
|
||||
// and the common setup code here. The file is both compiled as part of the
|
||||
// project (moc needs to see it), and included (so that the benchmark code sees
|
||||
// the class declaration).
|
||||
#if !defined(QFONTENGINE_BENCHMARK) || defined(QFONTENGINE_COMMON)
|
||||
|
||||
void tst_QFontEngine::initTestCase_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("platform");
|
||||
QTest::addColumn<QByteArray>("engine");
|
||||
QTest::addColumn<QFontEngine::Type>("engineType");
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
QTest::addRow("DirectWrite") << ""_ba << ""_ba << QFontEngine::DirectWrite;
|
||||
QTest::addRow("GDI") << "windows"_ba << "gdi"_ba << QFontEngine::Win;
|
||||
QTest::addRow("Freetype") << "windows"_ba << "freetype"_ba << QFontEngine::Freetype;
|
||||
#elif defined(Q_OS_DARWIN)
|
||||
QTest::addRow("CoreText") << ""_ba << ""_ba << QFontEngine::Mac;
|
||||
QTest::addRow("Freetype") << "cocoa"_ba << "freetype"_ba << QFontEngine::Freetype;
|
||||
#else
|
||||
QTest::addRow("freetype") << ""_ba << ""_ba << QFontEngine::Freetype;
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QFontEngine::init()
|
||||
{
|
||||
setupApplication();
|
||||
|
||||
#if !defined(QFONTENGINE_BENCHMARK)
|
||||
QtsSpecialTestFont = QFontDatabase::addApplicationFont(QFINDTESTDATA("test.ttf"));
|
||||
QVERIFY(QtsSpecialTestFont >= 0);
|
||||
QCOMPARE(QFontDatabase::applicationFontFamilies(QtsSpecialTestFont),
|
||||
QStringList{u"QtsSpecialTestFont"_s});
|
||||
QtBidiTestFont = QFontDatabase::addApplicationFont(QFINDTESTDATA("testfont.ttf"));
|
||||
QVERIFY(QtBidiTestFont >= 0);
|
||||
QCOMPARE(QFontDatabase::applicationFontFamilies(QtBidiTestFont),
|
||||
QStringList{u"QtBidiTestFont"_s});
|
||||
// This font comes with two font faces, so we get multiple entries (which
|
||||
// might be a bug, esp since on macOS we get two, on Windows we get three
|
||||
// identical families).
|
||||
QtTestVariableFont = QFontDatabase::addApplicationFont(QFINDTESTDATA("testfont_variable.ttf"));
|
||||
QVERIFY(QtTestVariableFont >= 0);
|
||||
QVERIFY(QFontDatabase::applicationFontFamilies(QtTestVariableFont)
|
||||
.contains(u"QtTestVariableFont"_s));
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QFontEngine::cleanup()
|
||||
{
|
||||
#if !defined(QFONTENGINE_BENCHMARK)
|
||||
QFontDatabase::removeApplicationFont(QtTestVariableFont);
|
||||
QFontDatabase::removeApplicationFont(QtBidiTestFont);
|
||||
QFontDatabase::removeApplicationFont(QtsSpecialTestFont);
|
||||
#endif
|
||||
|
||||
theApp.reset();
|
||||
theArguments = {};
|
||||
}
|
||||
|
||||
void tst_QFontEngine::setupApplication()
|
||||
{
|
||||
if (theApp)
|
||||
return;
|
||||
QFETCH_GLOBAL(const QByteArray, platform);
|
||||
QFETCH_GLOBAL(const QByteArray, engine);
|
||||
|
||||
QList<const char *> arguments = {"tst_qfontengine"};
|
||||
if (!platform.isEmpty()) {
|
||||
arguments += "-platform";
|
||||
platformArgument = platform + ":fontengine=" + engine;
|
||||
arguments += platformArgument.data();
|
||||
}
|
||||
|
||||
theArguments = arguments;
|
||||
int argc = arguments.size();
|
||||
theApp = std::make_unique<QGuiApplication>(argc, const_cast<char **>(theArguments.data()));
|
||||
}
|
||||
|
||||
// only called once per test function, even if the global data (i.e. font engine) changed!
|
||||
void tst_QFontEngine::data()
|
||||
{
|
||||
QTest::addColumn<Candidate>("candidate");
|
||||
|
||||
const std::initializer_list<Candidate> candidates = {
|
||||
#if !defined(QFONTENGINE_BENCHMARK)
|
||||
// our own testfonts
|
||||
{
|
||||
".notdef@QtsSpecialTestFont"_L1,
|
||||
6, 0x0000, 0,
|
||||
},
|
||||
{
|
||||
"Dotty@QtsSpecialTestFont"_L1,
|
||||
6, 0xe000, 1,
|
||||
},
|
||||
{
|
||||
"A@QtsSpecialTestFont"_L1,
|
||||
6, 0x0041, 2,
|
||||
},
|
||||
{
|
||||
"one@QtsSpecialTestFont"_L1,
|
||||
6, 0x0031, 3,
|
||||
},
|
||||
{
|
||||
"uni200D@QtsSpecialTestFont"_L1,
|
||||
6, 0x200d, 4,
|
||||
},
|
||||
{
|
||||
"uniFFFC@QtsSpecialTestFont"_L1,
|
||||
6, 0xfffc, 5,
|
||||
},
|
||||
{
|
||||
"percent@QtBidiTestFont"_L1,
|
||||
150, 0x0025, 13,
|
||||
},
|
||||
{ // up arrow, beyond 0xFFFF
|
||||
"u1034A@QtBidiTestFont"_L1,
|
||||
150, 0x1034A, 149,
|
||||
},
|
||||
{
|
||||
"peseta@QtTestVariableFont"_L1,
|
||||
235, 0x20a7, 218,
|
||||
},
|
||||
#else // some typical icon fonts that might be present on the system, but also change frequently
|
||||
{ // early hit
|
||||
"faucet@Font Awesome 6 Free"_L1,
|
||||
1399, 0xe005, 51,
|
||||
},
|
||||
{ // last glyph in the font
|
||||
"caravan@Font Awesome 6 Free"_L1,
|
||||
1399, 0xf8ff, 1398,
|
||||
},
|
||||
{
|
||||
"delete@Material Symbols Outlined"_L1,
|
||||
5032, 0xe872, 1,
|
||||
},
|
||||
{ // the Segoe Fluent Icons font that comes with Windows 11
|
||||
"gid1486@Segoe Fluent Icons"_L1, // voicemail
|
||||
1529, 0xf47f, 1486,
|
||||
},
|
||||
#endif // !defined(QFONTENGINE_BENCHMARK)
|
||||
};
|
||||
|
||||
for (auto candidate : candidates)
|
||||
QTest::addRow("%s", candidate.rowName.constData()) << candidate;
|
||||
}
|
||||
|
||||
#include "tst_qfontengine.moc"
|
||||
QTEST_APPLESS_MAIN(tst_QFontEngine)
|
||||
|
||||
#endif // !defined(QFONTENGINE_BENCHMARK) || defined(QFONTENGINE_COMMON)
|
||||
|
||||
// The benchmark project implements the test functions differently.
|
||||
|
||||
#if !defined(QFONTENGINE_BENCHMARK)
|
||||
|
||||
void tst_QFontEngine::glyphCount()
|
||||
{
|
||||
QFETCH_GLOBAL(const QFontEngine::Type, engineType);
|
||||
QFETCH(const Candidate, candidate);
|
||||
if (!candidate.isFontAvailable())
|
||||
QSKIP("Font is not available");
|
||||
|
||||
const auto fontEngine = candidate.fontEngine();
|
||||
QCOMPARE(fontEngine->type(), engineType);
|
||||
QCOMPARE(fontEngine->glyphCount(), candidate.expectedGlyphCount);
|
||||
}
|
||||
|
||||
void tst_QFontEngine::glyphIndex()
|
||||
{
|
||||
QFETCH_GLOBAL(const QFontEngine::Type, engineType);
|
||||
QFETCH(const Candidate, candidate);
|
||||
if (!candidate.isFontAvailable())
|
||||
QSKIP("Font is not available");
|
||||
|
||||
const auto fontEngine = candidate.fontEngine();
|
||||
QCOMPARE(fontEngine->type(), engineType);
|
||||
QCOMPARE(fontEngine->glyphIndex(candidate.ucs4), candidate.expectedGlyphIndex);
|
||||
}
|
||||
|
||||
#endif // !define(QFONTENGINE_BENCHMARK)
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
add_subdirectory(qfontengine)
|
||||
add_subdirectory(qfontmetrics)
|
||||
add_subdirectory(qtext)
|
||||
add_subdirectory(qtextdocument)
|
||||
|
18
tests/benchmarks/gui/text/qfontengine/CMakeLists.txt
Normal file
18
tests/benchmarks/gui/text/qfontengine/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (C) 2024 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_bench_QFontEngine Binary:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_benchmark(tst_bench_QFontEngine
|
||||
SOURCES
|
||||
../../../../auto/gui/text/qfontengine/tst_qfontengine.cpp
|
||||
tst_qfontengine.cpp
|
||||
DEFINES
|
||||
QFONTENGINE_BENCHMARK
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::Test
|
||||
)
|
33
tests/benchmarks/gui/text/qfontengine/tst_qfontengine.cpp
Normal file
33
tests/benchmarks/gui/text/qfontengine/tst_qfontengine.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2024 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#define QFONTENGINE_COMMON
|
||||
#include "../../../../auto/gui/text/qfontengine/tst_qfontengine.cpp"
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
void tst_QFontEngine::glyphCount()
|
||||
{
|
||||
QFETCH(const Candidate, candidate);
|
||||
if (!candidate.isFontAvailable())
|
||||
QSKIP("Font is not available");
|
||||
|
||||
const auto fontEngine = candidate.fontEngine();
|
||||
|
||||
QBENCHMARK {
|
||||
fontEngine->glyphCount();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFontEngine::glyphIndex()
|
||||
{
|
||||
QFETCH(const Candidate, candidate);
|
||||
if (!candidate.isFontAvailable())
|
||||
QSKIP("Font is not available");
|
||||
|
||||
const auto fontEngine = candidate.fontEngine();
|
||||
|
||||
QBENCHMARK {
|
||||
fontEngine->glyphIndex(candidate.ucs4);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user