File indexing completed on 2024-05-12 04:44:23

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 // First included header is the public header of the class we are testing;
0005 // this forces the header to be self-contained.
0006 #include "colordialog.h"
0007 // Second, the private implementation.
0008 #include "colordialog_p.h" // IWYU pragma: keep
0009 
0010 #include "absolutecolor.h"
0011 #include "chromahuediagram.h"
0012 #include "colorpatch.h"
0013 #include "constpropagatinguniquepointer.h"
0014 #include "genericcolor.h"
0015 #include "gradientslider.h"
0016 #include "helperconversion.h"
0017 #include "helperqttypes.h"
0018 #include "initializetranslation.h"
0019 #include "lchadouble.h"
0020 #include "lchdouble.h"
0021 #include "multispinbox.h"
0022 #include "rgbcolor.h"
0023 #include "rgbcolorspace.h"
0024 #include "rgbcolorspacefactory.h"
0025 #include "settranslation.h"
0026 #include "wheelcolorpicker.h"
0027 #include <qaction.h>
0028 #include <qapplication.h>
0029 #include <qbenchmark.h>
0030 #include <qbytearray.h>
0031 #include <qcolor.h>
0032 #include <qcolordialog.h>
0033 #include <qcoreapplication.h>
0034 #include <qcoreevent.h>
0035 #include <qdebug.h>
0036 #include <qglobal.h>
0037 #include <qhash.h>
0038 #include <qlineedit.h>
0039 #include <qlist.h>
0040 #include <qlocale.h>
0041 #include <qmetaobject.h>
0042 #include <qnamespace.h>
0043 #include <qobject.h>
0044 #include <qpair.h>
0045 #include <qpointer.h>
0046 #include <qpushbutton.h>
0047 #include <qscopedpointer.h>
0048 #include <qsharedpointer.h>
0049 #include <qsignalspy.h>
0050 #include <qspinbox.h>
0051 #include <qstringbuilder.h>
0052 #include <qstringliteral.h>
0053 #include <qtabwidget.h>
0054 #include <qtemporaryfile.h>
0055 #include <qtest.h>
0056 #include <qtestcase.h>
0057 #include <qtestdata.h>
0058 #include <qtestkeyboard.h>
0059 #include <qwidget.h>
0060 #include <type_traits>
0061 
0062 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0063 #include <qcontainerfwd.h>
0064 #include <qobjectdefs.h>
0065 #include <qstring.h>
0066 #include <qtmetamacros.h>
0067 #include <utility>
0068 #else
0069 #include <qobjectdefs.h>
0070 #include <qstring.h>
0071 #include <qstringlist.h>
0072 #include <qvector.h>
0073 #include <utility>
0074 #endif
0075 
0076 // From Qt documentation:
0077 //     “Note: This function is not declared in any of Qt's header files. To
0078 //      use it in your application, declare the function prototype before
0079 //      calling it.”
0080 void qt_set_sequence_auto_mnemonic(bool b);
0081 
0082 class TestColorDialogSnippetClass : public QWidget
0083 {
0084     Q_OBJECT
0085 public Q_SLOTS:
0086     void mySlot(QColor color)
0087     {
0088         Q_UNUSED(color)
0089     }
0090 
0091 public:
0092     // A constructor that is clazy-conform
0093     explicit TestColorDialogSnippetClass(QWidget *parent = nullptr)
0094         : QWidget(parent)
0095     {
0096     }
0097 
0098     void testSnippet05()
0099     {
0100         //! [ColorDialog Open]
0101         PerceptualColor::ColorDialog *m_dialog = //
0102             new PerceptualColor::ColorDialog( //
0103                 PerceptualColor::RgbColorSpaceFactory::createSrgb());
0104         m_dialog->open(this, SLOT(mySlot(QColor)));
0105         //! [ColorDialog Open]
0106         delete m_dialog;
0107     }
0108 };
0109 
0110 static void snippet01()
0111 {
0112     // This function will not be called in the unit tests because getColor()
0113     // does not return without user interaction!
0114     //! [ColorDialog Get color with alpha channel]
0115     QColor myColor = PerceptualColor::ColorDialog::getColor(
0116         // Current color at widget startup:
0117         Qt::green,
0118         // Parent widget (or nullptr for no parent):
0119         nullptr,
0120         // Window title (or an empty string for default title):
0121         QStringLiteral("Window title"),
0122         // Options:
0123         PerceptualColor::ColorDialog::ColorDialogOption::ShowAlphaChannel);
0124     //! [ColorDialog Get color with alpha channel]
0125     Q_UNUSED(myColor)
0126 }
0127 
0128 static void snippet02()
0129 {
0130     //! [setOptionsWithLocalEnum]
0131     auto myDialog = new PerceptualColor::ColorDialog();
0132     myDialog->setOption( //
0133         PerceptualColor::ColorDialog::ColorDialogOption::ShowAlphaChannel,
0134         false);
0135     //! [setOptionsWithLocalEnum]
0136     QCOMPARE( //
0137         myDialog->testOption( //
0138             PerceptualColor::ColorDialog::ColorDialogOption::ShowAlphaChannel), //
0139         false);
0140     delete myDialog;
0141 }
0142 
0143 static void snippet03()
0144 {
0145     //! [setOptionsWithQColorDialogEnum]
0146     auto myDialog = new PerceptualColor::ColorDialog();
0147     myDialog->setOption(QColorDialog::ShowAlphaChannel, false);
0148     //! [setOptionsWithQColorDialogEnum]
0149     QCOMPARE( //
0150         myDialog->testOption( //
0151             PerceptualColor::ColorDialog::ColorDialogOption::ShowAlphaChannel), //
0152         false);
0153     delete myDialog;
0154 }
0155 
0156 static void snippet04()
0157 {
0158     // This function will not be called in the unit tests because getColor()
0159     // does not return without user interaction!
0160     //! [ColorDialog Get color]
0161     // Show a modal color dialog and get the color that the user has chosen
0162     QColor myColor = PerceptualColor::ColorDialog::getColor();
0163     //! [ColorDialog Get color]
0164     Q_UNUSED(myColor)
0165 }
0166 
0167 namespace PerceptualColor
0168 {
0169 class TestColorDialog : public QObject
0170 {
0171     Q_OBJECT
0172 
0173 public:
0174     explicit TestColorDialog(QObject *parent = nullptr)
0175         : QObject(parent)
0176     {
0177     }
0178 
0179 private:
0180     QScopedPointer<ColorDialog> m_perceptualDialog;
0181     QScopedPointer<ColorDialog> m_perceptualDialog2;
0182     QScopedPointer<QColorDialog> m_qDialog;
0183     QScopedPointer<QColorDialog> m_qDialog2;
0184     QColor m_color;
0185     QSharedPointer<RgbColorSpace> m_srgbBuildinColorSpace = //
0186         RgbColorSpaceFactory::createSrgb();
0187 
0188     static void voidMessageHandler(QtMsgType, const QMessageLogContext &, const QString &)
0189     {
0190         // dummy message handler that does not print messages
0191     }
0192 
0193     void helperProvideQColors()
0194     {
0195         // suppress warning for generating invalid QColor
0196         qInstallMessageHandler(voidMessageHandler);
0197 
0198         QTest::addColumn<QColor>("color");
0199 
0200         QTest::newRow("RGB 1 2 3") //
0201             << QColor(1, 2, 3);
0202         QTest::newRow("RGBA 1 2 3 4") //
0203             << QColor(1, 2, 3, 4);
0204         QTest::newRow("RGB 1 2 300") //
0205             << QColor(1, 2, 300);
0206         QTest::newRow("RGB 1 2 -300") //
0207             << QColor(1, 2, -300);
0208         QTest::newRow("RGBA 1 2 300 4") //
0209             << QColor(1, 2, 300, 4);
0210         QTest::newRow("RGBA 1 2 3 400") //
0211             << QColor(1, 2, 3, 400);
0212         QTest::newRow("RGBA 1 2 3 -400") //
0213             << QColor(1, 2, 3, -400);
0214 
0215         QTest::newRow("RGB 0.1 0.2 0.3") //
0216             << QColor::fromRgbF(0.1f, 0.2f, 0.3f);
0217         QTest::newRow("RGBA 0.1 0.2 0.3 0.4") //
0218             << QColor::fromRgbF(0.1f, 0.2f, 0.3f, 0.4f);
0219         QTest::newRow("RGB 0.1 6.2 0.300") //
0220             << QColor::fromRgbF(0.1f, 6.2f, 0.300f);
0221         QTest::newRow("RGBA 0.1 6.2 0.300 0.4") //
0222             << QColor::fromRgbF(0.1f, 6.2f, 0.300f, 0.4f);
0223         QTest::newRow("RGBA 0.1 0.2 0.3 -0.4") //
0224             << QColor::fromRgbF(0.1f, 0.2f, 0.3f, -0.4f);
0225         QTest::newRow("RGBA 0.1 0.2 0.3 400") //
0226             << QColor::fromRgbF(0.1f, 0.2f, 0.3f, 400);
0227 
0228         QTest::newRow("CMYK 1 2 3 4") //
0229             << QColor::fromCmyk(1, 2, 3, 4);
0230         QTest::newRow("CMYK 1 2 3 4 5") //
0231             << QColor::fromCmyk(1, 2, 3, 4, 5);
0232         QTest::newRow("CMYK 1 2 300 4") //
0233             << QColor::fromCmyk(1, 2, 300, 4);
0234         QTest::newRow("CMYK 1 2 300 4 5") //
0235             << QColor::fromCmyk(1, 2, 300, 4, 5);
0236         QTest::newRow("CMYK 0.1 0.2 0.300 0.4") //
0237             << QColor::fromCmykF(0.1f, 0.2f, 0.300f, 0.4f);
0238         QTest::newRow("CMYK 0.1 0.2 0.300 0.4 0.6495217645") //
0239             << QColor::fromCmykF(0.1f, 0.2f, 0.300f, 0.4f, 0.6495217645f);
0240         QTest::newRow("CMYK 0.1 6.2 0.300 0.4") //
0241             << QColor::fromCmykF(0.1f, 6.2f, 0.300f, 0.4f);
0242         QTest::newRow("CMYK 0.1 -6.2 0.300 0.4") //
0243             << QColor::fromCmykF(0.1f, -6.2f, 0.300f, 0.4f);
0244         QTest::newRow("CMYK 0.1 6.2 0.300 0.4 0.6495217645") //
0245             << QColor::fromCmykF(0.1f, 6.2f, 0.300f, 0.4f, 0.6495217645f);
0246         QTest::newRow("CMYK 0.1 6.2 0.300 0.4 -0.6495217645") //
0247             << QColor::fromCmykF(0.1f, 6.2f, 0.300f, 0.4f, -0.6495217645f);
0248 
0249         QTest::newRow("HSL 2 3 4") //
0250             << QColor::fromHsl(2, 3, 4);
0251         QTest::newRow("HSL 2 3 4 5") //
0252             << QColor::fromHsl(2, 3, 4, 5);
0253         QTest::newRow("HSL 2 300 4") //
0254             << QColor::fromHsl(2, 300, 4);
0255         QTest::newRow("HSL 2 300 4 5") //
0256             << QColor::fromHsl(2, 300, 4, 5);
0257         QTest::newRow("HSL 0.2 0.300 0.4") //
0258             << QColor::fromHslF(0.2f, 0.300f, 0.4f);
0259         QTest::newRow("HSL 0.2 0.300 0.4 0.6495217645") //
0260             << QColor::fromHslF(0.2f, 0.300f, 0.4f, 0.6495217645f);
0261         QTest::newRow("HSL 6.2 0.300 0.4") //
0262             << QColor::fromHslF(6.2f, 0.300f, 0.4f);
0263         QTest::newRow("HSL -6.2 0.300 0.4") //
0264             << QColor::fromHslF(-6.2f, 0.300f, 0.4f);
0265         QTest::newRow("HSL 6.2 0.300 0.4 0.6495217645") //
0266             << QColor::fromHslF(6.2f, 0.300f, 0.4f, 0.6495217645f);
0267         QTest::newRow("HSL 6.2 0.300 0.4 -0.6495217645") //
0268             << QColor::fromHslF(6.2f, 0.300f, 0.4f, -0.6495217645f);
0269         QTest::newRow("HSL 6.2 0.300 0.4 1.6495217645") //
0270             << QColor::fromHslF(6.2f, 0.300f, 0.4f, 1.6495217645f);
0271 
0272         QTest::newRow("HSV 2 3 4") //
0273             << QColor::fromHsv(2, 3, 4);
0274         QTest::newRow("HSV 2 3 4 5") //
0275             << QColor::fromHsv(2, 3, 4, 5);
0276         QTest::newRow("HSV 2 300 4") //
0277             << QColor::fromHsv(2, 300, 4);
0278         QTest::newRow("HSV 2 300 4 5") //
0279             << QColor::fromHsv(2, 300, 4, 5);
0280         QTest::newRow("HSV 0.2 0.300 0.4") //
0281             << QColor::fromHsvF(0.2f, 0.300f, 0.4f);
0282         QTest::newRow("HSV 0.2 0.300 0.4 0.6495217645") //
0283             << QColor::fromHsvF(0.2f, 0.300f, 0.4f, 0.6495217645f);
0284         QTest::newRow("HSV 6.2 0.300 0.4") //
0285             << QColor::fromHsvF(6.2f, 0.300f, 0.4f);
0286         QTest::newRow("HSV -6.2 0.300 0.4") //
0287             << QColor::fromHsvF(-6.2f, 0.300f, 0.4f);
0288         QTest::newRow("HSV 6.2 0.300 0.4 0.6495217645") //
0289             << QColor::fromHsvF(6.2f, 0.300f, 0.4f, 0.6495217645f);
0290         QTest::newRow("HSV 6.2 0.300 0.4 -0.6495217645") //
0291             << QColor::fromHsvF(6.2f, 0.300f, 0.4f, -0.6495217645f);
0292         QTest::newRow("HSV 6.2 0.300 0.4 1.6495217645") //
0293             << QColor::fromHsvF(6.2f, 0.300f, 0.4f, 1.6495217645f);
0294 
0295         QTest::newRow("invalid") << QColor();
0296 
0297         // do not suppress warning for generating invalid QColor anymore
0298         qInstallMessageHandler(nullptr);
0299     }
0300 
0301     void helperCompareDialog(ColorDialog *perceptualDialog, QColorDialog *qColorDialog)
0302     {
0303         // Compare the state of perceptualDialog (actual)
0304         // to qColorDialog (expected)
0305         const QColor perceptualDialogSelected = //
0306             perceptualDialog->selectedColor();
0307         const QColor qColorDialogSelected = qColorDialog->selectedColor();
0308         QCOMPARE( //
0309             perceptualDialogSelected.name(), //
0310             qColorDialogSelected.name());
0311         QCOMPARE( //
0312             perceptualDialogSelected.alpha(), //
0313             qColorDialogSelected.alpha());
0314         QCOMPARE(perceptualDialogSelected.spec(), //
0315                  qColorDialogSelected.spec());
0316         const QColor perceptualDialogCurrent = perceptualDialog->currentColor();
0317         const QColor qColorDialogCurrent = qColorDialog->currentColor();
0318         QCOMPARE(perceptualDialogCurrent.name(), qColorDialogCurrent.name());
0319         QCOMPARE(perceptualDialogCurrent.alpha(), qColorDialogCurrent.alpha());
0320         QCOMPARE(perceptualDialogCurrent.spec(), qColorDialogCurrent.spec());
0321         QCOMPARE(perceptualDialog->testOption(QColorDialog::NoButtons), //
0322                  qColorDialog->testOption(QColorDialog::NoButtons));
0323         QCOMPARE( //
0324             perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
0325             qColorDialog->testOption(QColorDialog::ShowAlphaChannel));
0326         QCOMPARE( //
0327             perceptualDialog->options().testFlag(QColorDialog::NoButtons), //
0328             qColorDialog->options().testFlag(QColorDialog::NoButtons));
0329         QCOMPARE( //
0330             perceptualDialog->options().testFlag(QColorDialog::ShowAlphaChannel),
0331             qColorDialog->options().testFlag(QColorDialog::ShowAlphaChannel));
0332         QCOMPARE(perceptualDialog->isVisible(), qColorDialog->isVisible());
0333         QCOMPARE(perceptualDialog->isModal(), qColorDialog->isModal());
0334         QCOMPARE(perceptualDialog->result(), qColorDialog->result());
0335         QCOMPARE(perceptualDialog->parent(), qColorDialog->parent());
0336         QCOMPARE(perceptualDialog->parentWidget(), //
0337                  qColorDialog->parentWidget());
0338     }
0339 
0340 private Q_SLOTS:
0341     // This is just a helper slot, not an actual test.
0342     // It is neither called by QTest (because it has a parameter).
0343     void helperReceiveSignals(QColor color)
0344     {
0345         m_color = color;
0346     }
0347 
0348     void initTestCase()
0349     {
0350         // Called before the first test function is executed
0351     }
0352 
0353     void cleanupTestCase()
0354     {
0355         // Called after the last test function was executed
0356     }
0357 
0358     void init()
0359     {
0360         // Called before each test function is executed
0361 
0362         // Make sure to have mnemonics (like Qt::ALT+Qt::Key_X for "E&xit")
0363         // enabled, also on platforms that disable it by default.
0364         qt_set_sequence_auto_mnemonic(true);
0365     }
0366 
0367     void cleanup()
0368     {
0369         // Called after every test function
0370     }
0371 
0372     void testDefaultConstructorAndDestructor1()
0373     {
0374         // This should not crash!
0375         ColorDialog test(m_srgbBuildinColorSpace);
0376         Q_UNUSED(test);
0377     }
0378 
0379     void testDefaultConstructorAndDestructor2()
0380     {
0381         // This should not crash!
0382         ColorDialog test;
0383         Q_UNUSED(test);
0384     }
0385 
0386     void testDefaultConstructorAndDestructor3()
0387     {
0388         QWidget myWidget;
0389         {
0390             // This should not crash!
0391             ColorDialog test(m_srgbBuildinColorSpace, &myWidget);
0392             Q_UNUSED(test);
0393         }
0394     }
0395 
0396     void testDefaultConstructorAndDestructor4()
0397     {
0398         QWidget myWidget;
0399         {
0400             // This should not crash!
0401             ColorDialog test(&myWidget);
0402             Q_UNUSED(test);
0403         }
0404     }
0405 
0406     void testConstructorQWidget()
0407     {
0408         // Test the constructor ColorDialog(QWidget * parent = nullptr)
0409         m_perceptualDialog.reset( //
0410             new ColorDialog(m_srgbBuildinColorSpace));
0411         QScopedPointer<QWidget> tempWidget{new QWidget()};
0412         QScopedPointer<ColorDialog> tempPerceptualDialog2;
0413         tempPerceptualDialog2.reset( //
0414             new ColorDialog(m_srgbBuildinColorSpace, tempWidget.data()));
0415         QCOMPARE(tempPerceptualDialog2->parentWidget(), tempWidget.data());
0416         QCOMPARE(tempPerceptualDialog2->parent(), tempWidget.data());
0417     }
0418 
0419     void testConstructorQWidgetConformance()
0420     {
0421         // Test the constructor
0422         m_perceptualDialog.reset( //
0423             new ColorDialog( //
0424                 m_srgbBuildinColorSpace, //
0425                 QColor(Qt::white) //
0426                 ) //
0427         );
0428         QScopedPointer<QWidget> tempWidget{new QWidget()};
0429         ColorDialog *tempPerceptualDialog2 = //
0430             new ColorDialog( //
0431                 m_srgbBuildinColorSpace,
0432                 QColor(Qt::white), //
0433                 tempWidget.data() //
0434             );
0435         // Test if this coordinates is conform to QColorDialog
0436         m_qDialog.reset(new QColorDialog());
0437         QColorDialog *tempQDialog2 = new QColorDialog(tempWidget.data());
0438         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0439         helperCompareDialog(tempPerceptualDialog2, tempQDialog2);
0440     }
0441 
0442     void testConstructorQColorQWidget_data()
0443     {
0444         helperProvideQColors();
0445     }
0446 
0447     void testConstructorQColorQWidget()
0448     {
0449         QFETCH(QColor, color);
0450         QColor colorOpaque;
0451         if (color.isValid()) {
0452             colorOpaque = color.toRgb();
0453             colorOpaque.setAlpha(255);
0454         } else {
0455             colorOpaque = Qt::black;
0456         }
0457 
0458         // Test the constructors
0459         m_perceptualDialog.reset( //
0460             new ColorDialog(m_srgbBuildinColorSpace, color));
0461         QScopedPointer<QWidget> tempWidget{new QWidget()};
0462         QScopedPointer<ColorDialog> tempPerceptualDialog2{
0463             //
0464             new ColorDialog( //
0465                 m_srgbBuildinColorSpace, //
0466                 color, //
0467                 tempWidget.data() //
0468                 ) //
0469         };
0470 
0471         // Test post-condition: currentColor() is color
0472         QCOMPARE(m_perceptualDialog->currentColor().name(), //
0473                  colorOpaque.name());
0474         QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
0475                  colorOpaque.alpha());
0476         QCOMPARE(m_perceptualDialog->currentColor().spec(), //
0477                  colorOpaque.spec());
0478         QCOMPARE(tempPerceptualDialog2->currentColor().name(), //
0479                  colorOpaque.name());
0480         QCOMPARE(tempPerceptualDialog2->currentColor().alpha(), //
0481                  colorOpaque.alpha());
0482         QCOMPARE(tempPerceptualDialog2->currentColor().spec(), //
0483                  colorOpaque.spec());
0484         QCOMPARE(tempPerceptualDialog2->parentWidget(), //
0485                  tempWidget.data());
0486         QCOMPARE(tempPerceptualDialog2->parent(), //
0487                  tempWidget.data());
0488     }
0489 
0490 #ifdef Q_OS_LINUX
0491     // Linux has no native color dialog, so it is the only system guaranteed
0492     // to use QColorDialog’s own implementation not only after calling
0493     // QColorDialog::setOption(QColorDialog::DontUseNativeDialog, true);
0494     // but yet on QColorDialog’s constructor call.
0495     void testConstructorQColorQWidgetConformance_data()
0496     {
0497         helperProvideQColors();
0498     }
0499     void testConstructorQColorQWidgetConformance()
0500     {
0501         QFETCH(QColor, color);
0502 
0503         // Test the constructor ColorDialog(QWidget * parent = nullptr)
0504         m_perceptualDialog.reset( //
0505             new ColorDialog(m_srgbBuildinColorSpace, color));
0506         QScopedPointer<QWidget> tempWidget{new QWidget()};
0507         ColorDialog *tempPerceptualDialog2 = new ColorDialog( //
0508             m_srgbBuildinColorSpace,
0509             color,
0510             tempWidget.data());
0511         // Test if this coordinates is conform to QColorDialog
0512         m_qDialog.reset(new QColorDialog(color));
0513         QColorDialog *tempQDialog2 = //
0514             new QColorDialog(color, tempWidget.data());
0515         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0516         helperCompareDialog(tempPerceptualDialog2, tempQDialog2);
0517     }
0518 #endif
0519 
0520 #ifdef Q_OS_LINUX
0521     // Linux has no native color dialog, so it is the only system guaranteed
0522     // to use QColorDialog’s own implementation not only after calling
0523     // QColorDialog::setOption(QColorDialog::DontUseNativeDialog, true);
0524     // but yet on QColorDialog’s constructor call.
0525     void testConformanceWithQColorDialog_data()
0526     {
0527         QTest::addColumn<QColor>("initialColor");
0528         QTest::addColumn<QColor>("secondColor");
0529         QTest::addColumn<bool>("showAlphaChannel");
0530 
0531         QVector<QPair<QByteArray, QColor>> colorList;
0532 
0533         colorList.append( //
0534             QPair<QByteArray, QColor>(QByteArrayLiteral("redOpaque"), //
0535                                       QColor(255, 0, 0)));
0536 
0537         colorList.append( //
0538             QPair<QByteArray, QColor>(QByteArrayLiteral("greenHalf"), //
0539                                       QColor(0, 255, 0, 128)));
0540 
0541         colorList.append( //
0542             QPair<QByteArray, QColor>(QByteArrayLiteral("greenTransparent"), //
0543                                       QColor(255, 0, 255, 0)));
0544 
0545         colorList.append( //
0546             QPair<QByteArray, QColor>(QByteArrayLiteral("invalid"), //
0547                                       QColor()));
0548 
0549         for (int i = 0; i < colorList.size(); ++i) {
0550             for (int j = 0; j < colorList.size(); ++j) {
0551                 const QByteArray descriptionWithoutAlpha = //
0552                     colorList.at(i).first //
0553                     + QByteArrayLiteral("/") //
0554                     + colorList.at(j).first;
0555                 const QByteArray descriptionWithAlpha = //
0556                     descriptionWithoutAlpha //
0557                     + QByteArrayLiteral("/ShowAlphaChannel"); //
0558                 QTest::newRow(descriptionWithAlpha.constData()) //
0559                     << colorList.at(i).second //
0560                     << colorList.at(j).second //
0561                     << true;
0562                 QTest::newRow(descriptionWithoutAlpha.constData()) //
0563                     << colorList.at(i).second //
0564                     << colorList.at(j).second //
0565                     << false;
0566             }
0567         }
0568     }
0569     void testConformanceWithQColorDialog()
0570     {
0571         // Some conformance tests (without a particular systematic approach)
0572         QFETCH(QColor, initialColor);
0573         QFETCH(QColor, secondColor);
0574         QFETCH(bool, showAlphaChannel);
0575 
0576         m_perceptualDialog.reset( //
0577             new ColorDialog(m_srgbBuildinColorSpace, initialColor));
0578         m_qDialog.reset(new QColorDialog(initialColor));
0579         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0580 
0581         m_perceptualDialog->setOption( //
0582             QColorDialog::ShowAlphaChannel, //
0583             showAlphaChannel);
0584         m_qDialog->setOption( //
0585             QColorDialog::ShowAlphaChannel,
0586             showAlphaChannel);
0587 
0588         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0589 
0590         m_perceptualDialog->setCurrentColor(secondColor);
0591         m_qDialog->setCurrentColor(secondColor);
0592         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0593 
0594         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
0595         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
0596         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0597 
0598         m_perceptualDialog->setCurrentColor(secondColor);
0599         m_qDialog->setCurrentColor(secondColor);
0600         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0601 
0602         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Escape);
0603         QTest::keyClick(m_qDialog.data(), Qt::Key_Escape);
0604         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0605     }
0606 #endif
0607 
0608 #ifdef Q_OS_LINUX
0609     // Linux has no native color dialog, so it is the only system guaranteed
0610     // to use QColorDialog’s own implementation not only after calling
0611     // QColorDialog::setOption(QColorDialog::DontUseNativeDialog, true);
0612     // but yet on QColorDialog’s constructor call.
0613     void testConformanceWithQColorDialogNoButtons_data()
0614     {
0615         QTest::addColumn<bool>("showAlphaChannel");
0616         QTest::addColumn<bool>("noButtons");
0617 
0618         QTest::newRow("/ShowAlphaChannel/NoButtons") << true << true;
0619         QTest::newRow("/ShowAlphaChannel") << true << false;
0620         QTest::newRow("/NoButtons") << false << true;
0621         QTest::newRow("") << false << false;
0622     }
0623     void testConformanceWithQColorDialogNoButtons()
0624     {
0625         // Some conformance tests (without a particular systematic approach)
0626         QColor initialColor = Qt::red;
0627         QColor secondColor = Qt::green;
0628         QFETCH(bool, showAlphaChannel);
0629         QFETCH(bool, noButtons);
0630 
0631         m_perceptualDialog.reset( //
0632             new ColorDialog(m_srgbBuildinColorSpace, initialColor));
0633         m_qDialog.reset(new QColorDialog(initialColor));
0634         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0635 
0636         m_perceptualDialog->setOption( //
0637             QColorDialog::ShowAlphaChannel, //
0638             showAlphaChannel);
0639         m_qDialog->setOption( //
0640             QColorDialog::ShowAlphaChannel,
0641             showAlphaChannel);
0642 
0643         m_perceptualDialog->setOption(QColorDialog::NoButtons, noButtons);
0644         m_qDialog->setOption(QColorDialog::NoButtons, noButtons);
0645         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0646 
0647         m_perceptualDialog->setCurrentColor(secondColor);
0648         m_qDialog->setCurrentColor(secondColor);
0649         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0650 
0651         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
0652         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
0653         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0654 
0655         m_perceptualDialog->setCurrentColor(secondColor);
0656         m_qDialog->setCurrentColor(secondColor);
0657         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0658 
0659         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Escape);
0660         QTest::keyClick(m_qDialog.data(), Qt::Key_Escape);
0661         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
0662     }
0663 #endif
0664 
0665     void testColorSelectedSignal()
0666     {
0667         m_perceptualDialog.reset( //
0668             new ColorDialog(m_srgbBuildinColorSpace));
0669         m_perceptualDialog->show();
0670         m_qDialog.reset(new QColorDialog());
0671         m_qDialog->setOption(QColorDialog::DontUseNativeDialog, true);
0672         m_qDialog->show();
0673         QSignalSpy spyPerceptualDialog( //
0674             m_perceptualDialog.data(), //
0675             &ColorDialog::colorSelected);
0676         QSignalSpy spyQDialog(m_qDialog.data(), &QColorDialog::colorSelected);
0677         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
0678         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
0679         QCOMPARE(spyPerceptualDialog.count(), 1);
0680         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0681         m_perceptualDialog->show();
0682         m_qDialog->show();
0683         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Escape);
0684         QTest::keyClick(m_qDialog.data(), Qt::Key_Escape);
0685         QCOMPARE(spyPerceptualDialog.count(), 1);
0686         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0687         m_perceptualDialog->show();
0688         m_qDialog->show();
0689         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
0690         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
0691         QCOMPARE(spyPerceptualDialog.count(), 2);
0692         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0693     }
0694 
0695     void testPropertyConformance_data()
0696     {
0697         // We provide the property names as data. To get the property names,
0698         // this function is used:
0699         // const char *QMetaProperty::name() const
0700         // Now, the type “const char *” cannot be used with QTest::addColumn<>
0701         // because it is not known to Qt’s meta object system. The
0702         // meta object system requires copy constructors for its known types.
0703         // This might get wired with pointer types that might go out of scope.
0704         // Therefore, we create a QByteArray from the data, which can be passed
0705         // without problems through the meta object system. The good thing
0706         // is that for the conversion we do not need to know anything
0707         // about the actual encoding of “const char *”.
0708         QTest::addColumn<QByteArray>("propertyName");
0709         QMetaObject referenceClass = QColorDialog::staticMetaObject;
0710         for (int i = 0; i < referenceClass.propertyCount(); ++i) {
0711             QTest::newRow(referenceClass.property(i).name()) //
0712                 << QByteArray(referenceClass.property(i).name());
0713         }
0714     }
0715 
0716     void testPropertyConformance()
0717     {
0718         QFETCH(QByteArray, propertyName);
0719         QMetaObject testClass = ColorDialog::staticMetaObject;
0720         QMetaObject referenceClass = QColorDialog::staticMetaObject;
0721         int testClassIndex = //
0722             testClass.indexOfProperty(propertyName.constData());
0723         int referenceClassIndex = //
0724             referenceClass.indexOfProperty(propertyName.constData());
0725         QMetaProperty referenceClassProperty = //
0726             referenceClass.property(referenceClassIndex);
0727         // cppcheck-suppress unreadVariable // false positive
0728         QByteArray message = //
0729             QByteArrayLiteral("Test if property \"") //
0730             + QByteArray(referenceClassProperty.name()) //
0731             + QByteArrayLiteral("\" of class \"") //
0732             + QByteArray(referenceClass.className()) //
0733             + QByteArrayLiteral("\" is also available in \"") //
0734             + QByteArray(testClass.className()) //
0735             + QByteArrayLiteral("\".");
0736         QVERIFY2(testClassIndex >= 0, message.constData());
0737         QMetaProperty testClassProperty = testClass.property(testClassIndex);
0738         if (referenceClassProperty.hasNotifySignal()) {
0739             QVERIFY2(testClassProperty.hasNotifySignal(),
0740                      "If the reference class has a notify signal, "
0741                      "the test class must have also a notify signal.");
0742         }
0743         QCOMPARE(testClassProperty.isConstant(), //
0744                  referenceClassProperty.isConstant());
0745         QCOMPARE(testClassProperty.isDesignable(), //
0746                  referenceClassProperty.isDesignable());
0747         QCOMPARE(testClassProperty.isEnumType(), //
0748                  referenceClassProperty.isEnumType());
0749         if (referenceClassProperty.isEnumType()) {
0750             QCOMPARE(testClassProperty.enumerator().enumName(), //
0751                      referenceClassProperty.enumerator().enumName());
0752             QCOMPARE(testClassProperty.enumerator().isFlag(), //
0753                      referenceClassProperty.enumerator().isFlag());
0754             QCOMPARE(testClassProperty.enumerator().isScoped(), //
0755                      referenceClassProperty.enumerator().isScoped());
0756             QCOMPARE(testClassProperty.enumerator().isValid(), //
0757                      referenceClassProperty.enumerator().isValid());
0758             QCOMPARE(testClassProperty.enumerator().keyCount(), //
0759                      referenceClassProperty.enumerator().keyCount());
0760             QCOMPARE(testClassProperty.enumerator().name(), //
0761                      referenceClassProperty.enumerator().name());
0762             QCOMPARE(testClassProperty.enumerator().scope(), //
0763                      referenceClassProperty.enumerator().scope());
0764         }
0765         QCOMPARE(testClassProperty.isFinal(), //
0766                  referenceClassProperty.isFinal());
0767         QCOMPARE(testClassProperty.isFlagType(), //
0768                  referenceClassProperty.isFlagType());
0769         QCOMPARE(testClassProperty.isReadable(), //
0770                  referenceClassProperty.isReadable());
0771         QCOMPARE(testClassProperty.isResettable(), //
0772                  referenceClassProperty.isResettable());
0773         QCOMPARE(testClassProperty.isScriptable(), //
0774                  referenceClassProperty.isScriptable());
0775         QCOMPARE(testClassProperty.isStored(), //
0776                  referenceClassProperty.isStored());
0777         QCOMPARE(testClassProperty.isUser(), //
0778                  referenceClassProperty.isUser());
0779         QCOMPARE(testClassProperty.isValid(), //
0780                  referenceClassProperty.isValid());
0781         QCOMPARE(testClassProperty.isWritable(), //
0782                  referenceClassProperty.isWritable());
0783         QCOMPARE(testClassProperty.isWritable(), //
0784                  referenceClassProperty.isWritable());
0785         QCOMPARE(testClassProperty.name(), //
0786                  referenceClassProperty.name());
0787         if (referenceClassProperty.hasNotifySignal()) {
0788             QCOMPARE(testClassProperty.notifySignal().methodSignature(), //
0789                      referenceClassProperty.notifySignal().methodSignature());
0790             QCOMPARE(testClassProperty.notifySignal().methodType(), //
0791                      referenceClassProperty.notifySignal().methodType());
0792             QCOMPARE(testClassProperty.notifySignal().name(), //
0793                      referenceClassProperty.notifySignal().name());
0794             QCOMPARE(testClassProperty.notifySignal().parameterCount(), //
0795                      referenceClassProperty.notifySignal().parameterCount());
0796             QCOMPARE(testClassProperty.notifySignal().parameterNames(), //
0797                      referenceClassProperty.notifySignal().parameterNames());
0798             QCOMPARE(testClassProperty.notifySignal().parameterTypes(), //
0799                      referenceClassProperty.notifySignal().parameterTypes());
0800             QCOMPARE(testClassProperty.notifySignal().returnType(), //
0801                      referenceClassProperty.notifySignal().returnType());
0802             QCOMPARE(testClassProperty.notifySignal().revision(), //
0803                      referenceClassProperty.notifySignal().revision());
0804             QCOMPARE(testClassProperty.notifySignal().tag(), //
0805                      referenceClassProperty.notifySignal().tag());
0806             QCOMPARE(testClassProperty.notifySignal().typeName(), //
0807                      referenceClassProperty.notifySignal().typeName());
0808         }
0809 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0810         QCOMPARE(testClassProperty.metaType(), //
0811                  referenceClassProperty.metaType());
0812 #else
0813         QCOMPARE(testClassProperty.type(), referenceClassProperty.type());
0814 #endif
0815         QCOMPARE(testClassProperty.typeName(), //
0816                  referenceClassProperty.typeName());
0817         QCOMPARE(testClassProperty.userType(), //
0818                  referenceClassProperty.userType());
0819     }
0820 
0821     void testMethodConformance_data()
0822     {
0823         QTest::addColumn<QByteArray>("methodSignature");
0824         QTest::addColumn<int>("referenceClassIndex");
0825         QMetaObject referenceClass = QColorDialog::staticMetaObject;
0826         for (int i = 0; i < referenceClass.methodCount(); ++i) {
0827             if (referenceClass.method(i).access() != QMetaMethod::Private) {
0828                 // Exclude private methods from conformance check
0829                 QTest::newRow(referenceClass.method(i).name().data()) //
0830                     << QMetaObject::normalizedSignature( //
0831                            referenceClass.method(i).methodSignature().data()) //
0832                     << i;
0833             }
0834         }
0835     }
0836 
0837     void testMethodConformance()
0838     {
0839         // We do only check QMetaObject::method() and
0840         // not QMetaObject::constructor because QColorDialog
0841         // does not provide its constructors to the
0842         // meta-object system.
0843         QFETCH(QByteArray, methodSignature);
0844         QFETCH(int, referenceClassIndex);
0845         QMetaObject testClass = ColorDialog::staticMetaObject;
0846         QMetaObject referenceClass = QColorDialog::staticMetaObject;
0847         int testClassIndex = testClass.indexOfMethod(methodSignature.data());
0848         QMetaMethod referenceClassMethod = //
0849             referenceClass.method(referenceClassIndex);
0850         // cppcheck-suppress unreadVariable // false positive
0851         QByteArray message = //
0852             QByteArrayLiteral("Test if method \"") //
0853             + QByteArray(referenceClassMethod.methodSignature()) //
0854             + QByteArrayLiteral("\" of class \"") //
0855             + QByteArray(referenceClass.className()) //
0856             + QByteArrayLiteral("\" is also available in \"") //
0857             + QByteArray(testClass.className()) //
0858             + QByteArrayLiteral("\".");
0859         QVERIFY2(testClassIndex >= 0, message.constData());
0860         QMetaMethod testClassMethod = testClass.method(testClassIndex);
0861         QCOMPARE(testClassMethod.access(), //
0862                  referenceClassMethod.access());
0863         QCOMPARE(testClassMethod.isValid(), //
0864                  referenceClassMethod.isValid());
0865         QCOMPARE(testClassMethod.methodSignature(), //
0866                  referenceClassMethod.methodSignature());
0867         QCOMPARE(testClassMethod.methodType(), //
0868                  referenceClassMethod.methodType());
0869         QCOMPARE(testClassMethod.name(), //
0870                  referenceClassMethod.name());
0871         QCOMPARE(testClassMethod.parameterCount(), //
0872                  referenceClassMethod.parameterCount());
0873         QCOMPARE(testClassMethod.parameterNames(), //
0874                  referenceClassMethod.parameterNames());
0875         QCOMPARE(testClassMethod.parameterTypes(), //
0876                  referenceClassMethod.parameterTypes());
0877         QCOMPARE(testClassMethod.returnType(), //
0878                  referenceClassMethod.returnType());
0879         QCOMPARE(testClassMethod.revision(), //
0880                  referenceClassMethod.revision());
0881         QCOMPARE(testClassMethod.tag(), //
0882                  referenceClassMethod.tag());
0883         QCOMPARE(testClassMethod.typeName(), //
0884                  referenceClassMethod.typeName());
0885     }
0886 
0887     void testRttiConformance()
0888     {
0889         QMetaObject testClass = ColorDialog::staticMetaObject;
0890         QMetaObject referenceClass = QColorDialog::staticMetaObject;
0891         QVERIFY2(testClass.inherits(referenceClass.superClass()),
0892                  "Test that ColorDialog inherits "
0893                  "from QColorDialog’s superclass.");
0894     }
0895 
0896     void testCurrentColorChangedSignal()
0897     {
0898         m_perceptualDialog.reset( //
0899             new ColorDialog(m_srgbBuildinColorSpace));
0900         m_qDialog.reset(new QColorDialog());
0901         m_perceptualDialog->show();
0902         m_qDialog->show();
0903         QSignalSpy spyPerceptualDialog( //
0904             m_perceptualDialog.data(), //
0905             &ColorDialog::currentColorChanged);
0906         QSignalSpy spyQDialog(m_qDialog.data(), //
0907                               &QColorDialog::currentColorChanged);
0908 
0909         // Test that a simple “return key” click by the user
0910         // does not call this signal
0911         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
0912         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
0913         QCOMPARE(spyPerceptualDialog.count(), 0);
0914         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0915 
0916         // A different color should call the signal
0917         m_perceptualDialog->setCurrentColor(QColor(1, 2, 3));
0918         m_qDialog->setCurrentColor(QColor(1, 2, 3));
0919         QCOMPARE(spyPerceptualDialog.count(), 1);
0920         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0921 
0922         // The same color again should not call again the signal
0923         m_perceptualDialog->setCurrentColor(QColor(1, 2, 3));
0924         m_qDialog->setCurrentColor(QColor(1, 2, 3));
0925         QCOMPARE(spyPerceptualDialog.count(), 1);
0926         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0927 
0928         // The same RGB values, but defined in another color model, should not
0929         // emit a signal either.
0930         m_perceptualDialog->setCurrentColor(QColor(1, 2, 3).toHsl());
0931         m_qDialog->setCurrentColor(QColor(1, 2, 3).toHsl());
0932         QCOMPARE(spyPerceptualDialog.count(), 1);
0933         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0934 
0935         // Changing QColorDialog::ShowAlphaChannel should
0936         // not emit a signal either
0937         m_perceptualDialog->setOption( //
0938             QColorDialog::ShowAlphaChannel, //
0939             false);
0940         m_qDialog->setOption( //
0941             QColorDialog::ShowAlphaChannel, //
0942             false);
0943         QCOMPARE(spyPerceptualDialog.count(), 1);
0944         QCOMPARE(spyPerceptualDialog.count(), spyQDialog.count());
0945     }
0946 
0947     void testCurrentColorProperty_data()
0948     {
0949         helperProvideQColors();
0950     }
0951 
0952     void testCurrentColorProperty()
0953     {
0954         QFETCH(QColor, color);
0955         QColor correctedColor;
0956         if (color.isValid()) {
0957             correctedColor = color.toRgb();
0958         } else {
0959             correctedColor = Qt::black;
0960         }
0961         QColor opaqueColor = correctedColor;
0962         opaqueColor.setAlpha(255);
0963 
0964         m_perceptualDialog.reset( //
0965             new ColorDialog(m_srgbBuildinColorSpace));
0966         m_qDialog.reset(new QColorDialog);
0967         m_qDialog->setOption(QColorDialog::DontUseNativeDialog, true);
0968         m_perceptualDialog->setOption( //
0969             QColorDialog::ShowAlphaChannel, //
0970             true);
0971         m_qDialog->setOption( //
0972             QColorDialog::ShowAlphaChannel, //
0973             true);
0974         m_perceptualDialog->setCurrentColor(color);
0975         m_qDialog->setCurrentColor(color);
0976         // Test conformance (but only integer precision)
0977         QCOMPARE(m_perceptualDialog->currentColor().rgb(), //
0978                  m_qDialog->currentColor().rgb());
0979         if (color.isValid()) {
0980             // Testing alpha value conformance only for valid QColor inputs,
0981             // because QColorDialog has here some surprising behaviour that
0982             // we won’t imitate.
0983             QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
0984                      m_qDialog->currentColor().alpha());
0985         }
0986         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
0987                  static_cast<int>(m_qDialog->currentColor().spec()));
0988         // Test post condition (but only integer precision)
0989         QCOMPARE(m_perceptualDialog->currentColor().rgb(), //
0990                  correctedColor.rgb());
0991         QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
0992                  correctedColor.alpha());
0993         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
0994                  static_cast<int>(correctedColor.spec()));
0995 
0996         // Test that changing QColorDialog::ShowAlphaChannel
0997         // alone does not change the currentColor property
0998         m_perceptualDialog->setOption( //
0999             QColorDialog::ShowAlphaChannel, //
1000             false);
1001         m_qDialog->setOption( //
1002             QColorDialog::ShowAlphaChannel, //
1003             false);
1004         // Test conformance (but only integer precision)
1005         QCOMPARE(m_perceptualDialog->currentColor().rgb(), //
1006                  m_qDialog->currentColor().rgb());
1007         if (color.isValid()) {
1008             // Testing alpha value conformance only for valid QColor inputs,
1009             // because QColorDialog has here some surprising behaviour that
1010             // we won’t imitate.
1011             QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
1012                      m_qDialog->currentColor().alpha());
1013         }
1014         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
1015                  static_cast<int>(m_qDialog->currentColor().spec()));
1016         // Test post condition (but only integer precision)
1017         QCOMPARE(m_perceptualDialog->currentColor().rgb(), //
1018                  correctedColor.rgb());
1019         QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
1020                  correctedColor.alpha());
1021         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
1022                  static_cast<int>(correctedColor.spec()));
1023 
1024         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, false);
1025         m_qDialog->setOption( //
1026             QColorDialog::ShowAlphaChannel, //
1027             false);
1028         m_perceptualDialog->setCurrentColor(color);
1029         m_qDialog->setCurrentColor(color);
1030         // Test conformance (but only integer precision)
1031         QCOMPARE(m_perceptualDialog->currentColor().rgb(), //
1032                  m_qDialog->currentColor().rgb());
1033         QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
1034                  m_qDialog->currentColor().alpha());
1035         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
1036                  static_cast<int>(m_qDialog->currentColor().spec()));
1037         // Test post condition (but only integer precision)
1038         QCOMPARE(m_perceptualDialog->currentColor().rgb(), opaqueColor.rgb());
1039         QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
1040                  opaqueColor.alpha());
1041         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
1042                  static_cast<int>(opaqueColor.spec()));
1043 
1044         // Test that changing QColorDialog::ShowAlphaChannel
1045         // alone does not change the currentColor property
1046         m_perceptualDialog->setOption( //
1047             QColorDialog::ShowAlphaChannel, //
1048             true);
1049         m_qDialog->setOption( //
1050             QColorDialog::ShowAlphaChannel, //
1051             true);
1052         // Test conformance (but only integer precision)
1053         QCOMPARE(m_perceptualDialog->currentColor().rgb(), //
1054                  m_qDialog->currentColor().rgb());
1055         QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
1056                  m_qDialog->currentColor().alpha());
1057         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
1058                  static_cast<int>(m_qDialog->currentColor().spec()));
1059         // Test post condition (but only integer precision)
1060         QCOMPARE(m_perceptualDialog->currentColor().rgb(), opaqueColor.rgb());
1061         QCOMPARE(m_perceptualDialog->currentColor().alpha(), //
1062                  opaqueColor.alpha());
1063         QCOMPARE(static_cast<int>(m_perceptualDialog->currentColor().spec()), //
1064                  static_cast<int>(opaqueColor.spec()));
1065     }
1066 
1067 #ifndef MSVC_DLL
1068     void testSetCurrentColor()
1069     {
1070         m_perceptualDialog.reset( //
1071             new ColorDialog(m_srgbBuildinColorSpace));
1072         m_perceptualDialog->show();
1073         constexpr auto testColor = Qt::yellow;
1074         m_perceptualDialog->setCurrentColor(testColor);
1075 
1076         // Get internal LCH value
1077         const auto tmp = m_perceptualDialog->d_pointer->m_currentOpaqueColorAbs;
1078         const LchDouble color = //
1079             tmp.value(ColorModel::CielchD50).reinterpretAsLchToLchDouble();
1080 
1081         // The very same LCH value has to be found in all widgets using it.
1082         // (This is not trivial, because even coming from RGB, because of
1083         // rounding errors, you can get out-of-gamut LCH values when the
1084         // original RGB value was near to the border. And the child
1085         // widgets may change the LCH value that is given to them
1086         // to fit it into the gamut – each widget with a different
1087         // algorithm.)
1088         QVERIFY( //
1089             color.hasSameCoordinates( //
1090                 m_perceptualDialog //
1091                     ->d_pointer //
1092                     ->m_wheelColorPicker //
1093                     ->currentColor()));
1094         QVERIFY( //
1095             color.hasSameCoordinates( //
1096                 m_perceptualDialog //
1097                     ->d_pointer //
1098                     ->m_chromaHueDiagram //
1099                     ->currentColor()));
1100         // We do not also control this here for
1101         // m_perceptualDialog->d_pointer->m_ciehlcD50SpinBox because this
1102         // widget rounds the given value to the current decimal precision
1103         // it’s using. Therefore, it’s pointless to control here
1104         // for rounding errors.
1105 
1106         const auto sliderColor = //
1107             m_perceptualDialog->d_pointer->m_alphaGradientSlider->secondColor();
1108         QCOMPARE(sliderColor.l, color.l);
1109         QCOMPARE(sliderColor.c, color.c);
1110         QCOMPARE(sliderColor.h, color.h);
1111     }
1112 #endif
1113 
1114     void testOpen()
1115     {
1116         // Test our reference (QColorDialog)
1117         m_color = Qt::black;
1118         m_qDialog.reset(new QColorDialog);
1119         m_qDialog->setOption(QColorDialog::DontUseNativeDialog, true);
1120         m_qDialog->setCurrentColor(Qt::white);
1121         m_qDialog->open(this, SLOT(helperReceiveSignals(QColor)));
1122         m_qDialog->setCurrentColor(Qt::red);
1123         // Changing the current color does not emit the signal
1124         QCOMPARE(m_color, Qt::black);
1125         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
1126         // Return key really emits a signal
1127         QCOMPARE(m_color, Qt::red);
1128         m_qDialog->show();
1129         m_qDialog->setCurrentColor(Qt::green);
1130         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
1131         // The signal is really disconnected after the dialog has been closed.
1132         QCOMPARE(m_color, Qt::red);
1133 
1134         // Now test if ColorDialog does the same
1135         // thing as our reference
1136         m_color = Qt::black;
1137         m_perceptualDialog.reset( //
1138             new ColorDialog(m_srgbBuildinColorSpace));
1139         m_perceptualDialog->setCurrentColor(Qt::white);
1140         m_perceptualDialog->open(this, SLOT(helperReceiveSignals(QColor)));
1141         m_perceptualDialog->setCurrentColor(Qt::red);
1142         // Changing the current color does not emit the signal
1143         QCOMPARE(m_color, Qt::black);
1144         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
1145         // Return key really emits a signal
1146         QCOMPARE(m_color, Qt::red);
1147         m_perceptualDialog->show();
1148         m_perceptualDialog->setCurrentColor(Qt::green);
1149         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
1150         // The signal is really disconnected after the dialog has been closed.
1151         QCOMPARE(m_color, Qt::red);
1152     }
1153 
1154     void testDefaultOptions()
1155     {
1156         m_perceptualDialog.reset( //
1157             new ColorDialog(m_srgbBuildinColorSpace));
1158         m_qDialog.reset(new QColorDialog);
1159         QCOMPARE( //
1160             m_perceptualDialog->testOption( //
1161                 QColorDialog::DontUseNativeDialog), //
1162             true);
1163         QCOMPARE( //
1164             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1165             false);
1166         QCOMPARE( //
1167             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1168             m_qDialog->testOption(QColorDialog::NoButtons));
1169         QCOMPARE( //
1170             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1171             false);
1172         QCOMPARE( //
1173             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1174             m_qDialog->testOption(QColorDialog::ShowAlphaChannel));
1175         QCOMPARE( //
1176             m_perceptualDialog->options().testFlag( //
1177                 QColorDialog::DontUseNativeDialog), //
1178             true);
1179         QCOMPARE( //
1180             m_perceptualDialog->options().testFlag(QColorDialog::NoButtons), //
1181             false);
1182         QCOMPARE( //
1183             m_perceptualDialog->options().testFlag(QColorDialog::NoButtons), //
1184             m_qDialog->options().testFlag(QColorDialog::NoButtons));
1185         QCOMPARE( //
1186             m_perceptualDialog->options().testFlag( //
1187                 QColorDialog::ShowAlphaChannel), //
1188             false);
1189         QCOMPARE( //
1190             m_perceptualDialog->options().testFlag( //
1191                 QColorDialog::ShowAlphaChannel), //
1192             m_qDialog->options().testFlag(QColorDialog::ShowAlphaChannel));
1193     }
1194 
1195     void testOptionDontUseNativeDialogAlwaysTrue()
1196     {
1197         m_perceptualDialog.reset( //
1198             new ColorDialog(m_srgbBuildinColorSpace));
1199         m_perceptualDialog->setOption(QColorDialog::DontUseNativeDialog);
1200         QCOMPARE( //
1201             m_perceptualDialog->testOption( //
1202                 QColorDialog::DontUseNativeDialog), //
1203             true);
1204         QCOMPARE( //
1205             m_perceptualDialog->options().testFlag( //
1206                 QColorDialog::DontUseNativeDialog), //
1207             true);
1208         m_perceptualDialog->setOptions(QColorDialog::DontUseNativeDialog);
1209         QCOMPARE( //
1210             m_perceptualDialog->testOption( //
1211                 QColorDialog::DontUseNativeDialog), //
1212             true);
1213         QCOMPARE( //
1214             m_perceptualDialog->options().testFlag( //
1215                 QColorDialog::DontUseNativeDialog),
1216             true);
1217         m_perceptualDialog->setOptions( //
1218             QColorDialog::DontUseNativeDialog | QColorDialog::NoButtons);
1219         QCOMPARE( //
1220             m_perceptualDialog->testOption( //
1221                 QColorDialog::DontUseNativeDialog), //
1222             true);
1223         QCOMPARE( //
1224             m_perceptualDialog->options().testFlag( //
1225                 QColorDialog::DontUseNativeDialog), //
1226             true);
1227         m_perceptualDialog->setOptions( //
1228             QColorDialog::DontUseNativeDialog | QColorDialog::ShowAlphaChannel);
1229         QCOMPARE( //
1230             m_perceptualDialog->testOption( //
1231                 QColorDialog::DontUseNativeDialog), //
1232             true);
1233         QCOMPARE( //
1234             m_perceptualDialog->options().testFlag( //
1235                 QColorDialog::DontUseNativeDialog), //
1236             true);
1237         m_perceptualDialog->setOptions( //
1238             QColorDialog::DontUseNativeDialog //
1239             | QColorDialog::ShowAlphaChannel //
1240             | QColorDialog::NoButtons);
1241         QCOMPARE( //
1242             m_perceptualDialog->testOption( //
1243                 QColorDialog::DontUseNativeDialog), //
1244             true);
1245         QCOMPARE( //
1246             m_perceptualDialog->options().testFlag( //
1247                 QColorDialog::DontUseNativeDialog), //
1248             true);
1249     }
1250 
1251 #ifdef Q_OS_LINUX
1252     // Linux has no native color dialog, so it is the only system guaranteed
1253     // to use QColorDialog’s own implementation not only after calling
1254     // QColorDialog::setOption(QColorDialog::DontUseNativeDialog, true);
1255     // but yet on QColorDialog’s constructor call.
1256     void testOptionShowAlpha()
1257     {
1258         m_perceptualDialog.reset( //
1259             new ColorDialog(m_srgbBuildinColorSpace, QColor(Qt::white)));
1260         m_qDialog.reset(new QColorDialog);
1261         m_perceptualDialog->setOption( //
1262             QColorDialog::ShowAlphaChannel);
1263         QVERIFY2( //
1264             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1265             "ShowAlphaChannel successfully set.");
1266         m_qDialog->setOption(QColorDialog::ShowAlphaChannel);
1267         m_perceptualDialog->show();
1268         m_qDialog->show();
1269         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1270         QColor tempColor = QColor(1, 101, 201, 155);
1271         m_perceptualDialog->setCurrentColor(tempColor);
1272         m_qDialog->setCurrentColor(tempColor);
1273         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1274         m_perceptualDialog->setOption( //
1275             QColorDialog::ShowAlphaChannel, //
1276             false);
1277         QVERIFY2( //
1278             !m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1279             "ShowAlphaChannel successfully set.");
1280         m_qDialog->setOption(QColorDialog::ShowAlphaChannel, false);
1281         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1282         tempColor = QColor(5, 105, 205, 133);
1283         m_perceptualDialog->setCurrentColor(tempColor);
1284         m_qDialog->setCurrentColor(tempColor);
1285         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1286     }
1287 #endif
1288 
1289 #ifdef Q_OS_LINUX
1290     // Linux has no native color dialog, so it is the only system guaranteed
1291     // to use QColorDialog’s own implementation not only after calling
1292     // QColorDialog::setOption(QColorDialog::DontUseNativeDialog, true);
1293     // but yet on QColorDialog’s constructor call.
1294     void testOptionNoButtons()
1295     {
1296         m_perceptualDialog.reset( //
1297             new ColorDialog( //
1298                 m_srgbBuildinColorSpace, //
1299                 QColor(Qt::white) //
1300                 ) //
1301         );
1302         m_qDialog.reset(new QColorDialog);
1303         m_perceptualDialog->setOption( //
1304             QColorDialog::NoButtons);
1305         QVERIFY2( //
1306             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1307             "NoButtons successfully set to true.");
1308         m_qDialog->setOption(QColorDialog::NoButtons);
1309         m_perceptualDialog->show();
1310         m_qDialog->show();
1311         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1312         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
1313         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
1314         QCOMPARE(m_perceptualDialog->isVisible(), m_qDialog->isVisible());
1315         QVERIFY2(m_perceptualDialog->isVisible(), //
1316                  "Should still visible after Return key pressed.");
1317         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1318         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Escape);
1319         QTest::keyClick(m_qDialog.data(), Qt::Key_Escape);
1320         QCOMPARE(m_perceptualDialog->isVisible(), m_qDialog->isVisible());
1321         QVERIFY2(!m_perceptualDialog->isVisible(), //
1322                  "Should no longer be visible after Escape key pressed.");
1323         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1324 
1325         m_perceptualDialog->setOption(QColorDialog::NoButtons, false);
1326         QVERIFY2(!m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1327                  "NoButtons successfully set to false.");
1328         m_qDialog->setOption(QColorDialog::NoButtons, false);
1329         m_perceptualDialog->show();
1330         m_qDialog->show();
1331         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1332         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
1333         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
1334         QCOMPARE(m_perceptualDialog->isVisible(), m_qDialog->isVisible());
1335         QVERIFY2(!m_perceptualDialog->isVisible(), //
1336                  "Should no longer be visible after Return key pressed.");
1337         helperCompareDialog(m_perceptualDialog.data(), m_qDialog.data());
1338     }
1339 #endif
1340 
1341     void testSetOptionAndTestOptionInteraction()
1342     {
1343         m_perceptualDialog.reset( //
1344             new ColorDialog(m_srgbBuildinColorSpace));
1345         // Test if the option changes as expected
1346         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1347                                       true);
1348         QCOMPARE( //
1349             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1350             true);
1351         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1352                                       false);
1353         QCOMPARE( //
1354             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1355             false);
1356         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1357                                       true);
1358         QCOMPARE( //
1359             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1360             true);
1361         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1362                                       false);
1363         QCOMPARE( //
1364             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1365             false);
1366         m_perceptualDialog.reset(nullptr);
1367 
1368         m_perceptualDialog.reset( //
1369             new ColorDialog(m_srgbBuildinColorSpace));
1370         // Test if the option changes as expected
1371         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1372                                       false);
1373         QCOMPARE( //
1374             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1375             false);
1376         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1377                                       true);
1378         QCOMPARE( //
1379             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1380             true);
1381         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1382                                       false);
1383         QCOMPARE( //
1384             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1385             false);
1386         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel, //
1387                                       true);
1388         QCOMPARE( //
1389             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1390             true);
1391         m_perceptualDialog.reset(nullptr);
1392 
1393         m_perceptualDialog.reset( //
1394             new ColorDialog(m_srgbBuildinColorSpace));
1395         // Test if the option changes as expected
1396         m_perceptualDialog->setOption( //
1397             QColorDialog::NoButtons, //
1398             true);
1399         QCOMPARE( //
1400             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1401             true);
1402         m_perceptualDialog->setOption( //
1403             QColorDialog::NoButtons, //
1404             false);
1405         QCOMPARE(m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1406                  false);
1407         m_perceptualDialog->setOption( //
1408             QColorDialog::NoButtons, //
1409             true);
1410         QCOMPARE(m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1411                  true);
1412         m_perceptualDialog->setOption( //
1413             QColorDialog::NoButtons, //
1414             false);
1415         QCOMPARE(m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1416                  false);
1417         m_perceptualDialog.reset(nullptr);
1418 
1419         m_perceptualDialog.reset( //
1420             new ColorDialog(m_srgbBuildinColorSpace));
1421         // Test if the option changes as expected
1422         m_perceptualDialog->setOption( //
1423             QColorDialog::NoButtons, //
1424             false);
1425         QCOMPARE( //
1426             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1427             false);
1428         m_perceptualDialog->setOption(QColorDialog::NoButtons, true);
1429         QCOMPARE( //
1430             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1431             true);
1432         m_perceptualDialog->setOption( //
1433             QColorDialog::NoButtons, //
1434             false);
1435         QCOMPARE( //
1436             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1437             false);
1438         m_perceptualDialog->setOption( //
1439             QColorDialog::NoButtons, //
1440             true);
1441         QCOMPARE( //
1442             m_perceptualDialog->testOption(QColorDialog::NoButtons), //
1443             true);
1444         m_perceptualDialog.reset(nullptr);
1445 
1446         m_perceptualDialog.reset( //
1447             new ColorDialog(m_srgbBuildinColorSpace));
1448         // define an option
1449         m_perceptualDialog->setOption( //
1450             QColorDialog::ShowAlphaChannel, //
1451             true);
1452         // change some other option
1453         m_perceptualDialog->setOption( //
1454             QColorDialog::NoButtons, //
1455             true);
1456         // test if first option is still unchanged
1457         QCOMPARE( //
1458             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1459             true);
1460         m_perceptualDialog.reset(nullptr);
1461 
1462         m_perceptualDialog.reset( //
1463             new ColorDialog(m_srgbBuildinColorSpace));
1464         // define an option
1465         m_perceptualDialog->setOption( //
1466             QColorDialog::ShowAlphaChannel, //
1467             false);
1468         // change some other option
1469         m_perceptualDialog->setOption( //
1470             QColorDialog::NoButtons, //
1471             true);
1472         // test if first option is still unchanged
1473         QCOMPARE( //
1474             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1475             false);
1476         m_perceptualDialog.reset(nullptr);
1477 
1478         m_perceptualDialog.reset( //
1479             new ColorDialog(m_srgbBuildinColorSpace));
1480         // define an option
1481         m_perceptualDialog->setOption( //
1482             QColorDialog::ShowAlphaChannel, //
1483             true);
1484         // change some other option
1485         m_perceptualDialog->setOption( //
1486             QColorDialog::NoButtons, //
1487             false);
1488         // test if first option is still unchanged
1489         QCOMPARE( //
1490             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1491             true);
1492         m_perceptualDialog.reset(nullptr);
1493 
1494         m_perceptualDialog.reset( //
1495             new ColorDialog(m_srgbBuildinColorSpace));
1496         // define an option
1497         m_perceptualDialog->setOption( //
1498             QColorDialog::ShowAlphaChannel, //
1499             false);
1500         // change some other option
1501         m_perceptualDialog->setOption( //
1502             QColorDialog::NoButtons, //
1503             false);
1504         // test if first option is still unchanged
1505         QCOMPARE( //
1506             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1507             false);
1508         m_perceptualDialog.reset(nullptr);
1509     }
1510 
1511     void testRgbInput()
1512     {
1513         m_perceptualDialog.reset( //
1514             new ColorDialog(m_srgbBuildinColorSpace));
1515         m_perceptualDialog->setOption( //
1516             QColorDialog::ShowAlphaChannel, //
1517             true);
1518     }
1519 
1520     void testAlphaSpinbox()
1521     {
1522         m_perceptualDialog.reset( //
1523             new ColorDialog(m_srgbBuildinColorSpace));
1524         m_perceptualDialog->setOption( //
1525             QColorDialog::ShowAlphaChannel, //
1526             true);
1527         m_perceptualDialog->d_pointer->m_alphaGradientSlider->setValue(0.504);
1528         QCOMPARE( //
1529             m_perceptualDialog->d_pointer->m_alphaGradientSlider->value(), //
1530             0.504);
1531         QCOMPARE(m_perceptualDialog->d_pointer->m_alphaSpinBox->value(), 50);
1532         QTest::keyClick(m_perceptualDialog->d_pointer->m_alphaSpinBox, //
1533                         Qt::Key_Up);
1534         QCOMPARE( //
1535             m_perceptualDialog->d_pointer->m_alphaGradientSlider->value(), //
1536             0.51);
1537         QCOMPARE(m_perceptualDialog->d_pointer->m_alphaSpinBox->value(), 51);
1538     }
1539 
1540 #ifdef Q_OS_LINUX
1541     // Linux has no native color dialog, so it is the only system guaranteed
1542     // to use QColorDialog’s own implementation not only after calling
1543     // QColorDialog::setOption(QColorDialog::DontUseNativeDialog, true);
1544     // but yet on QColorDialog’s constructor call.
1545     void testSelectedColorAndSetVisible()
1546     {
1547         m_perceptualDialog.reset( //
1548             new ColorDialog(m_srgbBuildinColorSpace));
1549         m_qDialog.reset(new QColorDialog);
1550         QCOMPARE(m_perceptualDialog->selectedColor(), //
1551                  m_qDialog->selectedColor());
1552         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1553         m_perceptualDialog->setCurrentColor(QColor(Qt::blue));
1554         m_qDialog->setCurrentColor(QColor(Qt::blue));
1555         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
1556         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
1557         // Still no valid selectedColor() because the dialog still wasn't shown
1558         QCOMPARE(m_perceptualDialog->selectedColor(), //
1559                  m_qDialog->selectedColor());
1560         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1561         m_perceptualDialog->show();
1562         m_qDialog->show();
1563         QCOMPARE(m_perceptualDialog->selectedColor(), //
1564                  m_qDialog->selectedColor());
1565         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1566         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
1567         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
1568         QCOMPARE(m_perceptualDialog->selectedColor(), //
1569                  m_qDialog->selectedColor());
1570         QCOMPARE(m_perceptualDialog->selectedColor(), QColor(Qt::blue));
1571         m_perceptualDialog->show();
1572         m_qDialog->show();
1573         QCOMPARE(m_perceptualDialog->selectedColor(), //
1574                  m_qDialog->selectedColor());
1575         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1576         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Escape);
1577         QTest::keyClick(m_qDialog.data(), Qt::Key_Escape);
1578         QCOMPARE(m_perceptualDialog->selectedColor(), //
1579                  m_qDialog->selectedColor());
1580         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1581         m_perceptualDialog->setVisible(true);
1582         m_qDialog->setVisible(true);
1583         QCOMPARE(m_perceptualDialog->selectedColor(), //
1584                  m_qDialog->selectedColor());
1585         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1586         QTest::keyClick(m_perceptualDialog.data(), Qt::Key_Return);
1587         QTest::keyClick(m_qDialog.data(), Qt::Key_Return);
1588         QCOMPARE(m_perceptualDialog->selectedColor(), //
1589                  m_qDialog->selectedColor());
1590         QCOMPARE(m_perceptualDialog->selectedColor(), QColor(Qt::blue));
1591         m_perceptualDialog->show();
1592         m_qDialog->show();
1593         QCOMPARE(m_perceptualDialog->selectedColor(), //
1594                  m_qDialog->selectedColor());
1595         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1596         m_perceptualDialog->hide();
1597         m_qDialog->hide();
1598         QCOMPARE(m_perceptualDialog->selectedColor(), //
1599                  m_qDialog->selectedColor());
1600         QCOMPARE(m_perceptualDialog->selectedColor(), QColor());
1601     }
1602 #endif
1603 
1604     void testAliases()
1605     {
1606         m_perceptualDialog.reset( //
1607             new ColorDialog(m_srgbBuildinColorSpace));
1608         m_qDialog.reset(new QColorDialog);
1609 
1610         // Test setting QColorDialog syntax
1611         m_perceptualDialog->setOption(QColorDialog::ShowAlphaChannel);
1612         QCOMPARE( //
1613             m_perceptualDialog->testOption(QColorDialog::ShowAlphaChannel), //
1614             true);
1615         QCOMPARE( //
1616             m_perceptualDialog->testOption //
1617             (ColorDialog::ColorDialogOption::ShowAlphaChannel), //
1618             true);
1619         m_qDialog->setOption( //
1620             QColorDialog::ShowAlphaChannel);
1621         QCOMPARE(m_qDialog->testOption(QColorDialog::ShowAlphaChannel), //
1622                  true);
1623         QCOMPARE( //
1624             m_qDialog->testOption( //
1625                 ColorDialog::ColorDialogOption::ShowAlphaChannel), //
1626             true);
1627 
1628         // Test setting our alias syntax
1629         m_perceptualDialog->setOption( //
1630             ColorDialog::ColorDialogOption::NoButtons);
1631         QCOMPARE( //
1632             m_perceptualDialog->testOption(QColorDialog::NoButtons),
1633             true);
1634         QCOMPARE( //
1635             m_perceptualDialog->testOption( //
1636                 ColorDialog::ColorDialogOption::NoButtons), //
1637             true);
1638         m_qDialog->setOption( //
1639             ColorDialog::ColorDialogOption::NoButtons);
1640         QCOMPARE(m_qDialog->testOption(QColorDialog::NoButtons), //
1641                  true);
1642         QCOMPARE( //
1643             m_qDialog->testOption(ColorDialog::ColorDialogOption::NoButtons), //
1644             true);
1645 
1646         // Test if ColorDialogOptions is compatible (at least for == operator)
1647         // Configure conformance with our dialog:
1648         m_qDialog->setOption(QColorDialog::DontUseNativeDialog);
1649         QCOMPARE(m_perceptualDialog->options(), m_qDialog->options());
1650     }
1651 
1652 #ifndef MSVC_DLL
1653     void testReadLightnessValues()
1654     {
1655         QScopedPointer<ColorDialog> myDialog( //
1656             new ColorDialog(m_srgbBuildinColorSpace));
1657         myDialog->d_pointer->m_lchLightnessSelector->setValue(0.6);
1658         myDialog->d_pointer->readLightnessValue();
1659         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::CielchD50).first, 60);
1660     }
1661 #endif
1662 
1663     void testGamutIconCiehlcD50()
1664     {
1665         QScopedPointer<ColorDialog> myDialog( //
1666             new ColorDialog(m_srgbBuildinColorSpace));
1667 
1668         // On startup, the current color should be in-gamut, so no gamut action
1669         // should be visible.
1670         QCOMPARE( //
1671             myDialog->d_pointer->m_ciehlcD50SpinBoxGamutAction->isVisible(), //
1672             false);
1673         QCOMPARE( //
1674             myDialog->d_pointer->m_oklchSpinBoxGamutAction->isVisible(), //
1675             false);
1676 
1677         constexpr int iterations = 15;
1678 
1679         auto &myCiehlcD50SpinBox = myDialog->d_pointer->m_ciehlcD50SpinBox;
1680         myCiehlcD50SpinBox->setFocus();
1681         QTest::keyClick(myCiehlcD50SpinBox, Qt::Key_Tab);
1682         QTest::keyClick(myCiehlcD50SpinBox, Qt::Key_Tab);
1683         // Now, the focus should be on the chroma value.
1684         for (int i = 0; i < iterations; ++i) {
1685             QTest::keyClick(myCiehlcD50SpinBox, Qt::Key_PageUp);
1686         }
1687         QCOMPARE( //
1688             myDialog->d_pointer->m_ciehlcD50SpinBoxGamutAction->isVisible(), //
1689             true);
1690         for (int i = 0; i < iterations; ++i) {
1691             QTest::keyClick(myCiehlcD50SpinBox, Qt::Key_PageDown);
1692         }
1693         QCOMPARE( //
1694             myDialog->d_pointer->m_ciehlcD50SpinBoxGamutAction->isVisible(), //
1695             false);
1696     }
1697 
1698     void testGamutIconOklch()
1699     {
1700         QScopedPointer<ColorDialog> myDialog( //
1701             new ColorDialog(m_srgbBuildinColorSpace));
1702 
1703         // On startup, the current color should be in-gamut, so no gamut action
1704         // should be visible.
1705         QCOMPARE( //
1706             myDialog->d_pointer->m_oklchSpinBoxGamutAction->isVisible(), //
1707             false);
1708         QCOMPARE( //
1709             myDialog->d_pointer->m_oklchSpinBoxGamutAction->isVisible(), //
1710             false);
1711 
1712         constexpr int iterations = 15;
1713 
1714         auto &myOklchSpinBox = myDialog->d_pointer->m_oklchSpinBox;
1715         myOklchSpinBox->setFocus();
1716         QTest::keyClick(myOklchSpinBox, Qt::Key_Tab);
1717         // Now, the focus should be on the chroma value.
1718         for (int i = 0; i < iterations; ++i) {
1719             QTest::keyClick(myOklchSpinBox, Qt::Key_PageUp);
1720         }
1721         QCOMPARE( //
1722             myDialog->d_pointer->m_oklchSpinBoxGamutAction->isVisible(), //
1723             true);
1724         for (int i = 0; i < iterations; ++i) {
1725             QTest::keyClick(myOklchSpinBox, Qt::Key_PageDown);
1726         }
1727         QCOMPARE( //
1728             myDialog->d_pointer->m_oklchSpinBoxGamutAction->isVisible(), //
1729             false);
1730     }
1731 
1732 #ifndef MSVC_DLL
1733     void testReadHlcNumericValues()
1734     {
1735         QScopedPointer<ColorDialog> myDialog( //
1736             new ColorDialog(m_srgbBuildinColorSpace));
1737         QList<double> myValues = //
1738             myDialog->d_pointer->m_ciehlcD50SpinBox->sectionValues();
1739 
1740         // Test with a normal value
1741         myValues[0] = 10;
1742         myValues[1] = 11;
1743         myValues[2] = 12;
1744         myDialog->d_pointer->m_ciehlcD50SpinBox->setSectionValues(myValues);
1745         myDialog->d_pointer->readHlcNumericValues();
1746         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::CielchD50).third, 10);
1747         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::CielchD50).first, 11);
1748         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::CielchD50).second, 12);
1749 
1750         // Test with an out-of-gamut value. Hue and lightness should not change.
1751         myValues[0] = 10;
1752         myValues[1] = 11;
1753         myValues[2] = 50;
1754         myDialog->d_pointer->m_ciehlcD50SpinBox->setSectionValues(myValues);
1755         myDialog->d_pointer->readHlcNumericValues();
1756         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::CielchD50).third, 10);
1757         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::CielchD50).first, 11);
1758     }
1759 #endif
1760 
1761 #ifndef MSVC_DLL
1762     void testReadOklchNumericValues()
1763     {
1764         QScopedPointer<ColorDialog> myDialog( //
1765             new ColorDialog(m_srgbBuildinColorSpace));
1766         QList<double> myValues = //
1767             myDialog->d_pointer->m_oklchSpinBox->sectionValues();
1768 
1769         // Test with a normal value
1770         myValues[0] = 0.25;
1771         myValues[1] = 0.05;
1772         myValues[2] = 3;
1773         myDialog->d_pointer->m_oklchSpinBox->setSectionValues(myValues);
1774         myDialog->d_pointer->readOklchNumericValues();
1775         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::OklchD65).first, 0.25);
1776         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::OklchD65).second, 0.05);
1777         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::OklchD65).third, 3);
1778 
1779         // Test with an out-of-gamut value. Hue and lightness should not change.
1780         myValues[0] = 0.25;
1781         myValues[1] = 0.2;
1782         myValues[2] = 3;
1783         myDialog->d_pointer->m_oklchSpinBox->setSectionValues(myValues);
1784         myDialog->d_pointer->readOklchNumericValues();
1785         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::OklchD65).first, 0.25);
1786         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::OklchD65).third, 3);
1787     }
1788 #endif
1789 
1790     void testReadHsvNumericValues()
1791     {
1792         QScopedPointer<ColorDialog> myDialog( //
1793             new ColorDialog(m_srgbBuildinColorSpace));
1794         QList<double> myValues = //
1795             myDialog->d_pointer->m_hsvSpinBox->sectionValues();
1796         myValues[0] = 10;
1797         myValues[1] = 11;
1798         myValues[2] = 12;
1799         myDialog->d_pointer->m_hsvSpinBox->setSectionValues(myValues);
1800         myDialog->d_pointer->readHsvNumericValues();
1801         QCOMPARE(qRound(myDialog->currentColor().hueF() * 360), 10);
1802         QCOMPARE(qRound(myDialog->currentColor().saturationF() * 100), 11);
1803         QCOMPARE(qRound(myDialog->currentColor().valueF() * 100), 12);
1804     }
1805 
1806     void testReadRgbHexValues()
1807     {
1808         QScopedPointer<ColorDialog> myDialog( //
1809             new ColorDialog(m_srgbBuildinColorSpace));
1810 
1811         // Test some value
1812         myDialog->d_pointer->m_rgbLineEdit->setText(QStringLiteral("#010203"));
1813         myDialog->d_pointer->readRgbHexValues();
1814         QCOMPARE(myDialog->currentColor().red(), 1);
1815         QCOMPARE(myDialog->currentColor().green(), 2);
1816         QCOMPARE(myDialog->currentColor().blue(), 3);
1817         QCOMPARE(myDialog->d_pointer->m_rgbLineEdit->text(), //
1818                  QStringLiteral("#010203"));
1819 
1820         // Test this value which is known to have triggered yet rounding errors!
1821         myDialog->d_pointer->m_rgbLineEdit->setText(QStringLiteral("#ff0000"));
1822         myDialog->d_pointer->readRgbHexValues();
1823         QCOMPARE(myDialog->currentColor().red(), 255);
1824         QCOMPARE(myDialog->currentColor().green(), 0);
1825         QCOMPARE(myDialog->currentColor().blue(), 0);
1826         QCOMPARE(myDialog->d_pointer->m_rgbLineEdit->text(), //
1827                  QStringLiteral("#ff0000"));
1828 
1829         // Test this value which is known to have triggered yet rounding errors!
1830         myDialog->d_pointer->m_rgbLineEdit->setText(QStringLiteral("#ef6c00"));
1831         myDialog->d_pointer->readRgbHexValues();
1832         QCOMPARE(myDialog->currentColor().red(), 239);
1833         QCOMPARE(myDialog->currentColor().green(), 108);
1834         QCOMPARE(myDialog->currentColor().blue(), 0);
1835         QCOMPARE(myDialog->d_pointer->m_rgbLineEdit->text(), //
1836                  QStringLiteral("#ef6c00"));
1837 
1838         // Test this value which is known to have triggered yet rounding errors!
1839         myDialog->d_pointer->m_rgbLineEdit->setText(QStringLiteral("#ffff00"));
1840         myDialog->d_pointer->readRgbHexValues();
1841         QCOMPARE(myDialog->currentColor().red(), 255);
1842         QCOMPARE(myDialog->currentColor().green(), 255);
1843         QCOMPARE(myDialog->currentColor().blue(), 0);
1844         QCOMPARE(myDialog->d_pointer->m_rgbLineEdit->text(), //
1845                  QStringLiteral("#ffff00"));
1846     }
1847 
1848     void testReadRgbNumericValues()
1849     {
1850         QScopedPointer<ColorDialog> myDialog( //
1851             new ColorDialog(m_srgbBuildinColorSpace));
1852         QList<double> myValues = //
1853             myDialog->d_pointer->m_rgbSpinBox->sectionValues();
1854         myValues[0] = 10;
1855         myValues[1] = 11;
1856         myValues[2] = 12;
1857         myDialog->d_pointer->m_rgbSpinBox->setSectionValues(myValues);
1858         myDialog->d_pointer->readRgbNumericValues();
1859         QCOMPARE(myDialog->currentColor().red(), 10);
1860         QCOMPARE(myDialog->currentColor().green(), 11);
1861         QCOMPARE(myDialog->currentColor().blue(), 12);
1862     }
1863 
1864 #ifndef MSVC_DLL
1865     void testSetCurrentOpaqueColor()
1866     {
1867         QScopedPointer<ColorDialog> myDialog( //
1868             new ColorDialog(m_srgbBuildinColorSpace));
1869         const auto myMultiColor = AbsoluteColor::allConversions( //
1870             ColorModel::CielchD50,
1871             GenericColor(30, 40, 50));
1872         myDialog->d_pointer->setCurrentOpaqueColor(myMultiColor, nullptr);
1873         QCOMPARE(myDialog->d_pointer->m_currentOpaqueColorAbs, myMultiColor);
1874         QList<double> myValues = //
1875             myDialog->d_pointer->m_rgbSpinBox->sectionValues();
1876         QCOMPARE(qRound(myValues.at(0)), 113);
1877         QCOMPARE(qRound(myValues.at(1)), 53);
1878         QCOMPARE(qRound(myValues.at(2)), 23);
1879     }
1880 #endif
1881 
1882     void testUpdateColorPatch()
1883     {
1884         QScopedPointer<ColorDialog> myDialog( //
1885             new ColorDialog(m_srgbBuildinColorSpace));
1886         myDialog->d_pointer->m_currentOpaqueColorRgb = //
1887             RgbColor::fromRgbQColor(QColor(1, 2, 3));
1888         myDialog->d_pointer->updateColorPatch();
1889         QCOMPARE(myDialog->d_pointer->m_colorPatch->color().red(), 1);
1890         QCOMPARE(myDialog->d_pointer->m_colorPatch->color().green(), 2);
1891         QCOMPARE(myDialog->d_pointer->m_colorPatch->color().blue(), 3);
1892         QCOMPARE(myDialog->d_pointer->m_colorPatch->color().alphaF(), //
1893                  myDialog->d_pointer->m_alphaGradientSlider->value());
1894     }
1895 
1896     void testSizeGrip()
1897     {
1898         // As this dialog can indeed be resized, the size grip should
1899         // be enabled. So, users can see the little triangle at the
1900         // right bottom of the dialog (or the left bottom on a
1901         // right-to-left layout). So, the user will be aware
1902         // that he can indeed resize this dialog, which is
1903         // important as the users are used to the default
1904         // platform dialog, which often does not allow resizing. Therefore,
1905         // by default, QDialog::isSizeGripEnabled() should be true.
1906         // NOTE: Some widget styles like Oxygen or Breeze leave the size grip
1907         // widget invisible; nevertheless it reacts on mouse events. Other
1908         // widget styles indeed show the size grip widget, like Fusion or
1909         // QtCurve.
1910         m_perceptualDialog.reset( //
1911             new ColorDialog(m_srgbBuildinColorSpace));
1912         QCOMPARE(m_perceptualDialog->isSizeGripEnabled(), true);
1913         m_perceptualDialog->show();
1914         QCOMPARE(m_perceptualDialog->isSizeGripEnabled(), true);
1915         m_perceptualDialog->hide();
1916         QCOMPARE(m_perceptualDialog->isSizeGripEnabled(), true);
1917     }
1918 
1919     void testLayoutDimensions()
1920     {
1921         m_perceptualDialog.reset( //
1922             new ColorDialog(m_srgbBuildinColorSpace));
1923         // Test default value
1924         QCOMPARE( //
1925             m_perceptualDialog->layoutDimensions(), //
1926             ColorDialog::DialogLayoutDimensions::Collapsed);
1927 
1928         // Test if values are correctly stored before showing
1929         m_perceptualDialog->setLayoutDimensions( //
1930             ColorDialog::DialogLayoutDimensions::Collapsed);
1931         QCOMPARE( //
1932             m_perceptualDialog->layoutDimensions(), //
1933             ColorDialog::DialogLayoutDimensions::Collapsed);
1934         m_perceptualDialog->setLayoutDimensions( //
1935             ColorDialog::DialogLayoutDimensions::Expanded);
1936         QCOMPARE( //
1937             m_perceptualDialog->layoutDimensions(), //
1938             ColorDialog::DialogLayoutDimensions::Expanded);
1939         m_perceptualDialog->setLayoutDimensions( //
1940             ColorDialog::DialogLayoutDimensions::ScreenSizeDependent);
1941         QCOMPARE( //
1942             m_perceptualDialog->layoutDimensions(), //
1943             ColorDialog::DialogLayoutDimensions::ScreenSizeDependent);
1944 
1945         // Test if values are correctly stored after showing
1946         m_perceptualDialog->show();
1947         m_perceptualDialog->setLayoutDimensions( //
1948             ColorDialog::DialogLayoutDimensions::Collapsed);
1949         QCOMPARE( //
1950             m_perceptualDialog->layoutDimensions(), //
1951             ColorDialog::DialogLayoutDimensions::Collapsed);
1952         m_perceptualDialog->setLayoutDimensions( //
1953             ColorDialog::DialogLayoutDimensions::Expanded);
1954         QCOMPARE( //
1955             m_perceptualDialog->layoutDimensions(), //
1956             ColorDialog::DialogLayoutDimensions::Expanded);
1957         m_perceptualDialog->setLayoutDimensions( //
1958             ColorDialog::DialogLayoutDimensions::ScreenSizeDependent);
1959         QCOMPARE( //
1960             m_perceptualDialog->layoutDimensions(), //
1961             ColorDialog::DialogLayoutDimensions::ScreenSizeDependent);
1962     }
1963 
1964     void testApplyLayoutDimensions()
1965     {
1966         m_perceptualDialog.reset( //
1967             new ColorDialog(m_srgbBuildinColorSpace));
1968         // Test default value
1969         QCOMPARE( //
1970             m_perceptualDialog->layoutDimensions(), //
1971             ColorDialog::DialogLayoutDimensions::Collapsed);
1972 
1973         m_perceptualDialog->d_pointer->m_layoutDimensions = //
1974             ColorDialog::DialogLayoutDimensions::Collapsed;
1975         m_perceptualDialog->d_pointer->applyLayoutDimensions();
1976         int collapsedWidth = m_perceptualDialog->width();
1977 
1978         m_perceptualDialog->d_pointer->m_layoutDimensions = //
1979             ColorDialog::DialogLayoutDimensions::Expanded;
1980         m_perceptualDialog->d_pointer->applyLayoutDimensions();
1981         int expandedWidth = m_perceptualDialog->width();
1982 
1983         QVERIFY2(collapsedWidth < expandedWidth,
1984                  "Verify that collapsed width of the dialog is smaller than "
1985                  "the expanded width.");
1986     }
1987 
1988     void testLayoutDimensionsChanged()
1989     {
1990         m_perceptualDialog.reset( //
1991             new ColorDialog(m_srgbBuildinColorSpace));
1992         m_perceptualDialog->setLayoutDimensions( //
1993             ColorDialog::DialogLayoutDimensions::Collapsed);
1994         QSignalSpy spyPerceptualDialog(
1995             // QObject to spy:
1996             m_perceptualDialog.data(),
1997             // Signal to spy:
1998             &ColorDialog::layoutDimensionsChanged);
1999         // Setting a different DialogLayoutDimensions will emit a signal
2000         m_perceptualDialog->setLayoutDimensions( //
2001             ColorDialog::DialogLayoutDimensions::Expanded);
2002         QCOMPARE(spyPerceptualDialog.count(), 1);
2003         // Setting the same DialogLayoutDimensions will not emit a signal again
2004         m_perceptualDialog->setLayoutDimensions( //
2005             ColorDialog::DialogLayoutDimensions::Expanded);
2006         QCOMPARE(spyPerceptualDialog.count(), 1);
2007     }
2008 
2009     void testRoundingErrors_data()
2010     {
2011         QTest::addColumn<QColor>("color");
2012         QTest::newRow("Qt::yellow") << QColor(Qt::yellow);
2013         QColorFloatType red = 1;
2014         QColorFloatType green = 1;
2015         QColorFloatType blue = 0;
2016         while (blue < 1) {
2017             QTest::newRow( //
2018                 QStringLiteral("RGB %1 %2 %3") //
2019                     .arg(red) //
2020                     .arg(green) //
2021                     .arg(blue) //
2022                     .toUtf8() //
2023                     .data()) //
2024                 << QColor::fromRgbF(red, green, blue);
2025             blue += 0.1f;
2026         }
2027     }
2028 
2029     void testRoundingErrors()
2030     {
2031         QFETCH(QColor, color);
2032 
2033         // Moving around between the widgets with the Tab key should
2034         // never trigger a value change. (There could be a value
2035         // change because of rounding errors if the value gets updated
2036         // after the focus leaves, even though no editing has been
2037         // done. This would not be correct, and this test controls this.)
2038 
2039         m_perceptualDialog.reset( //
2040             new ColorDialog(m_srgbBuildinColorSpace));
2041         m_perceptualDialog->setCurrentColor(color);
2042         m_perceptualDialog->show();
2043         QApplication::setActiveWindow(m_perceptualDialog.data());
2044         // Using a QList instead of a simple QWidget* to make sure
2045         // not to have infinite loops even is focus passing might be
2046         // broken and never returns to the original focus.
2047         QList<QWidget *> oldFocusWidgets;
2048         for (int i = 0; //
2049              i < m_perceptualDialog->d_pointer->m_tabWidget->count(); //
2050              i++ //
2051         ) {
2052             m_perceptualDialog->d_pointer->m_tabWidget->setCurrentIndex(i);
2053             oldFocusWidgets.clear();
2054             const QColor oldColor = m_perceptualDialog->currentColor();
2055             const auto oldOpaqueColorAbs = //
2056                 m_perceptualDialog->d_pointer->m_currentOpaqueColorAbs;
2057             const auto oldOpaqueColorRgb = //
2058                 m_perceptualDialog->d_pointer->m_currentOpaqueColorRgb;
2059             bool focusPassingIsWorking = true;
2060             while ( //
2061                 focusPassingIsWorking //
2062                 && (!oldFocusWidgets.contains(QApplication::focusWidget())) //
2063             ) {
2064                 oldFocusWidgets.append(QApplication::focusWidget());
2065                 focusPassingIsWorking = m_perceptualDialog->focusNextChild();
2066                 QCOMPARE(oldColor, m_perceptualDialog->currentColor());
2067                 QVERIFY( //
2068                     oldOpaqueColorAbs //
2069                     == m_perceptualDialog->d_pointer->m_currentOpaqueColorAbs);
2070                 QVERIFY( //
2071                     oldOpaqueColorRgb //
2072                     == m_perceptualDialog->d_pointer->m_currentOpaqueColorRgb);
2073             }
2074         };
2075     }
2076 
2077     void testYellowRounding()
2078     {
2079         // During development was observed a particular bug for which
2080         // we test here.
2081 
2082         // As we expect rounding errors, we define a tolerance range,
2083         // which is used both for the assertions and for the actual test.
2084         // This is necessary to guarantee that this test does not produce
2085         // false-positives just because the rounding behaviour of the
2086         // library has changed.
2087         const int toleranceRange = 1;
2088 
2089         // Create a ColorDialog
2090         m_perceptualDialog.reset( //
2091             new ColorDialog(m_srgbBuildinColorSpace));
2092 
2093         // Start with Qt::yellow as initial color.
2094         // If this RGB value is interpreted in the sRGB (LittleCMS build-in)
2095         // profile, it has a particular property. Because of the irregular
2096         // shape of the sRGB color space at this position, thinking in
2097         // LCH values, when reducing (!) the chroma step-by-step, we run
2098         // out-of-gamut, before going again in-gamut at even lower chroma
2099         // values.
2100         m_perceptualDialog->setCurrentColor(QColor(Qt::yellow));
2101         // The value is also converted to HLC 100°, 98%, 95 (rounded)
2102         // visible in the HLC spin box.
2103         QList<double> hlc = //
2104             m_perceptualDialog->d_pointer->m_ciehlcD50SpinBox->sectionValues();
2105         QVERIFY(hlc.at(0) >= 100 - toleranceRange); // assertion
2106         QVERIFY(hlc.at(0) <= 100 + toleranceRange); // assertion
2107         QVERIFY(hlc.at(1) >= 98 - toleranceRange); // assertion
2108         QVERIFY(hlc.at(1) <= 98 + toleranceRange); // assertion
2109         QVERIFY(hlc.at(2) >= 95 - toleranceRange); // assertion
2110         QVERIFY(hlc.at(2) <= 95 + toleranceRange); // assertion
2111         // Now, the user clicks on the “Apply” button within the HLC spin box.
2112         // We simulate this by simply calling the slot that is connected
2113         // to this action:
2114         m_perceptualDialog->d_pointer->readHlcNumericValues();
2115         // Now, during development there was a bug observed: The buggy
2116         // behaviour was that the chroma value was changed from 95 to 24.
2117         // The expected result was that the chroma value only changes
2118         // slightly because of rounding (or ideally not at all).
2119         hlc = m_perceptualDialog->d_pointer->m_ciehlcD50SpinBox->sectionValues();
2120         QVERIFY(hlc.at(2) >= 95 - toleranceRange);
2121         QVERIFY(hlc.at(2) <= 95 + toleranceRange);
2122     }
2123 
2124     void testBlackHSV()
2125     {
2126         // In the HSV color space, if V is 0 then the color is black.
2127         // Both, H and S are meaningless. When converting from other
2128         // color spaces, they get probably a default value. However,
2129         // when the user is editing the HSV spin box, we does not expect
2130         // that H or S change when switching from one section to another
2131         // or when the focus leaves. Make sure that H and S are preserved
2132         // during editing even if V becomes 0:
2133 
2134         // Create a ColorDialog
2135         m_perceptualDialog.reset( //
2136             new ColorDialog(m_srgbBuildinColorSpace));
2137 
2138         const QList<double> hsvTestData{201, 33, 0};
2139         m_perceptualDialog->d_pointer->m_hsvSpinBox->setSectionValues( //
2140             hsvTestData);
2141         QCOMPARE( //
2142             m_perceptualDialog->d_pointer->m_hsvSpinBox->sectionValues(), //
2143             hsvTestData);
2144         m_perceptualDialog->d_pointer->readHsvNumericValues();
2145         QCOMPARE( //
2146             m_perceptualDialog->d_pointer->m_hsvSpinBox->sectionValues(), //
2147             hsvTestData);
2148     }
2149 
2150     void testRoundingMultipleError()
2151     {
2152         // This is a test for a bug discovered during development.
2153 
2154         // Create a ColorDialog:
2155         m_perceptualDialog.reset( //
2156             new ColorDialog(m_srgbBuildinColorSpace));
2157 
2158         // The user puts into the HLC spin box the value 100° 98% 94:
2159         m_perceptualDialog->d_pointer->m_ciehlcD50SpinBox->setSectionValues( //
2160             QList<double>{100, 98, 94});
2161         // This is an out-of-gamut color which is not corrected until
2162         // the focus will leave the widget or the Return key is pressed.
2163         // A nearby in-gamut color is around 100° 98% 24; this color
2164         // is used internally to perform the conversion to RGB and other
2165         // color spaces. (It is however still not visible in the HLC
2166         // spin box.)
2167         //
2168         // The RGB spin box becomes:
2169         const QList<double> expectedRgbValues{255, 251, 202};
2170         QCOMPARE( //
2171             m_perceptualDialog->d_pointer->m_rgbSpinBox->sectionValues(), //
2172             expectedRgbValues);
2173         // Now, the user finishes the editing process (the focus leaves
2174         // the widget or the Return key is pressed or the action button
2175         // is clicked):
2176         m_perceptualDialog->d_pointer->updateHlcButBlockSignals();
2177         // The buggy result during development phase was an RGB value
2178         // of 252 254 4. Why?
2179         // - The internal value was around 100° 97% 94, but not exactly.
2180         // - Now, the exact (!) value of 100° 97% 94 is applied, and this
2181         //   one, converted to RGB, triggers a different rounding.
2182         // The expected result is however still the very same RGB value
2183         // as above:
2184         QCOMPARE( //
2185             m_perceptualDialog->d_pointer->m_rgbSpinBox->sectionValues(), //
2186             expectedRgbValues);
2187     }
2188 
2189 #ifndef MSVC_DLL
2190     void testRgbHexRounding()
2191     {
2192         // This is a test for a bug discovered during development.
2193         // QColor can produce a QString that contains a hexadecimal
2194         // (integer) representation of the color, just as used in
2195         // HTML. Example: #0000FF is blue. When rounding to
2196         // integers, apparently it does not use round(), but floor().
2197         // That is not useful and not inconsistent with the rest of our
2198         // dialog. We want correct rounding!
2199 
2200         // Create a ColorDialog:
2201         m_perceptualDialog.reset( //
2202             new ColorDialog(m_srgbBuildinColorSpace));
2203 
2204         // Set a color that triggers the rounding error:
2205         GenericColor testColorLch{97, 94, 100};
2206         m_perceptualDialog->d_pointer->setCurrentOpaqueColor(
2207             // Color:
2208             AbsoluteColor::allConversions( //
2209                 ColorModel::CielchD50, //
2210                 testColorLch),
2211             // Widget to ignore:
2212             nullptr);
2213 
2214         // Get the actual result
2215         QColor actualHex;
2216         actualHex.setNamedColor( //
2217             m_perceptualDialog->d_pointer->m_rgbLineEdit->text());
2218 
2219         // Get the expected result (We assume our own RGB spin box rounds
2220         // correctly.)
2221         const QList<double> rgbList =
2222             // The the values from the MultiSpinBox:
2223             m_perceptualDialog->d_pointer->m_rgbSpinBox->sectionValues();
2224         QColor expectedHex = QColor::fromRgb(
2225             // The MultiSpinBox might have decimal places, so we round
2226             // here again.
2227             qRound(rgbList.at(0)),
2228             qRound(rgbList.at(1)),
2229             qRound(rgbList.at(2)));
2230 
2231         // Compare
2232         QCOMPARE(actualHex, expectedHex);
2233     }
2234 #endif
2235 
2236 #ifndef MSVC_DLL
2237     void testBugMaximumLightness()
2238     {
2239         QScopedPointer<QTemporaryFile> wideGamutProfile(
2240             // Create a temporary actual file…
2241             QTemporaryFile::createNativeFile(
2242                 // …from the content of this resource:
2243                 QStringLiteral(":/testbed/Compact-ICC-Profiles/Compact-ICC-Profiles/profiles/WideGamutCompat-v4.icc")));
2244         if (wideGamutProfile.isNull()) {
2245             throw 0;
2246         }
2247         // This test looks for a bug that was seen during development
2248         // phase. When using WideGamutRGB and raising the lightness
2249         // slider up to 100%: Bug behaviour: the color switches
2250         // to 0% lightness. Expected behaviour: the color has almost
2251         // 100% lightness.
2252         auto myColorSpace = RgbColorSpace::createFromFile( //
2253             wideGamutProfile->fileName());
2254         QCOMPARE(myColorSpace.isNull(), false); // assertion
2255         m_perceptualDialog.reset( //
2256             new ColorDialog(myColorSpace));
2257         QTest::keyClick( //
2258             m_perceptualDialog->d_pointer->m_lchLightnessSelector, //
2259             Qt::Key_End);
2260         QVERIFY( //
2261             m_perceptualDialog->d_pointer->m_currentOpaqueColorAbs.value(ColorModel::CielchD50).first > 95);
2262     }
2263 #endif
2264 
2265     void testExplicitShortcutsExpanded()
2266     {
2267         // Make sure to have mnemonics (like Qt::ALT+Qt::Key_X for "E&xit")
2268         // enabled, also on platforms that disable it by default.
2269         qt_set_sequence_auto_mnemonic(true);
2270 
2271         setTranslation(QCoreApplication::instance(), //
2272                        QLocale(QLocale::English).uiLanguages());
2273         // Create a ColorDialog:
2274         m_perceptualDialog.reset( //
2275             new ColorDialog(m_srgbBuildinColorSpace));
2276         m_perceptualDialog->setLayoutDimensions( //
2277             ColorDialog::DialogLayoutDimensions::Expanded);
2278         m_perceptualDialog->show();
2279         QApplication::setActiveWindow(m_perceptualDialog.data());
2280         auto &d = m_perceptualDialog->d_pointer;
2281 
2282         d->m_tabWidget->setFocus();
2283         QTest::keyClick(QApplication::focusWidget(), //
2284                         Qt::Key_B, //
2285                         Qt::AltModifier);
2286         QCOMPARE(d->m_tabWidget->currentIndex(), //
2287                  d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget));
2288 
2289         d->m_tabWidget->setFocus();
2290         QTest::keyClick(QApplication::focusWidget(), //
2291                         Qt::Key_H, //
2292                         Qt::AltModifier);
2293         QCOMPARE(d->m_tabWidget->currentIndex(), //
2294                  d->m_tabWidget->indexOf(d->m_hueFirstWrapperWidget));
2295 
2296         d->m_tabWidget->setFocus();
2297         QTest::keyClick(QApplication::focusWidget(), //
2298                         Qt::Key_L, //
2299                         Qt::AltModifier);
2300         QCOMPARE(d->m_tabWidget->currentIndex(), //
2301                  d->m_tabWidget->indexOf(d->m_lightnessFirstWrapperWidget));
2302 
2303         // Return to "Basic colors" tab
2304         d->m_tabWidget->setFocus();
2305         QTest::keyClick(QApplication::focusWidget(), //
2306                         Qt::Key_B, //
2307                         Qt::AltModifier);
2308         QCOMPARE(d->m_tabWidget->currentIndex(), //
2309                  d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget));
2310 
2311         // The shortcut for the "Numerical" works.
2312         d->m_tabWidget->setFocus();
2313         QTest::keyClick(QApplication::focusWidget(), //
2314                         Qt::Key_N, //
2315                         Qt::AltModifier);
2316         QCOMPARE(d->m_tabWidget->currentIndex(), //
2317                  d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget));
2318 
2319         const auto indexOfSwatchBookTab = //
2320             d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget);
2321         const auto toolTip = d->m_tabWidget->tabToolTip(indexOfSwatchBookTab);
2322         const auto toolTipContainsUnderline = //
2323             toolTip.contains(QStringLiteral("<u>"));
2324         QCOMPARE(toolTipContainsUnderline, true);
2325     }
2326 
2327     void testExplicitShortcutsCollapsed()
2328     {
2329         // Make sure to have mnemonics (like Qt::ALT+Qt::Key_X for "E&xit")
2330         // enabled, also on platforms that disable it by default.
2331         qt_set_sequence_auto_mnemonic(true);
2332 
2333         setTranslation(QCoreApplication::instance(), //
2334                        QLocale(QLocale::English).uiLanguages());
2335         // Create a ColorDialog:
2336         m_perceptualDialog.reset( //
2337             new ColorDialog(m_srgbBuildinColorSpace));
2338         m_perceptualDialog->setLayoutDimensions( //
2339             ColorDialog::DialogLayoutDimensions::Collapsed);
2340         m_perceptualDialog->show();
2341         QApplication::setActiveWindow(m_perceptualDialog.data());
2342         auto &d = m_perceptualDialog->d_pointer;
2343 
2344         d->m_tabWidget->setFocus();
2345         QTest::keyClick(QApplication::focusWidget(), //
2346                         Qt::Key_B, //
2347                         Qt::AltModifier);
2348         QCOMPARE(d->m_tabWidget->currentIndex(), //
2349                  d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget));
2350 
2351         d->m_tabWidget->setFocus();
2352         QTest::keyClick(QApplication::focusWidget(), //
2353                         Qt::Key_H, //
2354                         Qt::AltModifier);
2355         QCOMPARE(d->m_tabWidget->currentIndex(), //
2356                  d->m_tabWidget->indexOf(d->m_hueFirstWrapperWidget));
2357 
2358         d->m_tabWidget->setFocus();
2359         QTest::keyClick(QApplication::focusWidget(), //
2360                         Qt::Key_L, //
2361                         Qt::AltModifier);
2362         QCOMPARE(d->m_tabWidget->currentIndex(), //
2363                  d->m_tabWidget->indexOf(d->m_lightnessFirstWrapperWidget));
2364 
2365         // Return to "Basic colors" tab
2366         d->m_tabWidget->setFocus();
2367         QTest::keyClick(QApplication::focusWidget(), //
2368                         Qt::Key_B, //
2369                         Qt::AltModifier);
2370         QCOMPARE(d->m_tabWidget->currentIndex(), //
2371                  d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget));
2372 
2373         // The shortcut for the "Numerical" works.
2374         d->m_tabWidget->setFocus();
2375         QTest::keyClick(QApplication::focusWidget(), //
2376                         Qt::Key_N, //
2377                         Qt::AltModifier);
2378         QCOMPARE(d->m_tabWidget->currentIndex(), //
2379                  d->m_tabWidget->indexOf(d->m_numericalWidget));
2380 
2381         const auto indexOfSwatchBookTab = //
2382             d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget);
2383         const auto toolTip = d->m_tabWidget->tabToolTip(indexOfSwatchBookTab);
2384         const auto toolTipContainsUnderline = //
2385             toolTip.contains(QStringLiteral("<u>"));
2386         QCOMPARE(toolTipContainsUnderline, true);
2387     }
2388 
2389     void testExplicitShortcutsExpandedDisabled()
2390     {
2391         // Make sure to have mnemonics (like Qt::ALT+Qt::Key_X for "E&xit")
2392         // disabled, also on platforms that enable it by default.
2393         qt_set_sequence_auto_mnemonic(false);
2394 
2395         setTranslation(QCoreApplication::instance(), //
2396                        QLocale(QLocale::English).uiLanguages());
2397         // Create a ColorDialog:
2398         m_perceptualDialog.reset( //
2399             new ColorDialog(m_srgbBuildinColorSpace));
2400         m_perceptualDialog->setLayoutDimensions( //
2401             ColorDialog::DialogLayoutDimensions::Expanded);
2402         m_perceptualDialog->show();
2403         QApplication::setActiveWindow(m_perceptualDialog.data());
2404         auto &d = m_perceptualDialog->d_pointer;
2405 
2406         const auto originalIndex = d->m_tabWidget->currentIndex();
2407 
2408         // NOTE When mnemonics are disabled, all these key clicks should
2409         // not have any effect.
2410 
2411         d->m_tabWidget->setFocus();
2412         QTest::keyClick(QApplication::focusWidget(), //
2413                         Qt::Key_B, //
2414                         Qt::AltModifier);
2415         QCOMPARE(d->m_tabWidget->currentIndex(), //
2416                  originalIndex);
2417 
2418         d->m_tabWidget->setFocus();
2419         QTest::keyClick(QApplication::focusWidget(), //
2420                         Qt::Key_H, //
2421                         Qt::AltModifier);
2422         QCOMPARE(d->m_tabWidget->currentIndex(), //
2423                  originalIndex);
2424 
2425         d->m_tabWidget->setFocus();
2426         QTest::keyClick(QApplication::focusWidget(), //
2427                         Qt::Key_L, //
2428                         Qt::AltModifier);
2429         QCOMPARE(d->m_tabWidget->currentIndex(), //
2430                  originalIndex);
2431 
2432         d->m_tabWidget->setFocus();
2433         QTest::keyClick(QApplication::focusWidget(), //
2434                         Qt::Key_N, //
2435                         Qt::AltModifier);
2436         QCOMPARE(d->m_tabWidget->currentIndex(), //
2437                  originalIndex);
2438 
2439         const auto indexOfSwatchBookTab = //
2440             d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget);
2441         const auto toolTip = d->m_tabWidget->tabToolTip(indexOfSwatchBookTab);
2442         const auto toolTipContainsUnderline = //
2443             toolTip.contains(QStringLiteral("<u>"));
2444         QCOMPARE(toolTipContainsUnderline, false);
2445     }
2446 
2447     void testExplicitShortcutsCollapsedDisabled()
2448     {
2449         // Make sure to have mnemonics (like Qt::ALT+Qt::Key_X for "E&xit")
2450         // disabled, also on platforms that enable it by default.
2451         qt_set_sequence_auto_mnemonic(false);
2452 
2453         setTranslation(QCoreApplication::instance(), //
2454                        QLocale(QLocale::English).uiLanguages());
2455         // Create a ColorDialog:
2456         m_perceptualDialog.reset( //
2457             new ColorDialog(m_srgbBuildinColorSpace));
2458         m_perceptualDialog->setLayoutDimensions( //
2459             ColorDialog::DialogLayoutDimensions::Collapsed);
2460         m_perceptualDialog->show();
2461         QApplication::setActiveWindow(m_perceptualDialog.data());
2462         auto &d = m_perceptualDialog->d_pointer;
2463 
2464         const auto originalIndex = d->m_tabWidget->currentIndex();
2465 
2466         // NOTE When mnemonics are disabled, all these key clicks should
2467         // not have any effect.
2468 
2469         d->m_tabWidget->setFocus();
2470         QTest::keyClick(QApplication::focusWidget(), //
2471                         Qt::Key_B, //
2472                         Qt::AltModifier);
2473         QCOMPARE(d->m_tabWidget->currentIndex(), //
2474                  originalIndex);
2475 
2476         d->m_tabWidget->setFocus();
2477         QTest::keyClick(QApplication::focusWidget(), //
2478                         Qt::Key_H, //
2479                         Qt::AltModifier);
2480         QCOMPARE(d->m_tabWidget->currentIndex(), //
2481                  originalIndex);
2482 
2483         d->m_tabWidget->setFocus();
2484         QTest::keyClick(QApplication::focusWidget(), //
2485                         Qt::Key_L, //
2486                         Qt::AltModifier);
2487         QCOMPARE(d->m_tabWidget->currentIndex(), //
2488                  originalIndex);
2489 
2490         d->m_tabWidget->setFocus();
2491         QTest::keyClick(QApplication::focusWidget(), //
2492                         Qt::Key_N, //
2493                         Qt::AltModifier);
2494         QCOMPARE(d->m_tabWidget->currentIndex(), //
2495                  originalIndex);
2496 
2497         const auto indexOfSwatchBookTab = //
2498             d->m_tabWidget->indexOf(d->m_swatchBookWrapperWidget);
2499         const auto toolTip = d->m_tabWidget->tabToolTip(indexOfSwatchBookTab);
2500         const auto toolTipContainsUnderline = //
2501             toolTip.contains(QStringLiteral("<u>"));
2502         QCOMPARE(toolTipContainsUnderline, false);
2503     }
2504 
2505     void testSnippet02()
2506     {
2507         snippet02();
2508     }
2509 
2510     void testSnippet03()
2511     {
2512         snippet03();
2513     }
2514 
2515     void testSnippet05()
2516     {
2517         TestColorDialogSnippetClass mySnippets;
2518         mySnippets.testSnippet05();
2519     }
2520 
2521     void benchmarkCreateAndShowPerceptualDialog()
2522     {
2523         m_perceptualDialog.reset(nullptr);
2524         QBENCHMARK {
2525             m_perceptualDialog.reset( //
2526                 new ColorDialog(m_srgbBuildinColorSpace));
2527             m_perceptualDialog->show();
2528             m_perceptualDialog->repaint();
2529             m_perceptualDialog.reset(nullptr);
2530         }
2531     }
2532 
2533     void benchmarkCreateAndShowMaximizedPerceptualDialog()
2534     {
2535         m_perceptualDialog.reset(nullptr);
2536         QBENCHMARK {
2537             m_perceptualDialog.reset( //
2538                 new ColorDialog(m_srgbBuildinColorSpace));
2539             m_perceptualDialog->showMaximized();
2540             m_perceptualDialog->repaint();
2541             m_perceptualDialog.reset(nullptr);
2542         }
2543     }
2544 
2545     void benchmarkCreateAndShowQColorDialog()
2546     {
2547         m_qDialog.reset(nullptr);
2548         QBENCHMARK {
2549             m_qDialog.reset(new QColorDialog);
2550             m_qDialog->show();
2551             m_qDialog->repaint();
2552             m_perceptualDialog.reset(nullptr);
2553         }
2554     }
2555 
2556     void benchmarkChangeColorPerceptualHueBased()
2557     {
2558         setTranslation(QCoreApplication::instance(), //
2559                        QLocale(QLocale::English).uiLanguages());
2560         m_perceptualDialog.reset( //
2561             new ColorDialog(m_srgbBuildinColorSpace));
2562         m_perceptualDialog->show();
2563 
2564         QTabWidget *theTabWidget = //
2565             m_perceptualDialog->findChild<QTabWidget *>();
2566         QVERIFY2(theTabWidget != nullptr, //
2567                  "Assert that theTabWidget has actually been found.");
2568         constexpr int myIndex = 1;
2569         // Assert that we got the correct tab widget:
2570         QCOMPARE(theTabWidget->tabToolTip(myIndex), //
2571                  QStringLiteral("<a/><u>H</u>ue-based"));
2572         theTabWidget->setCurrentIndex(myIndex);
2573 
2574         QBENCHMARK {
2575             m_perceptualDialog->setCurrentColor(Qt::green);
2576             m_perceptualDialog->repaint();
2577             m_perceptualDialog->setCurrentColor(Qt::blue);
2578             m_perceptualDialog->repaint();
2579             m_perceptualDialog->setCurrentColor(Qt::yellow);
2580             m_perceptualDialog->repaint();
2581         }
2582     }
2583 
2584     void benchmarkChangeColorPerceptualLightnessBased()
2585     {
2586         setTranslation(QCoreApplication::instance(), //
2587                        QLocale(QLocale::English).uiLanguages());
2588         m_perceptualDialog.reset( //
2589             new ColorDialog(m_srgbBuildinColorSpace));
2590         m_perceptualDialog->show();
2591 
2592         QTabWidget *theTabWidget = //
2593             m_perceptualDialog->findChild<QTabWidget *>();
2594         QVERIFY2(theTabWidget != nullptr, //
2595                  "Assert that theTabWidget has actually been found.");
2596         constexpr int myIndex = 2;
2597         // Assert that we got the correct tab widget:
2598         QCOMPARE(theTabWidget->tabToolTip(myIndex), //
2599                  QStringLiteral("<a/><u>L</u>ightness-based"));
2600         theTabWidget->setCurrentIndex(myIndex);
2601 
2602         QBENCHMARK {
2603             m_perceptualDialog->setCurrentColor(Qt::green);
2604             m_perceptualDialog->repaint();
2605             m_perceptualDialog->setCurrentColor(Qt::blue);
2606             m_perceptualDialog->repaint();
2607             m_perceptualDialog->setCurrentColor(Qt::yellow);
2608             m_perceptualDialog->repaint();
2609         }
2610     }
2611 
2612     void benchmarkChangeColorQColorDialog()
2613     {
2614         m_qDialog.reset(new QColorDialog);
2615         m_qDialog->show();
2616         QBENCHMARK {
2617             m_qDialog->setCurrentColor(Qt::green);
2618             m_qDialog->repaint();
2619             m_qDialog->setCurrentColor(Qt::blue);
2620             m_qDialog->repaint();
2621             m_qDialog->setCurrentColor(Qt::yellow);
2622             m_qDialog->repaint();
2623         }
2624     }
2625 
2626     // The last unit tests are those who need to change the locale.
2627     // To avoid side-effects, these unit tests are the last ones.
2628 
2629     void testChangeEventRetranslate()
2630     {
2631         initializeTranslation(QCoreApplication::instance(), //
2632                               QLocale(QLocale::English).uiLanguages());
2633         m_perceptualDialog.reset( //
2634             new ColorDialog(m_srgbBuildinColorSpace));
2635 
2636         // We test various translations, because we do not know the locale
2637         // of the system on which this unit test will run. As there might
2638         // be some coincidence between QLocale::system() and QLocale(),
2639         // we want to be sure and therefore test three translations.
2640 
2641         initializeTranslation(QCoreApplication::instance(), //
2642                               QLocale(QLocale::Dutch).uiLanguages());
2643         {
2644             QEvent temp(QEvent::LanguageChange);
2645             QCoreApplication::sendEvent(m_perceptualDialog.data(), &temp);
2646         }
2647         QCOMPARE(m_perceptualDialog->d_pointer->m_buttonCancel->text(), //
2648                  QStringLiteral("&Annuleren"));
2649 
2650         initializeTranslation(QCoreApplication::instance(), //
2651                               QLocale(QLocale::Catalan).uiLanguages());
2652         {
2653             QEvent temp(QEvent::LanguageChange);
2654             QCoreApplication::sendEvent(m_perceptualDialog.data(), &temp);
2655         }
2656         QCOMPARE(m_perceptualDialog->d_pointer->m_buttonCancel->text(), //
2657                  QStringLiteral("&Cancel·la"));
2658 
2659         initializeTranslation(QCoreApplication::instance(), //
2660                               QLocale(QLocale::Spanish).uiLanguages());
2661         {
2662             QEvent temp(QEvent::LanguageChange);
2663             QCoreApplication::sendEvent(m_perceptualDialog.data(), &temp);
2664         }
2665         QCOMPARE(m_perceptualDialog->d_pointer->m_buttonCancel->text(), //
2666                  QStringLiteral("&Cancelar"));
2667     }
2668 
2669     void testChangeEventRetranslateButtons()
2670     {
2671         initializeTranslation(QCoreApplication::instance(), //
2672                               QLocale(QLocale::English).uiLanguages());
2673         m_perceptualDialog.reset( //
2674             new ColorDialog(m_srgbBuildinColorSpace));
2675 
2676         // There is a particular reason for testing the translation of
2677         // the dialog buttons. We are using standard-buttons generated
2678         // by QDialogButtonBox. These standard-buttons apparently are
2679         // re-translated by QDialogButtonBox, which hooks into LanuageChange
2680         // events of its parent widgets. (LanuageChange events originally
2681         // only go to the top-level widgets.) This is a problem, because
2682         // it might change the text of the buttons from our own translation
2683         // to the Qt translation. Furthermore, QDialogButtonBox uses
2684         // QLocale::system() while we use QLocale(), which means we could
2685         // end up with two different languages after calling retranslate()
2686         // if the implementation would not prevent this problem. Therefore,
2687         // here we test if the implementation actually prevents this problem.
2688 
2689         initializeTranslation(QCoreApplication::instance(), //
2690                               QLocale(QLocale::Dutch).uiLanguages());
2691         {
2692             QEvent temp(QEvent::LanguageChange);
2693             QCoreApplication::sendEvent(m_perceptualDialog.data(), &temp);
2694         }
2695         QCOMPARE(m_perceptualDialog->d_pointer->m_buttonCancel->text(), //
2696                  QStringLiteral("&Annuleren"));
2697 
2698         initializeTranslation(QCoreApplication::instance(), //
2699                               QLocale(QLocale::Catalan).uiLanguages());
2700         {
2701             QEvent temp(QEvent::LanguageChange);
2702             QCoreApplication::sendEvent(m_perceptualDialog.data(), &temp);
2703         }
2704         QCOMPARE(m_perceptualDialog->d_pointer->m_buttonCancel->text(), //
2705                  QStringLiteral("&Cancel·la"));
2706 
2707         initializeTranslation(QCoreApplication::instance(), //
2708                               QLocale(QLocale::Spanish).uiLanguages());
2709         {
2710             QEvent temp(QEvent::LanguageChange);
2711             QCoreApplication::sendEvent(m_perceptualDialog.data(), &temp);
2712         }
2713         QCOMPARE(m_perceptualDialog->d_pointer->m_buttonCancel->text(), //
2714                  QStringLiteral("&Cancelar"));
2715     }
2716 
2717     void testRestoreTab_data()
2718     {
2719         QTest::addColumn<ColorDialog::DialogLayoutDimensions>("layout");
2720 
2721         QTest::newRow("Collapsed") //
2722             << ColorDialog::DialogLayoutDimensions::Collapsed;
2723         QTest::newRow("Expanded") //
2724             << ColorDialog::DialogLayoutDimensions::Expanded;
2725         QTest::newRow("ScreenSizeDependent") //
2726             << ColorDialog::DialogLayoutDimensions::ScreenSizeDependent;
2727     }
2728 
2729     void testRestoreTab()
2730     {
2731         QFETCH(ColorDialog::DialogLayoutDimensions, layout);
2732         m_perceptualDialog.reset( //
2733             new ColorDialog(m_srgbBuildinColorSpace));
2734         m_perceptualDialog->setLayoutDimensions(layout);
2735         m_perceptualDialog->show(); // Needed for correct m_tabWidget->count()
2736 
2737         QList<int> testList;
2738         const auto tabCount = m_perceptualDialog->d_pointer->m_tabWidget->count();
2739         testList.reserve(tabCount + 1);
2740         for (int i = 0; i < tabCount; ++i) {
2741             testList.append(i);
2742         }
2743         testList.append(0); // Finally return again to the first tab.
2744 
2745         for (const auto i : std::as_const(testList)) {
2746             m_perceptualDialog->d_pointer->m_tabWidget->setCurrentIndex(i);
2747             m_perceptualDialog.reset( //
2748                 new ColorDialog(m_srgbBuildinColorSpace));
2749             // The last tab is only restored on show().
2750             m_perceptualDialog->setLayoutDimensions(layout);
2751             m_perceptualDialog->show();
2752             QCOMPARE(m_perceptualDialog->d_pointer->m_tabWidget->currentIndex(), i);
2753         }
2754     }
2755 
2756 private:
2757     void unused()
2758     {
2759         // These will not be called in the unit tests because getColor()
2760         // does not return without user interaction!
2761         // They are noted here to avoid a warning about “unused function”.
2762         snippet01();
2763         snippet04();
2764     }
2765 };
2766 
2767 } // namespace PerceptualColor
2768 
2769 QTEST_MAIN(PerceptualColor::TestColorDialog)
2770 // The following “include” is necessary because we do not use a header file:
2771 #include "testcolordialog.moc"