diff --git a/tests/auto/gui/text/CMakeLists.txt b/tests/auto/gui/text/CMakeLists.txt index 30b35fb10a3..3283ae00970 100644 --- a/tests/auto/gui/text/CMakeLists.txt +++ b/tests/auto/gui/text/CMakeLists.txt @@ -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) diff --git a/tests/auto/gui/text/qfontengine/CMakeLists.txt b/tests/auto/gui/text/qfontengine/CMakeLists.txt new file mode 100644 index 00000000000..a68bc1148b8 --- /dev/null +++ b/tests/auto/gui/text/qfontengine/CMakeLists.txt @@ -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 +) diff --git a/tests/auto/gui/text/qfontengine/tst_qfontengine.cpp b/tests/auto/gui/text/qfontengine/tst_qfontengine.cpp new file mode 100644 index 00000000000..33b285f01dc --- /dev/null +++ b/tests/auto/gui/text/qfontengine/tst_qfontengine.cpp @@ -0,0 +1,257 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include + +#include +#include +#include +#include +#include + +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 theArguments; + std::unique_ptr 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("platform"); + QTest::addColumn("engine"); + QTest::addColumn("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 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(argc, const_cast(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"); + + const std::initializer_list 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) + diff --git a/tests/benchmarks/gui/text/CMakeLists.txt b/tests/benchmarks/gui/text/CMakeLists.txt index 052d49e03c2..05e4b873079 100644 --- a/tests/benchmarks/gui/text/CMakeLists.txt +++ b/tests/benchmarks/gui/text/CMakeLists.txt @@ -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) diff --git a/tests/benchmarks/gui/text/qfontengine/CMakeLists.txt b/tests/benchmarks/gui/text/qfontengine/CMakeLists.txt new file mode 100644 index 00000000000..ddd3797a661 --- /dev/null +++ b/tests/benchmarks/gui/text/qfontengine/CMakeLists.txt @@ -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 +) diff --git a/tests/benchmarks/gui/text/qfontengine/tst_qfontengine.cpp b/tests/benchmarks/gui/text/qfontengine/tst_qfontengine.cpp new file mode 100644 index 00000000000..15236d4c596 --- /dev/null +++ b/tests/benchmarks/gui/text/qfontengine/tst_qfontengine.cpp @@ -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); + } +}