diff options
Diffstat (limited to 'tests/baseline')
34 files changed, 2044 insertions, 531 deletions
diff --git a/tests/baseline/CMakeLists.txt b/tests/baseline/CMakeLists.txt index df5d35863b..037ffd9e0e 100644 --- a/tests/baseline/CMakeLists.txt +++ b/tests/baseline/CMakeLists.txt @@ -1,4 +1,7 @@ -if(TARGET Qt::Network) +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(TARGET Qt::Gui AND TARGET Qt::Network AND QT_FEATURE_pdf) add_subdirectory(painting) endif() if(TARGET Qt::Network AND TARGET Qt::Widgets) diff --git a/tests/baseline/painting/CMakeLists.txt b/tests/baseline/painting/CMakeLists.txt index b32d8f1324..72e737d227 100644 --- a/tests/baseline/painting/CMakeLists.txt +++ b/tests/baseline/painting/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + ##################################################################### ## tst_baseline_painting Test: ##################################################################### @@ -14,9 +17,11 @@ qt_internal_add_test(tst_baseline_painting ../shared/qbaselinetest.cpp ../shared/qbaselinetest.h ../shared/paintcommands.cpp ../shared/paintcommands.h tst_baseline_painting.cpp + NO_PCH_SOURCES + tst_baseline_painting.cpp # undef QT_NO_FOREACH INCLUDE_DIRECTORIES ../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::Network @@ -68,6 +73,6 @@ qt_internal_add_resource(tst_baseline_painting "images" ##################################################################### qt_internal_extend_target(tst_baseline_painting CONDITION QT_FEATURE_opengl - PUBLIC_LIBRARIES + LIBRARIES Qt::OpenGL ) diff --git a/tests/baseline/painting/scripts/aliasing.qps b/tests/baseline/painting/scripts/aliasing.qps index 59878f9c4d..1fb0113396 100644 --- a/tests/baseline/painting/scripts/aliasing.qps +++ b/tests/baseline/painting/scripts/aliasing.qps @@ -19,6 +19,17 @@ begin_block drawing setPen black drawText 0 68 "QwErTy@" + setPen green 1 SolidLine + drawLine 0 75 10 75 + setPen 800000ff 1 + drawPoint 0 75 + drawPoint 10 75 + + setPen green 2 SolidLine + drawLine 20 75 30 75 + setPen 800000ff 2 + drawPoint 20 75 + drawPoint 30 75 setPen black 1 setBrush 7f7fff @@ -153,4 +164,4 @@ drawText 15 185 "1.0" resetMatrix drawText 430 95 "Aliased" -drawText 430 275 "Anti-Aliased"
\ No newline at end of file +drawText 430 275 "Anti-Aliased" diff --git a/tests/baseline/painting/scripts/cliprects.qps b/tests/baseline/painting/scripts/cliprects.qps index aa0367eccf..cbc2a90055 100644 --- a/tests/baseline/painting/scripts/cliprects.qps +++ b/tests/baseline/painting/scripts/cliprects.qps @@ -1,7 +1,7 @@ # Version: 1 # CheckVsReference: 5% - +save translate 10 10 setPen NoPen @@ -56,4 +56,297 @@ setRenderHint Antialiasing setRenderHint SmoothPixmapTransform repeat_block clipping +restore + +# Excercise combining different clips + +translate 0 250 +scale 0.9 0.9 +setFont "times" 10 bold +region_addRect dummyRegion 1000 1000 10 10 +region_addRect realRegion 20 10 60 30 +path_addRect dummyPath 1000 1000 10 10 +path_addRect realPath 20 10 60 30 +begin_block paintstuff +fillRect 0 0 100 50 orange +drawText 0 5 "Should be clipped" +drawText 0 15 "Should be clipped" +drawText 0 25 "Should be clipped" +drawText 0 35 "Should be clipped" +drawText 0 45 "Should be clipped" +drawText 0 55 "Should be clipped" +end_block + +translate 0 100 + +# rect replaced by x +save +setClipRect 1000 1000 10 10 ReplaceClip +setClipRect 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRect 1000 1000 10 10 ReplaceClip +setClipRectF 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRect 1000 1000 10 10 ReplaceClip +setClipRegion realRegion ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRect 1000 1000 10 10 ReplaceClip +setClipPath realPath ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +# rectF replaced by x +save +setClipRectF 1000 1000 10 10 ReplaceClip +setClipRect 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRectF 1000 1000 10 10 ReplaceClip +setClipRectF 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRectF 1000 1000 10 10 ReplaceClip +setClipRegion realRegion ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRectF 1000 1000 10 10 ReplaceClip +setClipPath realPath ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +translate -800 100 + +# region replaced by x +save +setClipRegion dummyRegion ReplaceClip +setClipRect 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRegion dummyRegion ReplaceClip +setClipRectF 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRegion dummyRegion ReplaceClip +setClipRegion realRegion ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRegion dummyRegion ReplaceClip +setClipPath realPath ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +# path replaced by x +save +setClipPath dummyPath ReplaceClip +setClipRect 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipPath dummyPath ReplaceClip +setClipRectF 20 10 60 30 ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +save +setClipPath dummyPath ReplaceClip +setClipRegion realRegion ReplaceClip +repeat_block paintstuff +restore +translate 100 0 +save +setClipPath dummyPath ReplaceClip +setClipPath realPath ReplaceClip +repeat_block paintstuff +restore +translate 100 0 + +region_addRect intregion 0 10 60 30 +path_addRect intpath 0 10 60 30 + +translate -800 100 +# rect & x +save +setClipRect 0 10 60 30 ReplaceClip +translate 40 0 +setClipRect 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRect 0 10 60 30 ReplaceClip +translate 40 0 +setClipRectF 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRect 0 10 60 30 ReplaceClip +translate 40 0 +setClipRegion intregion IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRect 0 10 60 30 ReplaceClip +translate 40 0 +setClipPath intpath IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +# rectF & x +save +setClipRectF 0 10 60 30 ReplaceClip +translate 40 0 +setClipRect 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRectF 0 10 60 30 ReplaceClip +translate 40 0 +setClipRectF 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRectF 0 10 60 30 ReplaceClip +translate 40 0 +setClipRegion intregion IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRectF 0 10 60 30 ReplaceClip +translate 40 0 +setClipPath intpath IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +translate -800 100 + +# region & x +save +setClipRegion intregion ReplaceClip +translate 40 0 +setClipRect 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRegion intregion ReplaceClip +translate 40 0 +setClipRectF 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRegion intregion ReplaceClip +translate 40 0 +setClipRegion intregion IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipRegion intregion ReplaceClip +translate 40 0 +setClipPath intpath IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +# path & x +save +setClipPath intpath ReplaceClip +translate 40 0 +setClipRect 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipPath intpath ReplaceClip +translate 40 0 +setClipRectF 0 10 60 30 IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipPath intpath ReplaceClip +translate 40 0 +setClipRegion intregion IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 + +save +setClipPath intpath ReplaceClip +translate 40 0 +setClipPath intpath IntersectClip +translate -40 0 +repeat_block paintstuff +restore +translate 100 0 diff --git a/tests/baseline/painting/scripts/pattern_xform.qps b/tests/baseline/painting/scripts/pattern_xform.qps new file mode 100644 index 0000000000..224969f1c7 --- /dev/null +++ b/tests/baseline/painting/scripts/pattern_xform.qps @@ -0,0 +1,79 @@ +# Version: 1 +# CheckVsReference: 5% + +#define basic block off screen +save +translate -1000 -1000 +begin_block drawrects +setBrush green Dense4Pattern +drawRect 0 0 40 40 +setBrush green DiagCrossPattern +drawRect 40 0 40 40 +setBrush green HorPattern +brushRotate 30 +drawRect 80 0 40 40 +fillRect 120 0 40 40 +save +setPen brush 40 SolidLine FlatCap +setBrush NoBrush +drawLine 160 20 200 20 +restore +end_block +restore + +begin_block hintsuite +save +setRenderHint NonCosmeticBrushPatterns false +setRenderHint SmoothPixmapTransform false +translate 10 10 +repeat_block drawrects + +setRenderHint NonCosmeticBrushPatterns false +setRenderHint SmoothPixmapTransform true +translate 0 50 +repeat_block drawrects + +setRenderHint NonCosmeticBrushPatterns true +setRenderHint SmoothPixmapTransform false +translate 0 50 +repeat_block drawrects + +setRenderHint NonCosmeticBrushPatterns true +setRenderHint SmoothPixmapTransform true +translate 0 50 +repeat_block drawrects +restore +end_block + +save +translate 0 200 +scale 2 2 +repeat_block hintsuite +restore + +save +translate 500 0 +scale 1.5 2.5 +rotate_y 60 +repeat_block hintsuite +restore + + +translate 0 650 +setBrush blue CrossPattern +setPen red +setRenderHint NonCosmeticBrushPatterns false + +begin_block dots +save +drawRect 0 0 50 50 +setBrushOrigin 12 0 +drawRect 50 0 50 50 +scale 2 1 +drawRect 50 0 50 50 +restore +end_block dots + +setRenderHint NonCosmeticBrushPatterns true +translate 0 60 +repeat_block dots diff --git a/tests/baseline/painting/scripts/pattern_xform2.qps b/tests/baseline/painting/scripts/pattern_xform2.qps new file mode 100644 index 0000000000..4f9314272d --- /dev/null +++ b/tests/baseline/painting/scripts/pattern_xform2.qps @@ -0,0 +1,81 @@ +# Version: 1 +# CheckVsReference: 5% + +# 1: Check brush origin vs (non)cosmetic brush patterns + +setBrush blue CrossPattern +begin_block blockName +save +setBrushOrigin 0 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 1 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 2 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 3 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 4 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 5 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 6 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 7 0 +fillRect 0 0 32 32 +translate 0 32 +setBrushOrigin 8 0 +fillRect 0 0 32 32 +restore +end_block blockName + +save +setBrush red CrossPattern +scale 2 1 +repeat_block blockName +restore + +save +translate 0 300 +setRenderHint NonCosmeticBrushPatterns true +setBrush blue CrossPattern +repeat_block blockName +setBrush red CrossPattern +scale 2 1 +repeat_block blockName +restore + +# 2: Check brush update after only xform or hint change +translate 100 0 + +save +setPen NoPen +setBrush blue DiagCrossPattern +setRenderHint NonCosmeticBrushPatterns true +drawRect 10 10 200 100 +scale 10 10 +drawRect 22 1 20 10 +drawRect 22 12 20 10 +setRenderHint NonCosmeticBrushPatterns false +drawRect 1 12 20 10 +restore + +setBrush green DiagCrossPattern +setPen brush 100 SolidLine FlatCap +pen_setCosmetic true +setBrush NoBrush +translate 0 250 +setRenderHint NonCosmeticBrushPatterns true +drawLine 10 60 210 60 +scale 10 10 +drawLine 22 6 42 6 +drawLine 22 17 42 17 +setRenderHint NonCosmeticBrushPatterns false +drawLine 1 17 21 17 + diff --git a/tests/baseline/painting/scripts/pixmapfragments.qps b/tests/baseline/painting/scripts/pixmapfragments.qps new file mode 100644 index 0000000000..4c837b760f --- /dev/null +++ b/tests/baseline/painting/scripts/pixmapfragments.qps @@ -0,0 +1,65 @@ +# Version: 1 +# CheckVsReference: 1% (0 0 690 580) + + +setRenderHint Antialiasing + +setPen #00ff00 + +pixmap_load dome_argb32.png the_pixmap +begin_block draw_stuff +save + drawPixmapFragments the_pixmap 1 50 50 25 25 60 60 1 1 0 1 + drawPixmapFragments the_pixmap 1 150 50 25 25 60 60 1 1 0 0.5 + drawPixmapFragments the_pixmap 1 250 50 25 25 60 60 1 1 30 1 + drawPixmapFragments the_pixmap 1 350 50 25 25 60 60 1.5 1 0 1 + drawPixmapFragments the_pixmap 1 450 50 25 25 60 60 1 1.5 0 1 + drawPixmapFragments the_pixmap 2 550 50 25 25 40 40 0.5 0.5 -45 1 600 50 25 25 40 40 0.7 0.7 45 1 +restore +end_block + + +translate 0 120 +pixmap_load dome_rgb32.png the_pixmap +repeat_block draw_stuff + +translate 0 120 +pixmap_load dome_indexed.png the_pixmap +repeat_block draw_stuff + +translate 0 120 +pixmap_load dome_indexed_mask.png the_pixmap +repeat_block draw_stuff + +translate 0 120 +pixmap_load dome_mono.png the_pixmap +repeat_block draw_stuff + + +resetMatrix +translate 700 60 +setPen black +drawText 0 0 "32 bit w/alpha" +translate 0 120 +drawText 0 0 "32 bit w/o alpha" +translate 0 120 +drawText 0 0 "8 bit indexed" +translate 0 120 +drawText 0 0 "8 bit indexed w/mask" +translate 0 120 +drawText 0 0 "1 bit" + +resetMatrix +translate 25 600 +drawText 0 0 "simple" +translate 100 0 +drawText 0 0 "opacity" +translate 100 0 +drawText 0 0 "rotation" +translate 100 0 +drawText 0 0 "scale x" +translate 100 0 +drawText 0 0 "scale y" +translate 100 0 +drawText 0 0 "two fragments" +translate 100 0 diff --git a/tests/baseline/painting/scripts/text.qps b/tests/baseline/painting/scripts/text.qps index 4d81b3084c..6bacdfd5e6 100644 --- a/tests/baseline/painting/scripts/text.qps +++ b/tests/baseline/painting/scripts/text.qps @@ -165,7 +165,7 @@ translate 0 75 save setPen black setFont "sansserif" 16 normal - drawText 0 40 "e😃m😇o😍j😜i😸!" + drawText 0 40 "e😃m😇o😍j😜i😸!✈️" restore translate 0 75 diff --git a/tests/baseline/painting/tst_baseline_painting.cpp b/tests/baseline/painting/tst_baseline_painting.cpp index a5d787f6ce..f486b33430 100644 --- a/tests/baseline/painting/tst_baseline_painting.cpp +++ b/tests/baseline/painting/tst_baseline_painting.cpp @@ -1,30 +1,7 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses #include "paintcommands.h" #include <qbaselinetest.h> @@ -66,7 +43,8 @@ private: }; void setupTestSuite(const QStringList& blacklist = QStringList()); - void runTestSuite(GraphicsEngine engine, QImage::Format format, const QSurfaceFormat &contextFormat = QSurfaceFormat()); + void runTestSuite(GraphicsEngine engine, QImage::Format format, + const QSurfaceFormat &contextFormat = QSurfaceFormat::defaultFormat()); void paint(QPaintDevice *device, GraphicsEngine engine, QImage::Format format, const QStringList &script, const QString &filePath); QStringList qpsFiles; @@ -76,7 +54,7 @@ private: private slots: void initTestCase(); - void cleanupTestCase() {} + void init(); void testRasterARGB32PM_data(); void testRasterARGB32PM(); @@ -112,6 +90,7 @@ private slots: void testCoreOpenGL_data(); void testCoreOpenGL(); private: + void initOpenGL(); bool checkSystemGLSupport(); bool checkSystemCoreGLSupport(); #endif @@ -143,13 +122,22 @@ void tst_Lancelot::initTestCase() std::sort(qpsFiles.begin(), qpsFiles.end()); foreach (const QString& fileName, qpsFiles) { QFile file(scriptsDir + fileName); - file.open(QFile::ReadOnly); + QVERIFY(file.open(QFile::ReadOnly)); QByteArray cont = file.readAll(); scripts.insert(fileName, QString::fromUtf8(cont).split(QLatin1Char('\n'), Qt::SkipEmptyParts)); scriptChecksums.insert(fileName, qChecksum(cont)); } + +#ifndef QT_NO_OPENGL + initOpenGL(); +#endif } +void tst_Lancelot::init() +{ + // This gets called for every row. QSKIP if current item is blacklisted on the baseline server: + QBASELINE_SKIP_IF_BLACKLISTED; +} void tst_Lancelot::testRasterARGB32PM_data() { @@ -292,6 +280,14 @@ void tst_Lancelot::testPdf() #ifndef QT_NO_OPENGL +void tst_Lancelot::initOpenGL() +{ + // Stencil buffer is needed for clipping + QSurfaceFormat glFormat; + glFormat.setStencilBufferSize(8); + QSurfaceFormat::setDefaultFormat(glFormat); +} + bool tst_Lancelot::checkSystemGLSupport() { QWindow win; @@ -315,7 +311,7 @@ bool tst_Lancelot::checkSystemCoreGLSupport() if (QOpenGLContext::openGLModuleType() != QOpenGLContext::LibGL) return false; - QSurfaceFormat coreFormat; + QSurfaceFormat coreFormat(QSurfaceFormat::defaultFormat()); coreFormat.setVersion(3, 2); coreFormat.setProfile(QSurfaceFormat::CoreProfile); QWindow win; @@ -370,7 +366,7 @@ void tst_Lancelot::testCoreOpenGL_data() void tst_Lancelot::testCoreOpenGL() { - QSurfaceFormat coreFormat; + QSurfaceFormat coreFormat(QSurfaceFormat::defaultFormat()); coreFormat.setVersion(3, 2); coreFormat.setProfile(QSurfaceFormat::CoreProfile); runTestSuite(OpenGL, QImage::Format_RGB32, coreFormat); @@ -427,19 +423,22 @@ void tst_Lancelot::runTestSuite(GraphicsEngine engine, QImage::Format format, co QString tempStem(QDir::tempPath() + QLatin1String("/lancelot_XXXXXX_") + qpsFile.chopped(4)); QTemporaryFile pdfFile(tempStem + QLatin1String(".pdf")); - pdfFile.open(); + QVERIFY(pdfFile.open()); QPdfWriter writer(&pdfFile); writer.setPdfVersion(QPdfWriter::PdfVersion_1_6); - writer.setResolution(150); + QPageSize pageSize(QSize(800, 800), QStringLiteral("LancePage"), QPageSize::ExactMatch); + writer.setPageSize(pageSize); + writer.setPageMargins(QMarginsF()); + writer.setResolution(72); paint(&writer, engine, format, script, QFileInfo(filePath).absoluteFilePath()); pdfFile.close(); // Convert pdf to something we can read into a QImage, using macOS' sips utility QTemporaryFile pngFile(tempStem + QLatin1String(".png")); - pngFile.open(); // Just create the file name + QVERIFY(pngFile.open()); // Just create the file name pngFile.close(); QProcess proc; - const char *rawArgs = "-s format png --cropOffset 20 20 -c 800 800 -o"; + const char *rawArgs = "-s format png -o"; QStringList argList = QString::fromLatin1(rawArgs).split(QLatin1Char(' ')); proc.start(QLatin1String("sips"), argList << pngFile.fileName() << pdfFile.fileName()); proc.waitForFinished(2 * 60 * 1000); // May need some time @@ -480,7 +479,8 @@ QTEST_MAIN(tst_Lancelot) int main(int argc, char *argv[]) { - qSetGlobalQHashSeed(0); // Avoid rendering variations caused by QHash randomization + // Avoid rendering variations caused by QHash randomization + QHashSeed::setDeterministicGlobalSeed(); QBaselineTest::handleCmdLineArgs(&argc, &argv); return _realmain(argc, argv); diff --git a/tests/baseline/shared/baselineprotocol.cpp b/tests/baseline/shared/baselineprotocol.cpp index ab6ced204a..6a38e71831 100644 --- a/tests/baseline/shared/baselineprotocol.cpp +++ b/tests/baseline/shared/baselineprotocol.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "baselineprotocol.h" #include <QLibraryInfo> #include <QImage> @@ -70,24 +45,26 @@ PlatformInfo PlatformInfo::localHostInfo() #endif pi.insert(PI_OSVersion, QSysInfo::kernelVersion()); + QString gc = qEnvironmentVariable("BASELINE_GIT_COMMIT"); #if QT_CONFIG(process) - QProcess git; - QString cmd; - QStringList args; -#if defined(Q_OS_WIN) - cmd = QLS("cmd.exe"); - args << QLS("/c") << QLS("git"); -#else - cmd = QLS("git"); -#endif - args << QLS("log") << QLS("--max-count=1") << QLS("--pretty=%H [%an] [%ad] %s"); - git.start(cmd, args); - git.waitForFinished(3000); - if (!git.exitCode()) - pi.insert(PI_GitCommit, QString::fromLocal8Bit(git.readAllStandardOutput().constData()).simplified()); - else - pi.insert(PI_GitCommit, QLS("Unknown")); + if (gc.isEmpty()) { + QProcess git; + QString cmd; + QStringList args; + #if defined(Q_OS_WIN) + cmd = QLS("cmd.exe"); + args << QLS("/c") << QLS("git"); + #else + cmd = QLS("git"); + #endif + args << QLS("log") << QLS("--max-count=1") << QLS("--pretty=%H [%an] [%ad] %s"); + git.start(cmd, args); + git.waitForFinished(3000); + if (!git.exitCode()) + gc = QString::fromLocal8Bit(git.readAllStandardOutput().constData()).simplified(); + } #endif // QT_CONFIG(process) + pi.insert(PI_GitCommit, gc.isEmpty() ? QLS("Unknown") : gc); if (qEnvironmentVariableIsSet("JENKINS_HOME")) pi.setAdHocRun(false); @@ -293,7 +270,7 @@ bool BaselineProtocol::connect(const QString &testCase, bool *dryrun, const Plat socket.connectToHost(serverName, ServerPort); if (!socket.waitForConnected(Timeout)) { - QThread::msleep(3000); // Wait a bit and try again, the server might just be restarting + QThread::sleep(std::chrono::seconds{3}); // Wait a bit and try again, the server might just be restarting if (!socket.waitForConnected(Timeout)) { errMsg += QLS("TCP connectToHost failed. Host:") + QLS(serverName) + QLS(" port:") + QString::number(ServerPort); return false; diff --git a/tests/baseline/shared/baselineprotocol.h b/tests/baseline/shared/baselineprotocol.h index a5cc6ba661..598a0cd3af 100644 --- a/tests/baseline/shared/baselineprotocol.h +++ b/tests/baseline/shared/baselineprotocol.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef BASELINEPROTOCOL_H #define BASELINEPROTOCOL_H diff --git a/tests/baseline/shared/lookup3.cpp b/tests/baseline/shared/lookup3.cpp index 1fbe6b499e..7964a184ae 100644 --- a/tests/baseline/shared/lookup3.cpp +++ b/tests/baseline/shared/lookup3.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only /* @@ -328,7 +303,7 @@ quint32 hashlittle( const void *key, size_t length, quint32 initval) * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). + * noticeably faster for short strings (like English words). */ #ifndef VALGRIND @@ -536,7 +511,7 @@ void hashlittle2( * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). + * noticeably faster for short strings (like English words). */ #ifndef VALGRIND @@ -736,7 +711,7 @@ quint32 hashbig( const void *key, size_t length, quint32 initval) * rest of the string. Every machine with memory protection I've seen * does it on word boundaries, so is OK with this. But VALGRIND will * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). + * noticeably faster for short strings (like English words). */ #ifndef VALGRIND diff --git a/tests/baseline/shared/paintcommands.cpp b/tests/baseline/shared/paintcommands.cpp index d2e48688f9..20201b66b0 100644 --- a/tests/baseline/shared/paintcommands.cpp +++ b/tests/baseline/shared/paintcommands.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "paintcommands.h" #include <qdir.h> @@ -198,6 +173,13 @@ const char *PaintCommands::imageFormatTable[] = { "RGBx32FPx4", "RGBA32FPx4", "RGBA32FPx4_Premultiplied", + "CMYK32", +}; + +const char *PaintCommands::renderHintTable[] = { + "Antialiasing", + "SmoothPixmapTransform", + "NonCosmeticBrushPatterns" }; int PaintCommands::translateEnum(const char *table[], const QString &pattern, int limit) @@ -338,7 +320,7 @@ void PaintCommands::staticInit() "pen_setCosmetic true"); DECL_PAINTCOMMAND("setRenderHint", command_setRenderHint, "^setRenderHint\\s+([\\w_0-9]*)\\s*(\\w*)$", - "setRenderHint <Antialiasing|SmoothPixmapTransform> <true|false>", + "setRenderHint <hint> <true|false>", "setRenderHint Antialiasing true"); DECL_PAINTCOMMAND("clearRenderHint", command_clearRenderHint, "^clearRenderHint$", @@ -488,6 +470,20 @@ void PaintCommands::staticInit() "^fillRectF\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s*(\\w*)?$", "fillRectF <x> <y> <w> <h> [color]\n - Uses current brush if no color given", "fillRectF 10.5 10.5 20.2 20.2 blue"); + DECL_PAINTCOMMAND("drawPixmapFragments", command_drawPixmapFragments, + "^drawPixmapFragments\\s+([\\w.:\\/]*)" + "\\s+(-?\\w*)" + "\\s+(-?[.\\w]*)\\s*(-?[.\\w]*)" + "\\s+(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)" + "\\s+(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)" + "\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?" + "\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?" + "\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)\\s*(-?[.\\w]*)?\\s*(-?[.\\w]*)?$", + "drawPixmapFragments <image filename> <count>" + " <centerx0> <centery0> <x0> <y0> <w0> <h0> <sx0> <sy0> <r0> <o0>" + " <centerx1> <centery1> <x1> <y1> <w1> ..." + "\n - where count is 1 or 2, and followed by centerPos, sourceRect, scaleX, scaleY, rotation, opacity <count> times", + "drawPixmapFragments :/images/sign.png 1 50 50 10 10 60 60 10 10 30 1"); DECL_PAINTCOMMANDSECTION("painterPaths"); DECL_PAINTCOMMAND("path_moveTo", command_path_moveTo, @@ -564,8 +560,12 @@ void PaintCommands::staticInit() "setClipPath mypath ReplaceClip"); DECL_PAINTCOMMAND("setClipRect", command_setClipRect, "^setClipRect\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s+(-?\\w*)\\s*(\\w*)$", - "setClipRect <x1> <y1> <x2> <y2> <clip operation enum>", - "setClipRect 0.0 0.0 10.0 10.0 ReplaceClip"); + "setClipRect <x> <y> <w> <h> <clip operation enum>", + "setClipRect 0 0 10 10 ReplaceClip"); + DECL_PAINTCOMMAND("setClipRectF", command_setClipRectF, + "^setClipRectF\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s+(-?[.\\w]*)\\s*(\\w.*)$", + "setClipRectF <x> <y> <w> <h> <clip operation enum>", + "setClipRectF 0.1 0.2 10.3 10.4 ReplaceClip"); DECL_PAINTCOMMAND("setClipping", command_setClipping, "^setClipping\\s+(\\w*)$", "setClipping <true|false>", @@ -686,6 +686,7 @@ void PaintCommands::staticInit() ADD_ENUMLIST("image formats", imageFormatTable); ADD_ENUMLIST("coordinate modes", coordinateMethodTable); ADD_ENUMLIST("size modes", sizeModeTable); + ADD_ENUMLIST("render hints", renderHintTable); } #undef DECL_PAINTCOMMAND @@ -745,8 +746,8 @@ void PaintCommands::runCommand(const QString &scriptLine) return; } QString firstWord = scriptLine.section(separators, 0, 0); - QList<int> indices = s_commandHash.values(firstWord); - foreach(int idx, indices) { + const QList<int> indices = s_commandHash.values(firstWord); + for (int idx : indices) { PaintCommandInfos command = s_commandInfoTable.at(idx); Q_ASSERT(command.regExp.isValid()); QRegularExpressionMatch match = command.regExp.match(scriptLine); @@ -921,7 +922,7 @@ void PaintCommands::command_import(QRegularExpressionMatch re) if (m_verboseMode) { printf(" -(lance) Command buffer now looks like:\n"); - for (int i = 0; i < m_commands.count(); ++i) + for (int i = 0; i < m_commands.size(); ++i) printf(" ---> {%s}\n", qPrintable(m_commands.at(i))); } delete file; @@ -939,7 +940,7 @@ void PaintCommands::command_begin_block(QRegularExpressionMatch re) m_commands[m_currentCommandIndex] = QLatin1String("# begin block (") + blockName + QLatin1Char(')'); QStringList newBlock; int i = m_currentCommandIndex + 1; - for (; i < m_commands.count(); ++i) { + for (; i < m_commands.size(); ++i) { const QString &nextCmd = m_commands.at(i); if (nextCmd.startsWith("end_block")) { m_commands[i] = QLatin1String("# end block (") + blockName + QLatin1Char(')'); @@ -949,10 +950,10 @@ void PaintCommands::command_begin_block(QRegularExpressionMatch re) } if (m_verboseMode) - for (int j = 0; j < newBlock.count(); ++j) + for (int j = 0; j < newBlock.size(); ++j) printf(" %d: %s\n", j, qPrintable(newBlock.at(j))); - if (i >= m_commands.count()) + if (i >= m_commands.size()) printf(" - Warning! Block doesn't have an 'end_block' marker!\n"); m_blockMap.insert(blockName, newBlock); @@ -1466,6 +1467,93 @@ void PaintCommands::command_fillRectF(QRegularExpressionMatch re) } } +void PaintCommands::command_drawPixmapFragments(QRegularExpressionMatch re) +{ + QPixmap pm; + pm = m_pixmapMap[re.captured(1)]; // try cache first + if (pm.isNull()) + pm = image_load<QPixmap>(re.captured(1)); + if (pm.isNull()) { + QFileInfo fi(m_filepath); + QDir dir = fi.absoluteDir(); + dir.cdUp(); + dir.cd("images"); + QString fileName = dir.absolutePath() + QLatin1Char('/') + re.captured(1); + pm = QPixmap(fileName); + if (pm.isNull() && !fileName.endsWith(".png")) { + fileName.append(".png"); + pm = QPixmap(fileName); + } + } + if (pm.isNull()) { + fprintf(stderr, "ERROR(drawPixmapFragments): failed to load pixmap: '%s'\n", + qPrintable(re.captured(1))); + return; + } + + int count = convertToInt(re.captured(2)); + + struct Fragment { + double posx; + double posy; + double srcx; + double srcy; + double srcw; + double srch; + double sx; + double sy; + double rotation; + double opacity; + }; + + QList<Fragment> fragments; + for (int i = 0; i < count; ++i) { + int captureIndexStart = 3 + i * 10; + if (re.hasCaptured(captureIndexStart)) { + Fragment f; + f.posx = convertToDouble(re.captured(captureIndexStart)); + f.posy = convertToDouble(re.captured(captureIndexStart + 1)); + f.srcx = convertToDouble(re.captured(captureIndexStart + 2)); + f.srcy = convertToDouble(re.captured(captureIndexStart + 3)); + f.srcw = convertToDouble(re.captured(captureIndexStart + 4)); + f.srch = convertToDouble(re.captured(captureIndexStart + 5)); + f.sx = convertToDouble(re.captured(captureIndexStart + 6)); + f.sy = convertToDouble(re.captured(captureIndexStart + 7)); + f.rotation = convertToDouble(re.captured(captureIndexStart + 8)); + f.opacity = convertToDouble(re.captured(captureIndexStart + 9)); + fragments.append(f); + } else { + break; + } + } + + if (m_verboseMode) { + printf(" -(lance) drawPixmapFragments('%s' count=%d ", + qPrintable(re.captured(1)), int(fragments.count())); + for (int i = 0; i < fragments.count(); ++i) { + printf("pos=(%.2f, %.2f) srcrect=(%.2f %.2f %.2f %.2f) scale=(%.2f %.2f) rotation=%.2f opacity=%.2f ", + fragments[i].posx, fragments[i].posy, + fragments[i].srcx, fragments[i].srcy, fragments[i].srcw, fragments[i].srch, + fragments[i].sx, fragments[i].sy, + fragments[i].rotation, + fragments[i].opacity); + } + printf("\n"); + } + + QList<QPainter::PixmapFragment> pixmapFragments; + for (int i = 0; i < fragments.count(); ++i) { + pixmapFragments.append( + QPainter::PixmapFragment::create(QPointF(fragments[i].posx, fragments[i].posy), + QRectF(fragments[i].srcx, fragments[i].srcy, fragments[i].srcw, fragments[i].srch), + fragments[i].sx, fragments[i].sy, + fragments[i].rotation, + fragments[i].opacity)); + } + + m_painter->drawPixmapFragments(pixmapFragments.constData(), pixmapFragments.count(), pm); +} + /***************************************************************************************************/ void PaintCommands::command_noop(QRegularExpressionMatch) { @@ -2082,6 +2170,25 @@ void PaintCommands::command_setClipRect(QRegularExpressionMatch re) } /***************************************************************************************************/ +void PaintCommands::command_setClipRectF(QRegularExpressionMatch re) +{ + QStringList caps = re.capturedTexts(); + double x = convertToDouble(caps.at(1)); + double y = convertToDouble(caps.at(2)); + double w = convertToDouble(caps.at(3)); + double h = convertToDouble(caps.at(4)); + + int combine = translateEnum(clipOperationTable, caps.at(5), Qt::IntersectClip + 1); + if (combine == -1) + combine = Qt::ReplaceClip; + + if (m_verboseMode) + printf(" -(lance) setClipRectF(%f, %f, %f, %f), %s\n", x, y, w, h, clipOperationTable[combine]); + + m_painter->setClipRect(QRectF(x, y, w, h), Qt::ClipOperation(combine)); +} + +/***************************************************************************************************/ void PaintCommands::command_setClipPath(QRegularExpressionMatch re) { int combine = translateEnum(clipOperationTable, re.captured(2), Qt::IntersectClip + 1); @@ -2121,19 +2228,27 @@ void PaintCommands::command_setFont(QRegularExpressionMatch re) { QStringList caps = re.capturedTexts(); QString family = caps.at(1); - int size = convertToInt(caps.at(2)); - - int weight = translateEnum(fontWeightTable, re.captured(3).toLower(), 5); - if (weight != -1) { - switch (weight) { - case 0: weight = QFont::Light; break; - case 1: weight = QFont::Normal; break; - case 2: weight = QFont::DemiBold; break; - case 3: weight = QFont::Bold; break; - case 4: weight = QFont::Black; break; - } - } else { - weight = convertToInt(re.captured(3)); + + int size = -1; // Default + QString sizeArg = caps.at(2); + if (!sizeArg.isEmpty()) + size = convertToInt(caps.at(2)); + + int weight = -1; // Default + QString weightArg = caps.at(3); + if (!weightArg.isEmpty()) { + weight = translateEnum(fontWeightTable, weightArg.toLower(), 5); + if (weight != -1) { + switch (weight) { + case 0: weight = QFont::Light; break; + case 1: weight = QFont::Normal; break; + case 2: weight = QFont::DemiBold; break; + case 3: weight = QFont::Bold; break; + case 4: weight = QFont::Black; break; + } + } else { + weight = convertToInt(weightArg); + } } bool italic = caps.at(4).toLower() == "true" || caps.at(4).toLower() == "italic"; @@ -2234,18 +2349,27 @@ void PaintCommands::command_setPen2(QRegularExpressionMatch re) void PaintCommands::command_setRenderHint(QRegularExpressionMatch re) { QString hintString = re.captured(1).toLower(); - bool on = re.captured(2).isEmpty() || re.captured(2).toLower() == "true"; - if (hintString.contains("antialiasing")) { - if (m_verboseMode) - printf(" -(lance) setRenderHint Antialiasing\n"); + QString setting = re.captured(2).toLower(); - m_painter->setRenderHint(QPainter::Antialiasing, on); + bool on = setting.isEmpty() || setting == "true" || setting == "on"; + QPainter::RenderHint hint; + int hintIdx = -1; + if (hintString.contains("antialiasing")) { + hintIdx = 0; + hint = QPainter::Antialiasing; } else if (hintString.contains("smoothpixmaptransform")) { + hintIdx = 1; + hint = QPainter::SmoothPixmapTransform; + } else if (hintString.contains("noncosmeticbrushpatterns")) { + hintIdx = 2; + hint = QPainter::NonCosmeticBrushPatterns; + } + if (hintIdx >= 0) { if (m_verboseMode) - printf(" -(lance) setRenderHint SmoothPixmapTransform\n"); - m_painter->setRenderHint(QPainter::SmoothPixmapTransform, on); + printf(" -(lance) setRenderHint %s %s\n", renderHintTable[hintIdx], on ? "true" : "false"); + m_painter->setRenderHint(hint, on); } else { - fprintf(stderr, "ERROR(setRenderHint): unknown hint '%s'\n", qPrintable(hintString)); + fprintf(stderr, "ERROR(setRenderHint): unknown hint '%s'\n", qPrintable(re.captured(1))); } } @@ -2254,6 +2378,7 @@ void PaintCommands::command_clearRenderHint(QRegularExpressionMatch /*re*/) { m_painter->setRenderHint(QPainter::Antialiasing, false); m_painter->setRenderHint(QPainter::SmoothPixmapTransform, false); + m_painter->setRenderHint(QPainter::NonCosmeticBrushPatterns, false); if (m_verboseMode) printf(" -(lance) clearRenderHint\n"); } diff --git a/tests/baseline/shared/paintcommands.h b/tests/baseline/shared/paintcommands.h index 15c6d3fa58..45f78f9af6 100644 --- a/tests/baseline/shared/paintcommands.h +++ b/tests/baseline/shared/paintcommands.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef PAINTCOMMANDS_H #define PAINTCOMMANDS_H @@ -68,15 +43,13 @@ class PaintCommands { public: // construction / initialization - PaintCommands(const QStringList &cmds, int w, int h, QImage::Format format) + PaintCommands(const QStringList &cmds, int /*w*/, int /*h*/, QImage::Format format) : m_painter(0) , m_surface_painter(0) , m_format(format) , m_commands(cmds) , m_gradientSpread(QGradient::PadSpread) , m_gradientCoordinate(QGradient::LogicalMode) - , m_width(w) - , m_height(h) , m_verboseMode(false) , m_type(WidgetType) , m_checkers_background(true) @@ -87,7 +60,9 @@ public: , m_surface_glbuffer(0) , m_surface_glpaintdevice(0) #endif - { staticInit(); } + { + staticInit(); + } public: void setCheckersBackground(bool b) { staticInit(); m_checkers_background = b; } @@ -162,7 +137,7 @@ private: void command_brushShear(QRegularExpressionMatch re); void command_setClipPath(QRegularExpressionMatch re); void command_setClipRect(QRegularExpressionMatch re); - void command_setClipRectangle(QRegularExpressionMatch re); + void command_setClipRectF(QRegularExpressionMatch re); void command_setClipRegion(QRegularExpressionMatch re); void command_setClipping(QRegularExpressionMatch re); void command_setCompositionMode(QRegularExpressionMatch re); @@ -208,6 +183,7 @@ private: void command_drawTiledPixmap(QRegularExpressionMatch re); void command_fillRect(QRegularExpressionMatch re); void command_fillRectF(QRegularExpressionMatch re); + void command_drawPixmapFragments(QRegularExpressionMatch re); // paths void command_path_addEllipse(QRegularExpressionMatch re); @@ -276,8 +252,6 @@ private: QGradient::Spread m_gradientSpread; QGradient::CoordinateMode m_gradientCoordinate; bool m_abort; - int m_width; - int m_height; bool m_verboseMode; DeviceType m_type; @@ -305,6 +279,7 @@ private: static const char *compositionModeTable[]; static const char *imageFormatTable[]; static const char *sizeModeTable[]; + static const char *renderHintTable[]; static int translateEnum(const char *table[], const QString &pattern, int limit); // utility diff --git a/tests/baseline/shared/qbaselinetest.cpp b/tests/baseline/shared/qbaselinetest.cpp index 493be4bd7a..e41b8d5321 100644 --- a/tests/baseline/shared/qbaselinetest.cpp +++ b/tests/baseline/shared/qbaselinetest.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qbaselinetest.h" #include "baselineprotocol.h" @@ -51,9 +26,6 @@ static QByteArray curFunction; static ImageItemList itemList; static bool gotBaselines; -static QString definedTestProject; -static QString definedTestCase; - void handleCmdLineArgs(int *argcp, char ***argvp) { @@ -167,7 +139,7 @@ void fetchCustomClientProperties() key = line.left(colonPos).simplified().replace(' ', '_'); val = line.mid(colonPos+1).trimmed(); } - if (!key.isEmpty() && key.length() < 64 && val.length() < 256) // ###TBD: maximum 256 chars in value? + if (!key.isEmpty() && key.size() < 64 && val.size() < 256) // ###TBD: maximum 256 chars in value? addClientProperty(key, val); else qDebug() << "Unparseable script output ignored:" << line; @@ -200,10 +172,7 @@ bool connect(QByteArray *msg, bool *error) if (!customAutoModeSet) clientInfo.setAdHocRun(defaultInfo.isAdHocRun()); - if (!definedTestProject.isEmpty()) - clientInfo.insert(PI_Project, definedTestProject); - - QString testCase = definedTestCase; + QString testCase = clientInfo.value(PI_TestCase); if (testCase.isEmpty() && QTest::testObject() && QTest::testObject()->metaObject()) { //qDebug() << "Trying to Read TestCaseName from Testlib!"; testCase = QTest::testObject()->metaObject()->className(); @@ -233,16 +202,10 @@ bool disconnectFromBaselineServer() return false; } -bool connectToBaselineServer(QByteArray *msg, const QString &testProject, const QString &testCase) +bool connectToBaselineServer(QByteArray *msg) { bool dummy; QByteArray dummyMsg; - - if (!testProject.isEmpty()) - definedTestProject = testProject; - if (!testCase.isEmpty()) - definedTestCase = testCase; - return connect(msg ? msg : &dummyMsg, &dummy); } @@ -259,7 +222,7 @@ void setSimFail(bool fail) void setProject(const QString &projectName) { - definedTestProject = projectName; + addClientProperty(PI_Project, projectName); } void setProjectImageKeys(const QStringList &keys) @@ -286,6 +249,7 @@ void modifyImage(QImage *img) bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, bool *error) { + *error = false; ImageItem item = baseline; if (simfail) { // Simulate test failure by forcing image mismatch; for testing purposes @@ -296,6 +260,7 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, } else { item.image = img; } + bool isNewItem = false; item.imageChecksums.clear(); item.imageChecksums.prepend(ImageItem::computeChecksum(item.image)); QByteArray srvMsg; @@ -307,9 +272,11 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, return true; break; case ImageItem::BaselineNotFound: - if (!customInfo.overrides().isEmpty() || baselinePolicy == UploadNone) { - qWarning() << "Cannot compare to baseline: No such baseline found on server."; + if (!customInfo.overrides().isEmpty()) return true; + if (baselinePolicy == UploadNone) { + isNewItem = true; + break; } if (proto.submitNewBaseline(item, &srvMsg)) qDebug() << msg->constData() << "Baseline not found on server. New baseline uploaded."; @@ -322,7 +289,6 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, return true; break; } - *error = false; // The actual comparison of the given image with the baseline: if (baseline.imageChecksums.contains(item.imageChecksums.at(0))) { if (!proto.submitMatch(item, &srvMsg)) @@ -343,7 +309,11 @@ bool compareItem(const ImageItem &baseline, const QImage &img, QByteArray *msg, qInfo() << "Baseline server reports:" << srvMsg; return true; // The server decides: a fuzzy match means no mismatch } - *msg += "Mismatch. See report:\n " + srvMsg; + if (isNewItem) + *msg += "No baseline on server, so cannot compare."; + else + *msg += "Mismatch."; + *msg += " See report:\n " + srvMsg; if (dryRunMode) { qDebug() << "Dryrun, so ignoring" << *msg; return true; @@ -409,22 +379,21 @@ QTestData &newRow(const char *dataTag, quint16 checksum) return QTest::newRow(dataTag); } - -bool testImage(const QImage& img, QByteArray *msg, bool *error) +const ImageItem *findCurrentItem(QByteArray *msg, bool *error) { if (!connected && !connect(msg, error)) - return true; + return nullptr; if (QTest::currentTestFunction() != curFunction || itemList.isEmpty()) { - qWarning() << "Usage error: QBASELINE_TEST used without corresponding QBaselineTest::newRow()"; - return true; + qWarning() << "Usage error: QBASELINE_ macro used without corresponding QBaselineTest::newRow()"; + return nullptr; } if (!gotBaselines) { if (!proto.requestBaselineChecksums(QString::fromLatin1(QTest::currentTestFunction()), &itemList) || itemList.isEmpty()) { *msg = "Communication with baseline server failed: " + proto.errorMessage().toLatin1(); *error = true; - return true; + return nullptr; } gotBaselines = true; } @@ -434,10 +403,24 @@ bool testImage(const QImage& img, QByteArray *msg, bool *error) while (it != itemList.constEnd() && it->itemName != curTag) ++it; if (it == itemList.constEnd()) { - qWarning() << "Usage error: QBASELINE_TEST used without corresponding QBaselineTest::newRow() for row" << curTag; - return true; + qWarning() << "Usage error: QBASELINE_ macro used without corresponding QBaselineTest::newRow() for row" << curTag; + return nullptr; } - return compareItem(*it, img, msg, error); + return &(*it); +} + +bool testImage(const QImage &img, QByteArray *msg, bool *error) +{ + const ImageItem *item = findCurrentItem(msg, error); + return item ? compareItem(*item, img, msg, error) : true; +} + +bool isCurrentItemBlacklisted() +{ + QByteArray msg; + bool error = false; + const ImageItem *item = findCurrentItem(&msg, &error); + return item ? (item->status == ImageItem::IgnoreItem) : false; } } diff --git a/tests/baseline/shared/qbaselinetest.h b/tests/baseline/shared/qbaselinetest.h index 68f2efe461..f120e2bcd8 100644 --- a/tests/baseline/shared/qbaselinetest.h +++ b/tests/baseline/shared/qbaselinetest.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef BASELINETEST_H #define BASELINETEST_H @@ -39,10 +14,11 @@ void handleCmdLineArgs(int *argcp, char ***argvp); void setProject(const QString &projectName); // Selects server config settings and top level dir void setProjectImageKeys(const QStringList &keys); // Overrides the ItemPathKeys config setting void addClientProperty(const QString& key, const QString& value); -bool connectToBaselineServer(QByteArray *msg = nullptr, const QString &testProject = QString(), const QString &testCase = QString()); +bool connectToBaselineServer(QByteArray *msg = nullptr); bool checkImage(const QImage& img, const char *name, quint16 checksum, QByteArray *msg, bool *error, int manualdatatag = 0); bool testImage(const QImage& img, QByteArray *msg, bool *error); QTestData &newRow(const char *dataTag, quint16 checksum = 0); +bool isCurrentItemBlacklisted(); bool disconnectFromBaselineServer(); bool shouldAbortIfUnstable(); } @@ -84,4 +60,10 @@ do {\ }\ } while (0) +#define QBASELINE_SKIP_IF_BLACKLISTED \ +do {\ + if (QBaselineTest::isCurrentItemBlacklisted())\ + QSKIP("Blacklisted on baseline server.");\ +} while (0) + #endif // BASELINETEST_H diff --git a/tests/baseline/shared/qwidgetbaselinetest.cpp b/tests/baseline/shared/qwidgetbaselinetest.cpp index e4b36ddb69..72a074e268 100644 --- a/tests/baseline/shared/qwidgetbaselinetest.cpp +++ b/tests/baseline/shared/qwidgetbaselinetest.cpp @@ -1,38 +1,16 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "qwidgetbaselinetest.h" #include <qbaselinetest.h> #include <QApplication> #include <QStyle> +#include <QStyleHints> #include <QScreen> +#include <QtWidgets/private/qapplication_p.h> + QT_BEGIN_NAMESPACE QWidgetBaselineTest::QWidgetBaselineTest() @@ -47,18 +25,25 @@ QWidgetBaselineTest::QWidgetBaselineTest() // Encode a number of parameters that impact the UI QPalette palette; QFont font; - QByteArray appearanceBytes; - { - QDataStream appearanceStream(&appearanceBytes, QIODevice::WriteOnly); - appearanceStream << palette << font << + const QString styleName = #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::style()->metaObject()->className(); #else QApplication::style()->name(); #endif + // turn off animations and make the cursor flash time really long to avoid blinking + QApplication::style()->setProperty("_qt_animation_time", QTime()); + QGuiApplication::styleHints()->setCursorFlashTime(50000); + + QByteArray appearanceBytes; + { + QDataStream appearanceStream(&appearanceBytes, QIODevice::WriteOnly); + appearanceStream << palette << font; const qreal screenDpr = QApplication::primaryScreen()->devicePixelRatio(); - if (screenDpr != 1.0) - qWarning() << "DPR is" << screenDpr << "- images will be scaled"; + if (screenDpr != 1.0) { + qWarning() << "DPR is" << screenDpr << "- images will not be compared to 1.0 baseline!"; + appearanceStream << screenDpr; + } } #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) const quint16 appearanceId = qChecksum(appearanceBytes, appearanceBytes.size()); @@ -72,8 +57,8 @@ QWidgetBaselineTest::QWidgetBaselineTest() 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); + ? QString("light-%1-%2") : QString("dark-%1-%2")) + .arg(styleName).arg(appearanceId, 0, 16); QBaselineTest::addClientProperty("AppearanceID", appearanceIdString); // let users know where they can find the results @@ -92,11 +77,15 @@ void QWidgetBaselineTest::initTestCase() void QWidgetBaselineTest::init() { QVERIFY(!window); - window = new QWidget; + background = new QWidget(nullptr, Qt::FramelessWindowHint); + window = new QWidget(background, Qt::Window); window->setWindowTitle(QTest::currentDataTag()); + window->setFocusPolicy(Qt::StrongFocus); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + background->setScreen(QGuiApplication::primaryScreen()); window->setScreen(QGuiApplication::primaryScreen()); #endif + background->move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); window->move(QGuiApplication::primaryScreen()->availableGeometry().topLeft()); doInit(); @@ -106,33 +95,46 @@ void QWidgetBaselineTest::cleanup() { doCleanup(); - delete window; + delete background; + background = nullptr; window = nullptr; } void QWidgetBaselineTest::makeVisible() { Q_ASSERT(window); + background->showMaximized(); window->show(); - QApplication::setActiveWindow(window); + QApplicationPrivate::setActiveWindow(window); QVERIFY(QTest::qWaitForWindowActive(window)); - // explicitly unset focus, the test needs to control when focus is shown - if (window->focusWidget()) - window->focusWidget()->clearFocus(); + // explicitly set focus on the window so that the test widget doesn't have it + window->setFocus(Qt::OtherFocusReason); + QTRY_COMPARE(window->focusWidget(), window); } /* - Always return images scaled to a DPR of 1.0. - - This might produce some fuzzy differences, but lets us - compare those. + Grabs the test window and returns the resulting QImage, without + compensating for DPR differences. */ QImage QWidgetBaselineTest::takeSnapshot() { - QGuiApplication::processEvents(); - QPixmap pm = window->grab(); - QTransform scaleTransform = QTransform::fromScale(1.0 / pm.devicePixelRatioF(), 1.0 / pm.devicePixelRatioF()); - return pm.toImage().transformed(scaleTransform, Qt::SmoothTransformation); + // make sure all effects are done + QTest::qWait(250); + return window->grab().toImage(); +} + +/* + Grabs the test window screen and returns the resulting QImage, without + compensating for DPR differences. + This can be used for popup windows. +*/ +QImage QWidgetBaselineTest::takeScreenSnapshot(const QRect& windowRect) +{ + // make sure all effects are done - wait longer here because entire + // windows might be fading in and out. + QTest::qWait(750); + return window->screen()->grabWindow(0, windowRect.x(), windowRect.y(), + windowRect.width(), windowRect.height()).toImage(); } /*! @@ -145,23 +147,25 @@ QImage QWidgetBaselineTest::takeSnapshot() void QWidgetBaselineTest::takeStandardSnapshots() { makeVisible(); - struct PublicWidget : QWidget { - bool focusNextPrevChild(bool next) override { return QWidget::focusNextPrevChild(next); } - }; + QWidget *oldFocusWidget = testWindow()->focusWidget(); + QCOMPARE(oldFocusWidget, testWindow()); QBASELINE_CHECK_DEFERRED(takeSnapshot(), "default"); // try hard to set focus - static_cast<PublicWidget*>(window)->focusNextPrevChild(true); - if (!window->focusWidget()) { - QWidget *firstChild = window->findChild<QWidget*>(); - if (firstChild) - firstChild->setFocus(); - } - - if (testWindow()->focusWidget()) { + QWidget *testWidget = window->nextInFocusChain(); + if (!testWidget) + testWidget = window->findChild<QWidget*>(); + QVERIFY(testWidget); + // use TabFocusReason, some widgets handle that specifically to e.g. select + testWidget->setFocus(Qt::TabFocusReason); + + if (testWindow()->focusWidget() != oldFocusWidget) { QBASELINE_CHECK_DEFERRED(takeSnapshot(), "focused"); - testWindow()->focusWidget()->clearFocus(); + // set focus back + oldFocusWidget->setFocus(Qt::OtherFocusReason); + } else { + qWarning() << "Couldn't set focus on tested widget" << testWidget; } // this disables all children diff --git a/tests/baseline/shared/qwidgetbaselinetest.h b/tests/baseline/shared/qwidgetbaselinetest.h index db73dd0540..2142217c09 100644 --- a/tests/baseline/shared/qwidgetbaselinetest.h +++ b/tests/baseline/shared/qwidgetbaselinetest.h @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #pragma once @@ -57,8 +32,10 @@ private slots: protected: void makeVisible(); QImage takeSnapshot(); + QImage takeScreenSnapshot(const QRect& rect = QRect()); private: + QWidget *background = nullptr; QWidget *window = nullptr; }; diff --git a/tests/baseline/stylesheet/CMakeLists.txt b/tests/baseline/stylesheet/CMakeLists.txt index 11f6e52179..3fdaa739fe 100644 --- a/tests/baseline/stylesheet/CMakeLists.txt +++ b/tests/baseline/stylesheet/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + file(GLOB_RECURSE test_data_glob RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} qss/*) @@ -11,9 +14,10 @@ qt_internal_add_test(tst_baseline_stylesheet tst_baseline_stylesheet.cpp INCLUDE_DIRECTORIES ../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate Qt::Network TESTDATA ${test_data} ) diff --git a/tests/baseline/stylesheet/qss/dummy.qss b/tests/baseline/stylesheet/qss/dummy.qss new file mode 100644 index 0000000000..7f09309153 --- /dev/null +++ b/tests/baseline/stylesheet/qss/dummy.qss @@ -0,0 +1,31 @@ +/* dummy stylesheet to reproduce QTBUG-100433 for QToolButton +QDummyView { + alternate-background-color: yellow; +} + +QDummyView { + show-decoration-selected: 1; +} + +QDummyView::item { + border: 1px solid #d9d9d9; + border-top-color: transparent; + border-bottom-color: transparent; +} + +QDummyView::item:hover { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #e7effd, stop: 1 #cbdaf1); + border: 1px solid #bfcde4; +} + +QDummyView::item:selected { + border: 1px solid #567dbc; +} + +QDummyView::item:selected:active{ + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6ea1f1, stop: 1 #567dbc); +} + +QDummyView::item:selected:!active { + background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #6b9be8, stop: 1 #577fbf); +} diff --git a/tests/baseline/stylesheet/qss/qheaderview/selectedFontWeight.qss b/tests/baseline/stylesheet/qss/qheaderview/selectedFontWeight.qss new file mode 100644 index 0000000000..1c45a99767 --- /dev/null +++ b/tests/baseline/stylesheet/qss/qheaderview/selectedFontWeight.qss @@ -0,0 +1,16 @@ +QHeaderView::section { + background-color: red; + font-size: 10px; +} + +QHeaderView::section:checked { + background-color: green; + font-size: 20px; + font-weight: bold; +} + +QHeaderView::section:first { + background-color: yellow; + font-size: 20px; + font-weight: normal; +} diff --git a/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_no_border.qss b/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_no_border.qss new file mode 100644 index 0000000000..e9e098eb5c --- /dev/null +++ b/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_no_border.qss @@ -0,0 +1 @@ +QToolButton::menu-button { border: none } diff --git a/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_subcontrol_padding.qss b/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_subcontrol_padding.qss new file mode 100644 index 0000000000..44e67671f0 --- /dev/null +++ b/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_subcontrol_padding.qss @@ -0,0 +1,12 @@ +QToolButton { + border: 5px solid #9e9e9e; + background: #ffffff; + padding-top: 8px; + padding-bottom: 8px; +} +QToolButton[popupMode=InstantPopup] { + padding-right: 75px; +} +QToolButton::menu-indicator { + subcontrol-position: right; +}; diff --git a/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_subcontrol_position.qss b/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_subcontrol_position.qss new file mode 100644 index 0000000000..4a1a5f0c23 --- /dev/null +++ b/tests/baseline/stylesheet/qss/qtoolbutton/menuButton_subcontrol_position.qss @@ -0,0 +1,4 @@ +QToolButton::menu-indicator { + subcontrol-position: right center; + subcontrol-origin: padding; +} diff --git a/tests/baseline/stylesheet/qss/qtreeview/showDecorationSelected.qss b/tests/baseline/stylesheet/qss/qtreeview/showDecorationSelected.qss new file mode 100644 index 0000000000..b279b587bd --- /dev/null +++ b/tests/baseline/stylesheet/qss/qtreeview/showDecorationSelected.qss @@ -0,0 +1,3 @@ +QTreeView { + show-decoration-selected: 1 +} diff --git a/tests/baseline/stylesheet/qss/qtreeview/styledIndicators.qss b/tests/baseline/stylesheet/qss/qtreeview/styledIndicators.qss new file mode 100644 index 0000000000..02263ad644 --- /dev/null +++ b/tests/baseline/stylesheet/qss/qtreeview/styledIndicators.qss @@ -0,0 +1,18 @@ +QTreeWidget::indicator:indeterminate { + background: red +} +QTreeWidget::indicator:indeterminate:disabled { + background: pink +} +QTreeWidget::indicator:checked { + background: green +} +QTreeWidget::indicator:checked:disabled { + background: lightgreen +} +QTreeWidget::indicator:unchecked { + background: blue +} +QTreeWidget::indicator:unchecked:disabled { + background: lightblue +}; diff --git a/tests/baseline/stylesheet/qss/qtreeview/styledItem.qss b/tests/baseline/stylesheet/qss/qtreeview/styledItem.qss new file mode 100644 index 0000000000..1da627881c --- /dev/null +++ b/tests/baseline/stylesheet/qss/qtreeview/styledItem.qss @@ -0,0 +1,7 @@ +QAbstractItemView::item +{ + background: grey; +} +QTreeWidget::indicator:checked { + background: green +} diff --git a/tests/baseline/stylesheet/qss/qtreeview/styledSelection.qss b/tests/baseline/stylesheet/qss/qtreeview/styledSelection.qss new file mode 100644 index 0000000000..7d54a74fe5 --- /dev/null +++ b/tests/baseline/stylesheet/qss/qtreeview/styledSelection.qss @@ -0,0 +1,10 @@ +QTreeView { + alternate-background-color: yellow; + show-decoration-selected: 1; +} +QTreeView::item:selected:active { + background: qlineargradient(x1:0, y1:0 x2: 0, y2: 1, stop: 0 #fea1f1 stop: 1 #567dbc) +} +QTreeView::branch { + border: 2px +} diff --git a/tests/baseline/stylesheet/tst_baseline_stylesheet.cpp b/tests/baseline/stylesheet/tst_baseline_stylesheet.cpp index 99b21b4bb5..67a618988b 100644 --- a/tests/baseline/stylesheet/tst_baseline_stylesheet.cpp +++ b/tests/baseline/stylesheet/tst_baseline_stylesheet.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qbaselinetest.h> #include <qwidgetbaselinetest.h> @@ -49,6 +24,12 @@ private slots: void tst_QScrollArea_data(); void tst_QScrollArea(); + void tst_QTreeView_data(); + void tst_QTreeView(); + + void tst_QHeaderView_data(); + void tst_QHeaderView(); + private: QDir styleSheetDir; }; @@ -85,7 +66,7 @@ void tst_Stylesheet::loadTestFiles() for (const auto &qssFile : qssFiles) { QFileInfo fileInfo(qssFile); QFile file(qssFile); - file.open(QFile::ReadOnly); + QVERIFY(file.open(QFile::ReadOnly)); QString styleSheet = QString::fromUtf8(file.readAll()); QBaselineTest::newRow(fileInfo.baseName().toUtf8()) << styleSheet; } @@ -98,7 +79,7 @@ void tst_Stylesheet::tst_QToolButton_data() void tst_Stylesheet::tst_QToolButton() { - const QIcon fileIcon = QApplication::style()->standardIcon(QStyle::SP_FileIcon); + const QIcon trashIcon = QApplication::style()->standardIcon(QStyle::SP_TrashIcon); QVBoxLayout *vbox = new QVBoxLayout; @@ -107,8 +88,8 @@ void tst_Stylesheet::tst_QToolButton() Qt::ToolButtonTextUnderIcon, Qt::ToolButtonTextBesideIcon}) { QToolButton *normal = new QToolButton; normal->setToolButtonStyle(buttonStyle); - normal->setText("Text"); - normal->setIcon(fileIcon); + normal->setText("Norm"); + normal->setIcon(trashIcon); normalButtons->addWidget(normal); } vbox->addLayout(normalButtons); @@ -116,7 +97,7 @@ void tst_Stylesheet::tst_QToolButton() QHBoxLayout *arrowButtons = new QHBoxLayout; for (const auto &arrowType : {Qt::LeftArrow, Qt::RightArrow, Qt::UpArrow, Qt::DownArrow}) { QToolButton *arrow = new QToolButton; - arrow->setText("Text"); + arrow->setText("Arrs"); arrow->setArrowType(arrowType); arrowButtons->addWidget(arrow); } @@ -126,7 +107,7 @@ void tst_Stylesheet::tst_QToolButton() for (const auto &buttonStyle : {Qt::ToolButtonTextOnly, Qt::ToolButtonTextUnderIcon, Qt::ToolButtonTextBesideIcon}) { QToolButton *arrow = new QToolButton; - arrow->setText("Text"); + arrow->setText("ArrTxt"); arrow->setArrowType(Qt::UpArrow); arrow->setToolButtonStyle(buttonStyle); arrowWithTextButtons->addWidget(arrow); @@ -137,8 +118,8 @@ void tst_Stylesheet::tst_QToolButton() for (const auto &popupMode : {QToolButton::InstantPopup, QToolButton::MenuButtonPopup, QToolButton::DelayedPopup}) { QToolButton *menuButton = new QToolButton; - menuButton->setText("Text"); - menuButton->setIcon(fileIcon); + menuButton->setText("PppMd"); + menuButton->setIcon(trashIcon); QMenu *menuButtonMenu = new QMenu; menuButtonMenu->addAction(QIcon(":/icons/align-left.png"), "Left"); menuButtonMenu->addAction(QIcon(":/icons/align-right.png"), "Right"); @@ -170,13 +151,87 @@ void tst_Stylesheet::tst_QScrollArea() QBASELINE_TEST(takeSnapshot()); } +void tst_Stylesheet::tst_QTreeView_data() +{ + loadTestFiles(); +} + +void tst_Stylesheet::tst_QTreeView() +{ + QHBoxLayout *layout = new QHBoxLayout; + QTreeWidget *tw = new QTreeWidget(); + tw->header()->hide(); + layout->addWidget(tw); + + enum { + Unchecked = 0, + Checked = 1, + Children = 2, + Disabled = 3, + CheckedDisabled = 4, + ChildrenDisabled = 5, + NConfigs + }; + + for (int i = 0; i < NConfigs; ++i) { + QTreeWidgetItem *topLevelItem = new QTreeWidgetItem(tw, QStringList{QString("top %1").arg(i)}); + switch (i) { + case Unchecked: + case Disabled: + topLevelItem->setCheckState(0, Qt::Unchecked); + break; + case Checked: + case CheckedDisabled: + topLevelItem->setCheckState(0, Qt::Checked); + break; + case Children: + case ChildrenDisabled: + topLevelItem->setCheckState(0, Qt::PartiallyChecked); + topLevelItem->setExpanded(true); + for (int j = 0; j < 2; ++j) { + QTreeWidgetItem *childItem = new QTreeWidgetItem(topLevelItem, QStringList{QString("child %1").arg(j)}); + childItem->setCheckState(0, j % 2 ? Qt::Unchecked : Qt::Checked); + } + break; + } + topLevelItem->setDisabled(i >= Disabled); + } + testWindow()->setLayout(layout); + tw->setRootIsDecorated(true); + makeVisible(); + + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "rootDecorated"); + tw->setRootIsDecorated(false); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "rootNotDecorated"); + + tw->topLevelItem(Children)->child(0)->setSelected(true); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "itemSelected"); +} + +void tst_Stylesheet::tst_QHeaderView_data() +{ + loadTestFiles(); +} + +void tst_Stylesheet::tst_QHeaderView() +{ + QHBoxLayout *layout = new QHBoxLayout; + QTableWidget *tw = new QTableWidget(10, 10); + tw->setCurrentCell(1, 1); + layout->addWidget(tw); + testWindow()->setLayout(layout); + makeVisible(); + QBASELINE_TEST(takeSnapshot()); +} + #define main _realmain QTEST_MAIN(tst_Stylesheet) #undef main int main(int argc, char *argv[]) { - qSetGlobalQHashSeed(0); // Avoid rendering variations caused by QHash randomization + // Avoid rendering variations caused by QHash randomization + QHashSeed::setDeterministicGlobalSeed(); QBaselineTest::handleCmdLineArgs(&argc, &argv); return _realmain(argc, argv); diff --git a/tests/baseline/text/CMakeLists.txt b/tests/baseline/text/CMakeLists.txt index 707b3794b5..74d01337cb 100644 --- a/tests/baseline/text/CMakeLists.txt +++ b/tests/baseline/text/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + list(APPEND test_data "./data") qt_internal_add_test(tst_baseline_text @@ -8,9 +11,10 @@ qt_internal_add_test(tst_baseline_text tst_baseline_text.cpp INCLUDE_DIRECTORIES ../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate Qt::Network TESTDATA ${test_data} ) diff --git a/tests/baseline/text/data/colored_list.html b/tests/baseline/text/data/colored_list.html new file mode 100644 index 0000000000..d1cca94460 --- /dev/null +++ b/tests/baseline/text/data/colored_list.html @@ -0,0 +1,68 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html> +<head> +<style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +body { background-color: #111155; color: #ffffff; } +</style> +</head> +<body> + +<ul> +<li>disc</li> +<li style=" color:#a58d47;">bronze</li> +<li style=" color:red;"><span style=" color:#ffcdb9;">red bullet, pink text</span></li> +<li style=" color:#dddddd;" class="checked">checked</li> +<li style=" color:#dddddd;" class="unchecked">unchecked</li> +</ul> + +<ul type="circle"> +<li>circle</li> +<li style=" color:#dddddd;">silver</li> +<li style=" color:lightgrey;"><span style=" color:#ffcdb9;">grey bullet, pink text</span></li> +<li style=" color:#dddddd;" class="checked">checked</li> +<li style=" color:#dddddd;" class="unchecked">unchecked</li> +</ul> + +<ul type="square"> +<li style=" color:#ffffff;">square</li> +<li style=" color:#fceebb;">gold</li> +<li style=" color:yellow;"><span style=" color:#ffcdb9;">yellow bullet, pink text</span></li> +<li style=" color:#dddddd;" class="checked">checked</li> +<li style=" color:#dddddd;" class="unchecked">unchecked</li> +</ul> + +<ol> +<li>decimal</li> +<li style=" color:#a58d47;">bronze decimal</li> +<li style=" color:red;"><span style=" color:#ffcdb9;">red number, pink text</span></li> +</ol> + +<ol type="A"> +<li>uppercase</li> +<li style=" color:#a58d47;">bronze uppercase</li> +<li style=" color:red;"><span style=" color:#ffcdb9;">red letter, pink text</span></li> +</ol> + +<ol type="a"> +<li>lowercase</li> +<li style=" color:#a58d47;">bronze lowercase</li> +<li style=" color:red;"><span style=" color:#ffcdb9;">red letter, pink text</span></li> +</ol> + +<ol type="i"> +<li>lower roman</li> +<li style=" color:#a58d47;">bronze roman</li> +<li style=" color:red;"><span style=" color:#ffcdb9;">red number, pink text</span></li> +</ol> + +<ol type="I"> +<li>upper roman</li> +<li style=" color:#a58d47;">bronze roman</li> +<li style=" color:red;"><span style=" color:#ffcdb9;">red number, pink text</span></li> +</ol> +</body> +</html> diff --git a/tests/baseline/text/tst_baseline_text.cpp b/tests/baseline/text/tst_baseline_text.cpp index 363a2ef6cd..59a5f478a5 100644 --- a/tests/baseline/text/tst_baseline_text.cpp +++ b/tests/baseline/text/tst_baseline_text.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qbaselinetest.h> #include <qwidgetbaselinetest.h> @@ -42,6 +17,7 @@ public: private slots: void tst_render_data(); void tst_render(); + void tst_differentScriptsBackgrounds(); private: QDir htmlDir; @@ -73,7 +49,7 @@ void tst_Text::loadTestFiles() for (const auto &htmlFile : htmlFiles) { QFileInfo fileInfo(htmlFile); QFile file(htmlFile); - file.open(QFile::ReadOnly); + QVERIFY(file.open(QFile::ReadOnly)); QString html = QString::fromUtf8(file.readAll()); QBaselineTest::newRow(fileInfo.baseName().toUtf8()) << html; } @@ -106,6 +82,26 @@ void tst_Text::tst_render() QBASELINE_TEST(image); } +void tst_Text::tst_differentScriptsBackgrounds() +{ + QTextDocument textDocument; + textDocument.setPageSize(QSizeF(800, 600)); + textDocument.setHtml(QString::fromUtf8("<i><font style=\"font-size:72px\"><font style=\"background:#FFFF00\">イ雨エ</font></font></i>")); + + QImage image(800, 600, QImage::Format_ARGB32); + image.fill(Qt::white); + + { + QPainter painter(&image); + + QAbstractTextDocumentLayout::PaintContext context; + context.palette.setColor(QPalette::Text, Qt::black); + textDocument.documentLayout()->draw(&painter, context); + } + + QBASELINE_CHECK(image, "tst_differentScriptsBackgrounds"); +} + #define main _realmain QTEST_MAIN(tst_Text) @@ -113,7 +109,8 @@ QTEST_MAIN(tst_Text) int main(int argc, char *argv[]) { - qSetGlobalQHashSeed(0); // Avoid rendering variations caused by QHash randomization + // Avoid rendering variations caused by QHash randomization + QHashSeed::setDeterministicGlobalSeed(); QBaselineTest::handleCmdLineArgs(&argc, &argv); return _realmain(argc, argv); diff --git a/tests/baseline/widgets/CMakeLists.txt b/tests/baseline/widgets/CMakeLists.txt index 124e67785f..07938f69b4 100644 --- a/tests/baseline/widgets/CMakeLists.txt +++ b/tests/baseline/widgets/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + qt_internal_add_test(tst_baseline_widgets SOURCES ../shared/baselineprotocol.cpp ../shared/baselineprotocol.h ../shared/lookup3.cpp @@ -6,8 +9,9 @@ qt_internal_add_test(tst_baseline_widgets tst_baseline_widgets.cpp INCLUDE_DIRECTORIES ../shared - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate Qt::Network ) diff --git a/tests/baseline/widgets/tst_baseline_widgets.cpp b/tests/baseline/widgets/tst_baseline_widgets.cpp index d49e9b494f..8a763eb8fa 100644 --- a/tests/baseline/widgets/tst_baseline_widgets.cpp +++ b/tests/baseline/widgets/tst_baseline_widgets.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qbaselinetest.h> #include <qwidgetbaselinetest.h> @@ -45,12 +20,26 @@ private slots: void tst_QPushButton_data(); void tst_QPushButton(); + void tst_QPushButtonSquare(); + void tst_QProgressBar_data(); void tst_QProgressBar(); void tst_QSpinBox_data(); void tst_QSpinBox(); + void tst_QDoubleSpinBox_data(); + void tst_QDoubleSpinBox(); + + void tst_QDateTimeEdit_data(); + void tst_QDateTimeEdit(); + + void tst_QTimeEdit_data(); + void tst_QTimeEdit(); + + void tst_QDateEdit_data(); + void tst_QDateEdit(); + void tst_QDial_data(); void tst_QDial(); @@ -65,6 +54,46 @@ private slots: void tst_QTabBar_data(); void tst_QTabBar(); + + void tst_QTabWidget_data(); + void tst_QTabWidget(); + + void tst_QListView_data(); + void tst_QListView(); + + void tst_QTableView_data(); + void tst_QTableView(); + + void tst_QTreeView_data(); + void tst_QTreeView(); + + void tst_QLineEdit_data(); + void tst_QLineEdit(); + + void tst_QMenu_data(); + void tst_QMenu(); + + void tst_QCombobox_data(); + void tst_QCombobox(); + + void tst_QCommandLinkButton_data(); + void tst_QCommandLinkButton(); + + void tst_QLCDNumber_data(); + void tst_QLCDNumber(); + +private: + + // Abstract SpinBox test for QSpinBox, QDoubleSpinBox, QDateTimeEdit, QDateEdit, QTimeEdit + void tst_SpinBox_data(); + void tst_SpinBox(QAbstractSpinBox* spinBox); + + // 78 standard icons from 6.3 + const int numberStandardIcons = 78; + + // recursive methods for QTreeView population + void tst_QTreeView_populateTree(QStandardItem* node, int height, int itemsPerNode, bool hasIcon); + QStandardItem* tst_QTreeView_populateItem(int height, int number, bool hasIcon); }; void tst_Widgets::tst_QSlider_data() @@ -158,6 +187,29 @@ void tst_Widgets::tst_QPushButton() testButton->setDown(false); } +void tst_Widgets::tst_QPushButtonSquare() +{ + QVBoxLayout layout; + + QPushButton button(testWindow()); + button.setText(QLatin1String("Square")); + const auto sizeHint = button.sizeHint().width(); + // Depending on the current QStyle, this may result in + // a different button look - on macOS it will look as + // a toolbutton: + button.setFixedSize(sizeHint, sizeHint); + + layout.addWidget(&button); + testWindow()->setLayout(&layout); + + takeStandardSnapshots(); + + button.setCheckable(true); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "square_unchecked"); + button.setChecked(true); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "square_checked"); +} + void tst_Widgets::tst_QProgressBar_data() { QTest::addColumn<Qt::Orientation>("orientation"); @@ -195,38 +247,126 @@ void tst_Widgets::tst_QProgressBar() takeStandardSnapshots(); } -void tst_Widgets::tst_QSpinBox_data() +void tst_Widgets::tst_SpinBox_data() { QTest::addColumn<QAbstractSpinBox::ButtonSymbols>("buttons"); - QTest::addRow("NoButtons") << QSpinBox::NoButtons; - QTest::addRow("UpDownArrows") << QSpinBox::UpDownArrows; - QTest::addRow("PlusMinus") << QSpinBox::PlusMinus; + QTest::addRow("NoButtons") << QAbstractSpinBox::NoButtons; + QTest::addRow("UpDownArrows") << QAbstractSpinBox::UpDownArrows; + QTest::addRow("PlusMinus") << QAbstractSpinBox::PlusMinus; } -void tst_Widgets::tst_QSpinBox() +void tst_Widgets::tst_SpinBox(QAbstractSpinBox *spinBox) { - QFETCH(const QSpinBox::ButtonSymbols, buttons); + QFETCH(const QAbstractSpinBox::ButtonSymbols, buttons); - QSpinBox *spinBox = new QSpinBox; spinBox->setButtonSymbols(buttons); spinBox->setMinimumWidth(200); - QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(spinBox); - - testWindow()->setLayout(layout); + QVBoxLayout layout; + layout.addWidget(spinBox); + testWindow()->setLayout(&layout); takeStandardSnapshots(); - // Left is default alignment: - QBASELINE_CHECK(takeSnapshot(), "align_left"); - spinBox->setAlignment(Qt::AlignHCenter); - QBASELINE_CHECK(takeSnapshot(), "align_center"); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "alignCenter"); spinBox->setAlignment(Qt::AlignRight); - QBASELINE_CHECK(takeSnapshot(), "align_right"); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "alignRight"); + + // Press / release up button + QStyleOptionSpinBox styleOption; + styleOption.initFrom(spinBox); + QPoint clickTarget = spinBox->style()->subControlRect(QStyle::CC_SpinBox,&styleOption, + QStyle::SC_SpinBoxUp,spinBox).center(); + + QTest::mousePress(spinBox, Qt::LeftButton, Qt::KeyboardModifiers(), clickTarget); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "upPressed"); + QTest::mouseRelease(spinBox, Qt::LeftButton, Qt::KeyboardModifiers(), clickTarget); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "upReleased"); + + // Press / release down button + clickTarget = spinBox->style()->subControlRect(QStyle::CC_SpinBox,&styleOption, + QStyle::SC_SpinBoxDown,spinBox).center(); + + QTest::mousePress(spinBox, Qt::LeftButton, Qt::KeyboardModifiers(), clickTarget); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "downPressed"); + QTest::mouseRelease(spinBox, Qt::LeftButton, Qt::KeyboardModifiers(), clickTarget); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "downReleased"); +} + +void tst_Widgets::tst_QSpinBox_data() +{ + tst_SpinBox_data(); +} + +void tst_Widgets::tst_QSpinBox() +{ + QSpinBox spinBox; + tst_SpinBox(&spinBox); +} + +void tst_Widgets::tst_QDoubleSpinBox_data() +{ + tst_SpinBox_data(); +} + +void tst_Widgets::tst_QDoubleSpinBox() +{ + QDoubleSpinBox spinBox; + tst_SpinBox(&spinBox); +} + +void tst_Widgets::tst_QDateTimeEdit_data() +{ + tst_SpinBox_data(); +} + +void tst_Widgets::tst_QDateTimeEdit() +{ + QDateTimeEdit edit; + tst_SpinBox(&edit); + + // show calendar popup + QStyleOptionSpinBox styleOption; + styleOption.initFrom(&edit); + const QRect buttonUp = edit.style()->subControlRect(QStyle::CC_SpinBox,&styleOption, + QStyle::SC_SpinBoxUp,&edit); + + // no rect for popup button => use bottom center of up-button + QPoint clickTarget = buttonUp.center(); + clickTarget.setY(buttonUp.bottomLeft().y()); + edit.setCalendarPopup(true); + QTest::mouseClick(&edit, Qt::LeftButton, Qt::KeyboardModifiers(), clickTarget); + QCalendarWidget* calendar = edit.calendarWidget(); + QVERIFY(calendar); + QVBoxLayout layout; + layout.addWidget(calendar); + testWindow()->setLayout(&layout); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "showCalendar"); +} + +void tst_Widgets::tst_QTimeEdit_data() +{ + tst_SpinBox_data(); +} + +void tst_Widgets::tst_QTimeEdit() +{ + QTimeEdit edit; + tst_SpinBox(&edit); +} + +void tst_Widgets::tst_QDateEdit_data() +{ + tst_SpinBox_data(); +} + +void tst_Widgets::tst_QDateEdit() +{ + QDateEdit edit; + tst_SpinBox(&edit); } void tst_Widgets::tst_QDial_data() @@ -287,8 +427,14 @@ void tst_Widgets::tst_QCheckbox() QFETCH(bool, hasIcon); QFETCH(bool, isTriState); + class CheckBox : public QCheckBox + { + public: + using QCheckBox::initStyleOption; + }; + QBoxLayout layout(QBoxLayout::TopToBottom); - QCheckBox box; + CheckBox box; box.setTristate(isTriState); if (!text.isEmpty()) @@ -301,10 +447,11 @@ void tst_Widgets::tst_QCheckbox() testWindow()->setLayout(&layout); takeStandardSnapshots(); - do - { + do { const Qt::CheckState checkState = box.checkState(); - const QPoint clickTarget = box.rect().center(); + QStyleOptionButton styleOption; + box.initStyleOption(&styleOption); + const QPoint clickTarget = box.style()->subElementRect(QStyle::SE_CheckBoxClickRect, &styleOption, &box).center(); const std::array titles = {"unChecked", "partiallyChecked", "checked"}; const QString snapShotTitle = titles[checkState]; @@ -337,7 +484,14 @@ void tst_Widgets::tst_QRadioButton() QFETCH(QString,text); QFETCH(bool,hasIcon); - QRadioButton button1(testWindow()); + class RadioButton : public QRadioButton + { + public: + using QRadioButton::QRadioButton; + using QRadioButton::initStyleOption; + }; + + RadioButton button1(testWindow()); if (!text.isEmpty()) button1.setText(text); @@ -347,7 +501,7 @@ void tst_Widgets::tst_QRadioButton() button1.setChecked(false); - QRadioButton button2(testWindow()); + RadioButton button2(testWindow()); if (!text.isEmpty()) button2.setText(text); @@ -364,7 +518,10 @@ void tst_Widgets::tst_QRadioButton() testWindow()->setLayout(&box); takeStandardSnapshots(); - const QPoint clickTarget = button1.rect().center(); + QStyleOptionButton styleOption; + button1.initStyleOption(&styleOption); + const QPoint clickTarget = button1.style()->subElementRect(QStyle::SE_RadioButtonClickRect, &styleOption, &button1).center(); + QTest::mousePress(&button1,Qt::MouseButton::LeftButton, Qt::KeyboardModifiers(), clickTarget,0); QVERIFY(button1.isDown()); QBASELINE_CHECK_DEFERRED(takeSnapshot(), "pressUnchecked"); @@ -459,13 +616,14 @@ void tst_Widgets::tst_QTabBar_data() QTest::addColumn<QTabBar::Shape>("shape"); QTest::addColumn<int>("numberTabs"); QTest::addColumn<int>("fixedWidth"); + QTest::addColumn<bool>("isClosable"); // fixedWidth <0 will be interpreted as variable width - QTest::newRow("RoundedNorth_3_variableWidth") << QTabBar::RoundedNorth << 3 << -1; - QTest::newRow("RoundedEast_3_variableWidth") << QTabBar::RoundedEast << 3 << -1; - QTest::newRow("RoundedWest_3_variableWidth") << QTabBar::RoundedWest << 3 << -1; - QTest::newRow("RoundedSouth_3_variableWidth") << QTabBar::RoundedSouth << 3 << -1; - QTest::newRow("RoundedNorth_20_fixedWidth") << QTabBar::RoundedNorth << 20 << 250; + QTest::newRow("RoundedNorth_3_variableWidth") << QTabBar::RoundedNorth << 3 << -1 << false; + QTest::newRow("RoundedEast_3_variableWidth") << QTabBar::RoundedEast << 3 << -1 << false; + QTest::newRow("RoundedWest_3_variableWidth") << QTabBar::RoundedWest << 3 << -1 << false; + QTest::newRow("RoundedSouth_3_variableWidth") << QTabBar::RoundedSouth << 3 << -1 << false; + QTest::newRow("RoundedNorth_20_fixedWidth") << QTabBar::RoundedNorth << 20 << 250 << true; } void tst_Widgets::tst_QTabBar() @@ -473,9 +631,11 @@ void tst_Widgets::tst_QTabBar() QFETCH(QTabBar::Shape, shape); QFETCH(int, numberTabs); QFETCH(int, fixedWidth); + QFETCH(bool, isClosable); QTabBar bar (testWindow()); bar.setShape(shape); + bar.setTabsClosable(isClosable); if (fixedWidth > 0) bar.setFixedWidth(fixedWidth); @@ -505,6 +665,614 @@ void tst_Widgets::tst_QTabBar() QTest::mouseRelease(&bar,Qt::MouseButton::LeftButton, Qt::KeyboardModifiers(), clickTarget,0); QVERIFY(bar.currentIndex() == 1); } + + // test press/release on close button + if (isClosable) { + + // CloseButton is either left or right + QWidget *leftButton = bar.tabButton(bar.currentIndex(),QTabBar::ButtonPosition::LeftSide); + QWidget *rightButton = bar.tabButton(bar.currentIndex(),QTabBar::ButtonPosition::RightSide); + QAbstractButton *button = qobject_cast<QAbstractButton*>(leftButton); + if (button == nullptr) + button = qobject_cast<QAbstractButton*>(rightButton); + + if (button != nullptr) { + clickTarget = button->rect().center(); + QTest::mousePress(button,Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "pressCloseFirstTab"); + QTest::mouseRelease(button,Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "releaseCloseFirstTab"); + } + } +} + +void tst_Widgets::tst_QTabWidget_data() +{ + QTest::addColumn<QTabWidget::TabPosition>("tabPosition"); + QTest::addColumn<int>("numberTabs"); + QTest::addColumn<QString>("tabText"); + QTest::addColumn<int>("fixedWidth"); + QTest::addColumn<bool>("isClosable"); + QTest::addColumn<bool>("isDocumentMode"); + + // fixedWidth <0 will be interpreted as variable width + QTest::newRow("North_3_variableWidthDocMode") << QTabWidget::North << 3 << "This is a tab text" << -1 << false << true; + QTest::newRow("East_3_variableWidth") << QTabWidget::East << 3 << "This is a tab text" << -1 << false << false; + QTest::newRow("West_3_variableWidthDocMode") << QTabWidget::West << 3 << "This is a tab text" << -1 << false << true; + QTest::newRow("South_3_variableWidth") << QTabWidget::South << 3 << "This is a tab text" << -1 << true << false; + QTest::newRow("North_20_fixedWidthDocMode") << QTabWidget::North << 20 + << "This is a very long text to actually force wrapping!" << 100 << true << true; + QTest::newRow("South_20_variableWidth") << QTabWidget::South << 20 + << "This is a very long text to actually force wrapping!" << -1 << false << false; +} + +void tst_Widgets::tst_QTabWidget() +{ + QFETCH(QTabWidget::TabPosition, tabPosition); + QFETCH(int, numberTabs); + QFETCH(QString, tabText); + QFETCH(int, fixedWidth); + QFETCH(bool, isClosable); + QFETCH(bool, isDocumentMode); + + QTabWidget tabWidget (testWindow()); + if (fixedWidth > 0) + tabWidget.setFixedWidth(fixedWidth); + tabWidget.setTabPosition(tabPosition); + tabWidget.setTabsClosable(isClosable); + tabWidget.setDocumentMode(isDocumentMode); + + for (int i = 0; i < numberTabs; ++i) { + QLabel *tabLabel = new QLabel("Tab number " + QString::number(i) + "\n" + tabText, &tabWidget); + QBoxLayout *tabBox = new QBoxLayout(QBoxLayout::TopToBottom,&tabWidget); + tabBox->addWidget(tabLabel); + tabWidget.insertTab(i,tabLabel,"Tab_" + QString::number(i)); + tabWidget.setCurrentIndex(i); + tabWidget.currentWidget()->setLayout(tabBox); + } + + tabWidget.setCurrentIndex(0); + QBoxLayout box(QBoxLayout::LeftToRight, testWindow()); + box.addWidget(&tabWidget); + testWindow()->setLayout(&box); + takeStandardSnapshots(); + + // press/release on second tab if it exists + if (numberTabs > 1) { + const QPoint clickTarget = tabWidget.tabBar()->tabRect(1).center(); + QTest::mousePress(tabWidget.tabBar(),Qt::MouseButton::LeftButton,Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "pressSecondTab"); + QTest::mouseRelease(tabWidget.tabBar(),Qt::MouseButton::LeftButton, Qt::KeyboardModifiers(), clickTarget,0); + QVERIFY(tabWidget.currentIndex() == 1); + } + + // test press/release on close button + if (isClosable) { + + // CloseButton is either left or right + QWidget *leftButton = tabWidget.tabBar()->tabButton(tabWidget.currentIndex(),QTabBar::ButtonPosition::LeftSide); + QWidget *rightButton = tabWidget.tabBar()->tabButton(tabWidget.currentIndex(),QTabBar::ButtonPosition::RightSide); + QAbstractButton *button = qobject_cast<QAbstractButton*>(leftButton); + if (button == nullptr) + button = qobject_cast<QAbstractButton*>(rightButton); + + if (button != nullptr) { + const QPoint clickTarget = button->rect().center(); + QTest::mousePress(button,Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "pressCloseTab"); + QTest::mouseRelease(button,Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "releaseCloseTab"); + } + } +} + +void tst_Widgets::tst_QListView_data() +{ + QTest::addColumn<QListView::ViewMode>("viewMode"); + QTest::addColumn<bool>("isWrapping"); + QTest::addColumn<bool>("hasWordWrap"); + QTest::addColumn<int>("numberItems"); + QTest::addColumn<QSize>("fixedSize"); + + + // QSize() will be interpreted as variable size + QTest::newRow("ListModeWrappingNoWordWrapFixed_10") << + QListView::ListMode << true << false << 10 << QSize(100, 500); + QTest::newRow("ListModeNoWrappingNoWordWrapVariable_20") << + QListView::ListMode << false << true << 20 << QSize(); + QTest::newRow("ListModeNoWrappingWordWrapVariable_30") << + QListView::ListMode << false << true << 30 << QSize(); + QTest::newRow("IconModeNoWrappingNoWordWrapFixed_10") << + QListView::IconMode << false << false << 10 << QSize(100, 500); + QTest::newRow("IconModeWrappingNoWordWrapVariable_20") << + QListView::IconMode << true << false << 20 << QSize(); + QTest::newRow("IconModeWrappingWordWrapVariable_30") << + QListView::IconMode << true << true << 30 << QSize(100, 500); +} +void tst_Widgets::tst_QListView() +{ + QFETCH(QListView::ViewMode,viewMode); + QFETCH(bool,isWrapping); + QFETCH(bool,hasWordWrap); + QFETCH(int,numberItems); + QFETCH(QSize,fixedSize); + + QListView listView; + listView.setViewMode(viewMode); + listView.setWrapping(isWrapping); + listView.setWordWrap(hasWordWrap); + if (fixedSize.isValid()) + listView.setFixedSize(fixedSize); + + QStandardItemModel model(0,1,testWindow()); + + // Populate model, add standard icons if required + const QString itemText = hasWordWrap ? "This is a long text for word wrapping Item_" + : "ListItem_"; + int icon = 0; + for (int i = 0; i < numberItems; ++i) { + QStandardItem *item; + if (viewMode == QListView::IconMode) { + item = new QStandardItem(QApplication::style()->standardIcon + (static_cast<QStyle::StandardPixmap>(icon)), itemText + QString::number(i)); + icon = (icon + 1) % numberStandardIcons; + } else { + item = new QStandardItem(itemText + QString::number(i)); + } + model.appendRow(item); + } + + listView.setModel(&model); + QBoxLayout layout(QBoxLayout::LeftToRight, testWindow()); + layout.addWidget(&listView); + testWindow()->setLayout(&layout); + takeStandardSnapshots(); + + // click on first item + QPoint clickTarget = listView.visualRect(model.index(0,0)).center(); + QTest::mouseClick(listView.viewport(),Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "clickFirstItem"); + + // click on scond item + if (numberItems > 1) { + clickTarget = listView.visualRect(model.index(1,0)).center(); + QTest::mouseClick(listView.viewport(),Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "clickSecondItem"); + } + + // Hide first row + listView.setRowHidden(0,true); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "hideFirstItem"); +} + +void tst_Widgets::tst_QTableView_data() +{ + QTest::addColumn<bool>("hasHeader"); + QTest::addColumn<bool>("hasRowNumbers"); + QTest::addColumn<bool>("hasWordWrap"); + QTest::addColumn<int>("numberRows"); + QTest::addColumn<int>("numberColumns"); + QTest::addColumn<int>("iconColumn"); + QTest::addColumn<QSize>("fixedSize"); + + // QSize() => variable size; iconColumn -1 => no icon + QTest::newRow("HeaderRowNumWordWrapFixed_10") << true << true << true << 10 << 3 << -1 << QSize(500, 100); + QTest::newRow("HeaderVariable_20") << true << false << false << 20 << 4 << 1 << QSize(); + QTest::newRow("HeaderFixed_20") << true << false << false << 20 << 4 << 1 << QSize(500, 700); +} + +void tst_Widgets::tst_QTableView() +{ + QFETCH(bool, hasHeader); + QFETCH(bool, hasRowNumbers); + QFETCH(bool, hasWordWrap); + QFETCH(int, numberRows); + QFETCH(int, numberColumns); + QFETCH(int, iconColumn); + QFETCH(QSize, fixedSize); + + // Populate model + int icon = 0; + QStandardItemModel model(numberRows, numberColumns, testWindow()); + + if (hasHeader) { + for (int i = 0; i < numberColumns; ++i) + model.setHorizontalHeaderItem(i, new QStandardItem("Header_" + QString::number(i))); + } + + const QString wrap = hasWordWrap ? "\n long text to wrap words" : "" ; + for (int row = 0; row < numberRows; ++row) { + for (int column = 0; column < numberColumns; ++column) { + QStandardItem *item; + const QString itemText = QString::number(row) + "/" + QString::number(column) + wrap; + if (iconColumn == column) { + item = new QStandardItem(QApplication::style()->standardIcon + (static_cast<QStyle::StandardPixmap>(icon)),itemText); + + icon = (icon + 1) % numberStandardIcons; + } else { + item = new QStandardItem(itemText); + } + model.setItem(row,column,item); + } + if (hasRowNumbers) + model.setVerticalHeaderItem(row, new QStandardItem(QString::number(row))); + } + + QTableView tableView(testWindow()); + tableView.setWordWrap(hasWordWrap); + if (fixedSize.isValid()) + tableView.setFixedSize(fixedSize); + + QBoxLayout layout(QBoxLayout::LeftToRight, testWindow()); + tableView.setModel(&model); + layout.addWidget(&tableView); + + takeStandardSnapshots(); + + // Hide grid + tableView.setShowGrid(false); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "hideGrid"); + tableView.setShowGrid(true); + + // click item 0,0 + QPoint clickTarget = tableView.visualRect(model.index(0,0)).center(); + QTest::mouseClick(tableView.viewport(),Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "clickFirstItem"); + + // click item 0,1 if it exists + if (numberColumns > 1) { + clickTarget = tableView.visualRect(model.index(0,1)).center(); + QTest::mouseClick(tableView.viewport(),Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget,0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "clickSecondItem"); + } + + tableView.clearSelection(); + + // Hide first row and column + tableView.setRowHidden(0, true); + tableView.setColumnHidden(0, true); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "hideFirstRowColumn"); + tableView.setRowHidden(0, false); + tableView.setColumnHidden(0, false); + + // Select first row + tableView.selectRow(0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "selectFirstRow"); + + // Select first column + tableView.selectColumn(0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "selectFirstColumn"); +} + +void tst_Widgets::tst_QTreeView_data() +{ + QTest::addColumn<bool>("showHeader"); + QTest::addColumn<bool>("hasIcons"); + QTest::addColumn<bool>("alternatingRowColors"); + QTest::addColumn<QSize>("fixedSize"); + QTest::addColumn<int>("treeHeight"); + QTest::addColumn<int>("itemsPerNode"); + + // QSize() => variable size + QTest::newRow("HeaderIcons_4_3") << true << true << false << QSize() << 3 << 2; + QTest::newRow("NoHeaderNoIcons_4_4") << false << false << false << QSize(100, 350) << 3 << 2; + QTest::newRow("AlternatingRows") << true << true << true << QSize() << 3 << 2; +} + +void tst_Widgets::tst_QTreeView() +{ + QFETCH(bool, showHeader); + QFETCH(bool, hasIcons); + QFETCH(bool, alternatingRowColors); + QFETCH(QSize, fixedSize); + QFETCH(int, treeHeight); + QFETCH(int, itemsPerNode); + QVERIFY(treeHeight > 0 && itemsPerNode > 0); + + QTreeView treeView(testWindow()); + fixedSize.isValid() ? treeView.setFixedSize(fixedSize) + : treeView.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + + QStandardItemModel model(&treeView); + showHeader ? model.setHorizontalHeaderItem(0, new QStandardItem("TreeHeader")) + : treeView.setHeaderHidden(true); + + treeView.setAlternatingRowColors(alternatingRowColors); + + // Populate tree model + for (int i = 0; i < itemsPerNode; ++i) { + QStandardItem* root = tst_QTreeView_populateItem(treeHeight, i, hasIcons); + tst_QTreeView_populateTree(root,treeHeight - 1,itemsPerNode, hasIcons); + model.appendRow(root); + } + + treeView.setModel(&model); + QBoxLayout layout(QBoxLayout::LeftToRight, testWindow()); + layout.addWidget(&treeView); + testWindow()->setLayout(&layout); + + treeView.expandAll(); + treeView.resizeColumnToContents(0); + takeStandardSnapshots(); + + // Partly expand if possible + if (treeHeight > 1) { + treeView.expandToDepth(1); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "partlyExpanded"); + } + + // Click on first node + QPoint clickTarget = treeView.visualRect(model.index(0, 0)).center(); + QTest::mouseClick(treeView.viewport(),Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget, 0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "clickFirstNode"); + + // Hide first row + treeView.setRowHidden(0, model.index(0, 0), true); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "hideFirstRow"); + treeView.setRowHidden(0, model.index(0, 0), false); + + // Click on second row if it exists + if (itemsPerNode > 1) { + clickTarget = treeView.visualRect(model.index(1, 0)).center(); + QTest::mouseClick(treeView.viewport(), Qt::MouseButton::LeftButton, + Qt::KeyboardModifiers(), clickTarget, 0); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "clickSecondNode"); + } +} + +void tst_Widgets::tst_QTreeView_populateTree(QStandardItem* node, int height, int itemsPerNode, bool hasIcon) +{ + QList<QStandardItem*> items; + for (int i = 0; i < itemsPerNode; ++i) { + if (height == 0) { + items.append(tst_QTreeView_populateItem(height, i, hasIcon)); + } else { + QStandardItem* item = tst_QTreeView_populateItem(height, i, hasIcon); + tst_QTreeView_populateTree(item, height - 1, itemsPerNode, hasIcon); + items.append(item); + } + } + return node->appendColumn(items); +} + +QStandardItem* tst_Widgets::tst_QTreeView_populateItem(int height, int number, bool hasIcon) +{ + static int icon = 0; + static int itemCount = 0; + + QStandardItem* item; + const QString itemText = QString("%1/%2/%3").arg(height).arg(number).arg(itemCount); + ++itemCount; + + if (hasIcon) { + item = new QStandardItem(QApplication::style()->standardIcon + (static_cast<QStyle::StandardPixmap>(icon)), itemText); + + icon = (icon + 1) % numberStandardIcons; + } else { + item = new QStandardItem(itemText); + } + return item; +} + +void tst_Widgets::tst_QLineEdit_data() +{ + QTest::addColumn<bool>("hasFrame"); + QTest::addColumn<QLineEdit::EchoMode>("echoMode"); + QTest::addColumn<QString>("placeHolderText"); + QTest::addColumn<QString>("text"); + + QTest::newRow("framePassword") << true << QLineEdit::Password << "password" << "secret"; + QTest::newRow("noFrameCleartext") << false << QLineEdit::Normal << "text" << "this is a text"; +} + +void tst_Widgets::tst_QLineEdit() +{ + QFETCH(const bool, hasFrame); + QFETCH(const QLineEdit::EchoMode, echoMode); + QFETCH(const QString, placeHolderText); + QFETCH(const QString, text); + + QLineEdit lineEdit(testWindow()); + lineEdit.setFrame(hasFrame); + lineEdit.setEchoMode(echoMode); + lineEdit.setPlaceholderText(placeHolderText); + + QHBoxLayout layout; + layout.addWidget(&lineEdit); + testWindow()->setLayout(&layout); + takeStandardSnapshots(); + + lineEdit.setText(text); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "setText"); + + lineEdit.setAlignment(Qt::AlignRight); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "alignedRight"); + + lineEdit.setAlignment(Qt::AlignCenter); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "alignedCenter"); + + lineEdit.setSelection(0,text.size()); + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "textSelected"); +} + +void tst_Widgets::tst_QMenu_data() +{ + QTest::addColumn<QStringList>("actions"); + + const QStringList menu1 = {"Text", "", "TextAndIcon", "", "SubMenu", "", "Checked"}; + QTest::newRow("showMenuPopup") << menu1; +} + +void tst_Widgets::tst_QMenu() +{ + QFETCH(const QStringList, actions); + + testWindow()->resize(300, 200); + + QBoxLayout layout(QBoxLayout::TopToBottom); + QMenu menu1; + + for (const auto& menuItem : actions) { + if (!menuItem.isEmpty()) { + if (menuItem == "Text") { + menu1.addAction(QString("MenuItem")); + menu1.addAction(QString("")); + } else if (menuItem == "TextAndIcon") { + // Using pixmap icon + QPixmap pix(10, 10); + pix.fill(Qt::green); + menu1.addAction(QIcon(pix), QString("MenuWithIcon")); + menu1.addAction(QIcon(), QString("MenuNoIcon")); + } else if (menuItem == "SubMenu") { + QMenu* submenu = menu1.addMenu(QString("&Submenu1")); + submenu->addAction("SubMenuA"); + submenu->addAction("SubMenuB"); + } else if (menuItem == "Checked") { + auto checked = menu1.addAction(QString("MenuChecked")); + checked->setCheckable(true); + checked->setChecked(true); + auto notChecked = menu1.addAction(QString("MenuNotChecked")); + notChecked->setCheckable(true); + notChecked->setChecked(false); + } + } else { + menu1.addSeparator(); + } + } + + layout.addWidget(&menu1); + testWindow()->setLayout(&layout); + + testWindow()->show(); + QVERIFY(QTest::qWaitForWindowExposed(testWindow())); + QRect testWindowRect(testWindow()->geometry()); + // There can be rounded corners in the window and this leads to test + // case to be fuzzy. Adjust window rectangle that need to be captured + int adjustPixel = menu1.geometry().left(); + testWindowRect.adjust(adjustPixel, adjustPixel, -adjustPixel, -adjustPixel); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "showitems"); + + // Normal menu item with text + QTest::keyClick(&menu1, Qt::Key_Down); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "selectmenutext"); + QTest::keyClick(&menu1, Qt::Key_Down); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "selectmenunotext"); + + // Menu with icon and text + QTest::keyClick(&menu1, Qt::Key_Down); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "selectmenuwithicon"); + QTest::keyClick(&menu1, Qt::Key_Down); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "selectmenuwithnullicon"); + + // Sub-menu items + QTest::keyClick(&menu1, Qt::Key_Down); + QTest::keyClick(&menu1, Qt::Key_Right); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "selectsubmenu"); + QTest::keyClick(&menu1, Qt::Key_Left); + + // Checked menu + QTest::keyClick(&menu1, Qt::Key_Down); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "selectmenuchecked"); + QTest::keyClick(&menu1, Qt::Key_Down); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindowRect), "selectmenunotchecked"); +} + +void tst_Widgets::tst_QCombobox_data() +{ + QTest::addColumn<bool>("hasFrame"); + QTest::addColumn<bool>("isEditable"); + + QTest::addRow("frameNonEditable") << true << false; + QTest::addRow("frameEditable") << true << true; + QTest::addRow("noFrameNonEditable") << false << false; + QTest::addRow("noFrameEditable") << false << true; +} + +void tst_Widgets::tst_QCombobox() +{ + QFETCH(const bool, hasFrame); + QFETCH(const bool, isEditable); + + testWindow()->resize(300, 300); + + QScopedPointer<QComboBox> combobox(new QComboBox(testWindow())); + QStringList items; + items << tr("Item1") << tr("Item2") << tr("Item3"); + QStringListModel* itemModel = new QStringListModel(items, this); + combobox->setModel(itemModel); + combobox->setFrame(hasFrame); + combobox->setEditable(isEditable); + + QHBoxLayout layout; + layout.addWidget(combobox.get()); + testWindow()->setLayout(&layout); + takeStandardSnapshots(); + + QTest::keyClick(combobox.get(), Qt::Key_Down, Qt::AltModifier); + QBASELINE_CHECK_DEFERRED(takeScreenSnapshot(testWindow()->geometry()), "combobox"); +} + +void tst_Widgets::tst_QCommandLinkButton_data() +{ + QTest::addColumn<bool>("flat"); + QTest::addColumn<QString>("description"); + + QTest::addRow("flatDescription") << true << QString("Command button very specific to windows vista"); + QTest::addRow("flatNoDescription") << true << QString(""); + QTest::addRow("noFlatNoDescription") << false << QString(""); +} + +void tst_Widgets::tst_QCommandLinkButton() +{ + QFETCH(const bool, flat); + QFETCH(const QString, description); + + QScopedPointer<QCommandLinkButton> commandLink(new QCommandLinkButton(QString("CommandLink"), description, testWindow())); + commandLink->setFlat(flat); + commandLink->setDescription(description); + + QHBoxLayout layout; + layout.addWidget(commandLink.get()); + testWindow()->setLayout(&layout); + takeStandardSnapshots(); +} + +void tst_Widgets::tst_QLCDNumber_data() +{ + QTest::addColumn<int>("segmentstyle"); + + QTest::addRow("outline") << 0; + QTest::addRow("filled") << 1; + QTest::addRow("flat") << 2; +} + +void tst_Widgets::tst_QLCDNumber() +{ + QFETCH(const int, segmentstyle); + + testWindow()->resize(100, 100); + + QScopedPointer<QLCDNumber> lcdNumber(new QLCDNumber(99, testWindow())); + lcdNumber->setHexMode(); + lcdNumber->setSegmentStyle(static_cast<QLCDNumber::SegmentStyle>(segmentstyle)); + + + QHBoxLayout layout; + layout.addWidget(lcdNumber.get()); + testWindow()->setLayout(&layout); + + QBASELINE_CHECK_DEFERRED(takeSnapshot(), "lcdnumber"); } #define main _realmain @@ -513,7 +1281,8 @@ QTEST_MAIN(tst_Widgets) int main(int argc, char *argv[]) { - qSetGlobalQHashSeed(0); // Avoid rendering variations caused by QHash randomization + // Avoid rendering variations caused by QHash randomization + QHashSeed::setDeterministicGlobalSeed(); QBaselineTest::handleCmdLineArgs(&argc, &argv); return _realmain(argc, argv); |