File indexing completed on 2024-07-14 08:12:54

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 "wheelcolorpicker.h"
0007 // Second, the private implementation.
0008 #include "wheelcolorpicker_p.h" // IWYU pragma: keep
0009 
0010 #include "chromalightnessdiagram.h"
0011 #include "colorwheel.h"
0012 #include "constpropagatinguniquepointer.h"
0013 #include "lchdouble.h"
0014 #include "rgbcolorspace.h"
0015 #include "rgbcolorspacefactory.h"
0016 #include <qglobal.h>
0017 #include <qobject.h>
0018 #include <qpointer.h>
0019 #include <qsharedpointer.h>
0020 #include <qsignalspy.h>
0021 #include <qsize.h>
0022 #include <qtest.h>
0023 #include <qtestcase.h>
0024 
0025 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0026 #include <qtmetamacros.h>
0027 #else
0028 #include <qobjectdefs.h>
0029 #include <qstring.h>
0030 #endif
0031 
0032 namespace PerceptualColor
0033 {
0034 class TestWheelColorPicker : public QObject
0035 {
0036     Q_OBJECT
0037 
0038 public:
0039     explicit TestWheelColorPicker(QObject *parent = nullptr)
0040         : QObject(parent)
0041     {
0042     }
0043 
0044 private:
0045     QSharedPointer<RgbColorSpace> m_rgbColorSpace = RgbColorSpaceFactory::createSrgb();
0046 
0047 private Q_SLOTS:
0048     void initTestCase()
0049     {
0050         // Called before the first test function is executed
0051     }
0052 
0053     void cleanupTestCase()
0054     {
0055         // Called after the last test function was executed
0056     }
0057 
0058     void init()
0059     {
0060         // Called before each test function is executed
0061     }
0062 
0063     void cleanup()
0064     {
0065         // Called after every test function
0066     }
0067 
0068     void testConstructorDestructor()
0069     {
0070         // Test for crash in constructor or destructor
0071         WheelColorPicker test{m_rgbColorSpace};
0072     }
0073 
0074     void testCurrentColorProperty()
0075     {
0076         WheelColorPicker test{m_rgbColorSpace};
0077         LchDouble color;
0078         color.l = 50;
0079         color.c = 20;
0080         color.h = 10;
0081         test.setCurrentColor(color);
0082         QSignalSpy spy(&test, &WheelColorPicker::currentColorChanged);
0083         QCOMPARE(spy.count(), 0);
0084 
0085         // Change hue only:
0086         color.h += 1;
0087         test.setCurrentColor(color);
0088         QCOMPARE(spy.count(), 1);
0089         QCOMPARE(test.d_pointer->m_chromaLightnessDiagram->currentColor().h, color.h);
0090         QCOMPARE(test.d_pointer->m_colorWheel->hue(), color.h);
0091 
0092         // Change chroma only:
0093         color.c += 1;
0094         test.setCurrentColor(color);
0095         QCOMPARE(spy.count(), 2);
0096         QCOMPARE(test.d_pointer->m_chromaLightnessDiagram->currentColor().c, color.c);
0097         QCOMPARE(test.d_pointer->m_colorWheel->hue(), color.h);
0098 
0099         // Not changing the color should not trigger the signal
0100         test.setCurrentColor(color);
0101         QCOMPARE(spy.count(), 2);
0102         QCOMPARE(test.d_pointer->m_chromaLightnessDiagram->currentColor().c, color.c);
0103         QCOMPARE(test.d_pointer->m_colorWheel->hue(), color.h);
0104     }
0105 
0106     void testSizeHints()
0107     {
0108         WheelColorPicker test{m_rgbColorSpace};
0109         QVERIFY(test.minimumSizeHint().width() <= test.sizeHint().width());
0110         QVERIFY(test.minimumSizeHint().height() <= test.sizeHint().height());
0111     }
0112 
0113     void testVerySmallWidgetSizes()
0114     {
0115         // Also very small widget sizes should not crash the widget.
0116         // This might happen because of divisions by 0, even when the widget
0117         // is bigger than 0 because of borders or offsets. We test this
0118         // here with various small sizes, always forcing in immediate
0119         // re-paint.
0120         WheelColorPicker myWidget{m_rgbColorSpace};
0121         myWidget.show();
0122         myWidget.resize(QSize());
0123         myWidget.repaint();
0124         myWidget.resize(QSize(-1, -1));
0125         myWidget.repaint();
0126         myWidget.resize(QSize(-1, 0));
0127         myWidget.repaint();
0128         myWidget.resize(QSize(0, -1));
0129         myWidget.repaint();
0130         myWidget.resize(QSize(0, 1));
0131         myWidget.repaint();
0132         myWidget.resize(QSize(1, 0));
0133         myWidget.repaint();
0134         myWidget.resize(QSize(1, 1));
0135         myWidget.repaint();
0136         myWidget.resize(QSize(2, 2));
0137         myWidget.repaint();
0138         myWidget.resize(QSize(3, 3));
0139         myWidget.repaint();
0140         myWidget.resize(QSize(4, 4));
0141         myWidget.repaint();
0142         myWidget.resize(QSize(5, 5));
0143         myWidget.repaint();
0144         myWidget.resize(QSize(6, 6));
0145         myWidget.repaint();
0146         myWidget.resize(QSize(7, 7));
0147         myWidget.repaint();
0148         myWidget.resize(QSize(8, 8));
0149         myWidget.repaint();
0150         myWidget.resize(QSize(9, 9));
0151         myWidget.repaint();
0152         myWidget.resize(QSize(10, 10));
0153         myWidget.repaint();
0154         myWidget.resize(QSize(11, 11));
0155         myWidget.repaint();
0156         myWidget.resize(QSize(12, 12));
0157         myWidget.repaint();
0158         myWidget.resize(QSize(13, 13));
0159         myWidget.repaint();
0160         myWidget.resize(QSize(14, 14));
0161         myWidget.repaint();
0162     }
0163 
0164     void testSetOutOfGamutColors()
0165     {
0166         WheelColorPicker myWidget{m_rgbColorSpace};
0167         myWidget.show();
0168         myWidget.resize(QSize(400, 400));
0169 
0170         // Test that setting out-of-gamut colors works
0171 
0172         const LchDouble myFirstColor{100, 150, 0};
0173         myWidget.setCurrentColor(myFirstColor);
0174         QVERIFY(myFirstColor.hasSameCoordinates(myWidget.currentColor()));
0175         QVERIFY(myFirstColor.hasSameCoordinates(
0176             // This widget has no own storage of this property, but
0177             // relies on its child widget:
0178             myWidget.d_pointer->m_chromaLightnessDiagram->currentColor()));
0179 
0180         const LchDouble mySecondColor{0, 150, 0};
0181         myWidget.setCurrentColor(mySecondColor);
0182         QVERIFY(mySecondColor.hasSameCoordinates(myWidget.currentColor()));
0183         QVERIFY(mySecondColor.hasSameCoordinates(
0184             // This widget has no own storage of this property, but
0185             // relies on its child widget:
0186             myWidget.d_pointer->m_chromaLightnessDiagram->currentColor()));
0187     }
0188 
0189     void testSetOutOfRangeColors()
0190     {
0191         WheelColorPicker myWidget{m_rgbColorSpace};
0192         myWidget.show();
0193         myWidget.resize(QSize(400, 400));
0194 
0195         // Test that setting colors, that are not only out-of-gamut colors
0196         // but also out of a reasonable range, works.
0197 
0198         const LchDouble myFirstColor{300, 550, -10};
0199         myWidget.setCurrentColor(myFirstColor);
0200         QVERIFY(myFirstColor.hasSameCoordinates(myWidget.currentColor()));
0201         QVERIFY(myFirstColor.hasSameCoordinates(
0202             // This widget has no own storage of this property, but
0203             // relies on its child widget:
0204             myWidget.d_pointer->m_chromaLightnessDiagram->currentColor()));
0205 
0206         const LchDouble mySecondColor{-100, -150, 890};
0207         myWidget.setCurrentColor(mySecondColor);
0208         QVERIFY(mySecondColor.hasSameCoordinates(myWidget.currentColor()));
0209         QVERIFY(mySecondColor.hasSameCoordinates(
0210             // This widget has no own storage of this property, but
0211             // relies on its child widget:
0212             myWidget.d_pointer->m_chromaLightnessDiagram->currentColor()));
0213     }
0214 
0215     void testHueChanges()
0216     {
0217         WheelColorPicker myWidget{m_rgbColorSpace};
0218         myWidget.resize(QSize(400, 400));
0219 
0220         // Choose a color with an extreme, but still clearly in-gamut chroma
0221         // (at least for the build-in sRGB gamut, with which we are testing):
0222         const LchDouble myColor{32, 115, 300};
0223         myWidget.setCurrentColor(myColor);
0224 
0225         // Move the wheel to a hue that allows much less chroma:
0226         myWidget.d_pointer->m_colorWheel->setHue(222);
0227 
0228         // Now, the chroma-lightness coordinates are out-of-gamut for
0229         // the new hue. Test if they have been corrected:
0230         QVERIFY(m_rgbColorSpace->isCielchD50InGamut(myWidget.currentColor()));
0231     }
0232 };
0233 
0234 } // namespace PerceptualColor
0235 
0236 QTEST_MAIN(PerceptualColor::TestWheelColorPicker)
0237 
0238 // The following “include” is necessary because we do not use a header file:
0239 #include "testwheelcolorpicker.moc"