File indexing completed on 2025-04-20 04:21:46
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 "gradientslider.h" 0007 // Second, the private implementation. 0008 #include "gradientslider_p.h" // IWYU pragma: keep 0009 0010 #include "constpropagatinguniquepointer.h" 0011 #include "gradientimageparameters.h" 0012 #include "lchadouble.h" 0013 #include "rgbcolorspacefactory.h" 0014 #include <qglobal.h> 0015 #include <qnamespace.h> 0016 #include <qobject.h> 0017 #include <qpoint.h> 0018 #include <qsharedpointer.h> 0019 #include <qsignalspy.h> 0020 #include <qsize.h> 0021 #include <qsizepolicy.h> 0022 #include <qtest.h> 0023 #include <qtestcase.h> 0024 #include <qtestkeyboard.h> 0025 0026 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0027 #include <qtmetamacros.h> 0028 #else 0029 #include <qobjectdefs.h> 0030 #include <qstring.h> 0031 #endif 0032 0033 namespace PerceptualColor 0034 { 0035 class RgbColorSpace; 0036 0037 class TestGradientSlider : public QObject 0038 { 0039 Q_OBJECT 0040 0041 public: 0042 explicit TestGradientSlider(QObject *parent = nullptr) 0043 : QObject(parent) 0044 { 0045 } 0046 0047 private: 0048 QSharedPointer<PerceptualColor::RgbColorSpace> m_rgbColorSpace = RgbColorSpaceFactory::createSrgb(); 0049 0050 private Q_SLOTS: 0051 void initTestCase() 0052 { 0053 // Called before the first test function is executed 0054 } 0055 0056 void cleanupTestCase() 0057 { 0058 // Called after the last test function was executed 0059 } 0060 0061 void init() 0062 { 0063 // Called before each test function is executed 0064 } 0065 0066 void cleanup() 0067 { 0068 // Called after every test function 0069 } 0070 0071 void testConstructorAndDestructor() 0072 { 0073 // Constructor and destructor should not crash. 0074 GradientSlider test(m_rgbColorSpace); 0075 QCOMPARE(test.d_pointer->m_gradientImageParameters.rgbColorSpace, // 0076 m_rgbColorSpace); 0077 QCOMPARE(test.d_pointer->m_orientation, Qt::Vertical); 0078 } 0079 0080 void testConstructorAndDestructorHorizontal() 0081 { 0082 // Constructor and destructor should not crash. 0083 GradientSlider test(m_rgbColorSpace, Qt::Horizontal); 0084 QCOMPARE(test.d_pointer->m_gradientImageParameters.rgbColorSpace, // 0085 m_rgbColorSpace); 0086 QCOMPARE(test.d_pointer->m_orientation, Qt::Horizontal); 0087 } 0088 0089 void testConstructorAndDestructorVertical() 0090 { 0091 // Constructor and destructor should not crash. 0092 GradientSlider test(m_rgbColorSpace, Qt::Vertical); 0093 QCOMPARE(test.d_pointer->m_gradientImageParameters.rgbColorSpace, // 0094 m_rgbColorSpace); 0095 QCOMPARE(test.d_pointer->m_orientation, Qt::Vertical); 0096 } 0097 0098 void testFirstColor() 0099 { 0100 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0101 LchaDouble color; 0102 color.l = 50; 0103 color.c = 50; 0104 color.h = 50; 0105 color.a = 1; 0106 QSignalSpy spy(&testSlider, &PerceptualColor::GradientSlider::firstColorChanged); 0107 testSlider.setFirstColor(color); 0108 QVERIFY(testSlider.firstColor().hasSameCoordinates(color)); 0109 QCOMPARE(spy.count(), 1); 0110 } 0111 0112 void testSecondColor() 0113 { 0114 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0115 LchaDouble color; 0116 color.l = 50; 0117 color.c = 50; 0118 color.h = 50; 0119 color.a = 1; 0120 QSignalSpy spy(&testSlider, &PerceptualColor::GradientSlider::secondColorChanged); 0121 testSlider.setSecondColor(color); 0122 QVERIFY(testSlider.secondColor().hasSameCoordinates(color)); 0123 QCOMPARE(spy.count(), 1); 0124 } 0125 0126 void testSetColors() 0127 { 0128 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0129 LchaDouble color; 0130 color.l = 50; 0131 color.c = 50; 0132 color.h = 50; 0133 color.a = 1; 0134 QSignalSpy spyFirst(&testSlider, &PerceptualColor::GradientSlider::firstColorChanged); 0135 QSignalSpy spySecond(&testSlider, &PerceptualColor::GradientSlider::secondColorChanged); 0136 testSlider.setColors(color, color); 0137 QVERIFY(testSlider.firstColor().hasSameCoordinates(color)); 0138 QCOMPARE(spyFirst.count(), 1); 0139 QVERIFY(testSlider.secondColor().hasSameCoordinates(color)); 0140 QCOMPARE(spySecond.count(), 1); 0141 } 0142 0143 void testMinimalSizeHint() 0144 { 0145 GradientSlider testWidget(m_rgbColorSpace); 0146 QVERIFY2(testWidget.minimumSizeHint().width() > 0, "minimalSizeHint width is implemented."); 0147 QVERIFY2(testWidget.minimumSizeHint().height() > 0, "minimalSizeHint height is implemented."); 0148 } 0149 0150 void testSizeHint() 0151 { 0152 GradientSlider testWidget(m_rgbColorSpace); 0153 QVERIFY2(testWidget.sizeHint().width() >= testWidget.minimumSizeHint().width(), "sizeHint width is bigger than or equal to minimalSizeHint width."); 0154 QVERIFY2(testWidget.sizeHint().height() >= testWidget.minimumSizeHint().height(), 0155 "sizeHint height is bigger than or equal to minimalSizeHint " 0156 "height."); 0157 } 0158 0159 void testSingleStep() 0160 { 0161 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0162 QSignalSpy spy(&testSlider, &PerceptualColor::GradientSlider::singleStepChanged); 0163 testSlider.setSingleStep(0.5); 0164 QCOMPARE(spy.count(), 1); 0165 QCOMPARE(testSlider.singleStep(), 0.5); 0166 QCOMPARE(testSlider.d_pointer->m_singleStep, 0.5); 0167 testSlider.setSingleStep(0.2); 0168 QCOMPARE(spy.count(), 2); 0169 QCOMPARE(testSlider.singleStep(), 0.2); 0170 QCOMPARE(testSlider.d_pointer->m_singleStep, 0.2); 0171 testSlider.setSingleStep(-10); 0172 QCOMPARE(spy.count(), 3); 0173 QCOMPARE(testSlider.singleStep(), 0); 0174 QCOMPARE(testSlider.d_pointer->m_singleStep, 0); 0175 testSlider.setSingleStep(10); 0176 QCOMPARE(spy.count(), 4); 0177 QCOMPARE(testSlider.singleStep(), 1); 0178 QCOMPARE(testSlider.d_pointer->m_singleStep, 1); 0179 } 0180 0181 void testPageStep() 0182 { 0183 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0184 QSignalSpy spy(&testSlider, &PerceptualColor::GradientSlider::pageStepChanged); 0185 testSlider.setPageStep(0.5); 0186 QCOMPARE(spy.count(), 1); 0187 QCOMPARE(testSlider.pageStep(), 0.5); 0188 QCOMPARE(testSlider.d_pointer->m_pageStep, 0.5); 0189 testSlider.setPageStep(0.2); 0190 QCOMPARE(spy.count(), 2); 0191 QCOMPARE(testSlider.pageStep(), 0.2); 0192 QCOMPARE(testSlider.d_pointer->m_pageStep, 0.2); 0193 testSlider.setPageStep(-10); 0194 QCOMPARE(spy.count(), 3); 0195 QCOMPARE(testSlider.pageStep(), 0); 0196 QCOMPARE(testSlider.d_pointer->m_pageStep, 0); 0197 testSlider.setPageStep(10); 0198 QCOMPARE(spy.count(), 4); 0199 QCOMPARE(testSlider.pageStep(), 1); 0200 QCOMPARE(testSlider.d_pointer->m_pageStep, 1); 0201 } 0202 0203 void testValue() 0204 { 0205 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0206 testSlider.setValue(0.3); 0207 QSignalSpy spy(&testSlider, &PerceptualColor::GradientSlider::valueChanged); 0208 testSlider.setValue(0.5); 0209 QCOMPARE(spy.count(), 1); 0210 QCOMPARE(testSlider.value(), 0.5); 0211 QCOMPARE(testSlider.d_pointer->m_value, 0.5); 0212 testSlider.setValue(0.2); 0213 QCOMPARE(spy.count(), 2); 0214 QCOMPARE(testSlider.value(), 0.2); 0215 QCOMPARE(testSlider.d_pointer->m_value, 0.2); 0216 testSlider.setValue(-10); 0217 QCOMPARE(spy.count(), 3); 0218 QCOMPARE(testSlider.value(), 0); 0219 QCOMPARE(testSlider.d_pointer->m_value, 0); 0220 testSlider.setValue(10); 0221 QCOMPARE(spy.count(), 4); 0222 QCOMPARE(testSlider.value(), 1); 0223 QCOMPARE(testSlider.d_pointer->m_value, 1); 0224 } 0225 0226 void testKeyPressEventLTR() 0227 { 0228 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0229 testSlider.setLayoutDirection(Qt::LayoutDirection::LeftToRight); 0230 testSlider.setSingleStep(0.1); 0231 testSlider.setPageStep(0.2); 0232 testSlider.setValue(0.3); 0233 QTest::keyClick(&testSlider, Qt::Key_Plus); 0234 QCOMPARE(testSlider.value(), 0.4); 0235 QTest::keyClick(&testSlider, Qt::Key_Minus); 0236 QCOMPARE(testSlider.value(), 0.3); 0237 QTest::keyClick(&testSlider, Qt::Key_Up); 0238 QCOMPARE(testSlider.value(), 0.4); 0239 QTest::keyClick(&testSlider, Qt::Key_Down); 0240 QCOMPARE(testSlider.value(), 0.3); 0241 QTest::keyClick(&testSlider, Qt::Key_Right); 0242 QCOMPARE(testSlider.value(), 0.4); 0243 QTest::keyClick(&testSlider, Qt::Key_Left); 0244 QCOMPARE(testSlider.value(), 0.3); 0245 QTest::keyClick(&testSlider, Qt::Key_PageUp); 0246 QCOMPARE(testSlider.value(), 0.5); 0247 QTest::keyClick(&testSlider, Qt::Key_PageDown); 0248 QCOMPARE(testSlider.value(), 0.3); 0249 QTest::keyClick(&testSlider, Qt::Key_Home); 0250 QCOMPARE(testSlider.value(), 0.0); 0251 QTest::keyClick(&testSlider, Qt::Key_End); 0252 QCOMPARE(testSlider.value(), 1.0); 0253 } 0254 0255 void testKeyPressEventRTL() 0256 { 0257 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0258 testSlider.setLayoutDirection(Qt::LayoutDirection::RightToLeft); 0259 testSlider.setSingleStep(0.1); 0260 testSlider.setPageStep(0.2); 0261 testSlider.setValue(0.3); 0262 QTest::keyClick(&testSlider, Qt::Key_Plus); 0263 QCOMPARE(testSlider.value(), 0.4); 0264 QTest::keyClick(&testSlider, Qt::Key_Minus); 0265 QCOMPARE(testSlider.value(), 0.3); 0266 QTest::keyClick(&testSlider, Qt::Key_Up); 0267 QCOMPARE(testSlider.value(), 0.4); 0268 QTest::keyClick(&testSlider, Qt::Key_Down); 0269 QCOMPARE(testSlider.value(), 0.3); 0270 QTest::keyClick(&testSlider, Qt::Key_Left); 0271 QCOMPARE(testSlider.value(), 0.4); 0272 QTest::keyClick(&testSlider, Qt::Key_Right); 0273 QCOMPARE(testSlider.value(), 0.3); 0274 QTest::keyClick(&testSlider, Qt::Key_PageUp); 0275 QCOMPARE(testSlider.value(), 0.5); 0276 QTest::keyClick(&testSlider, Qt::Key_PageDown); 0277 QCOMPARE(testSlider.value(), 0.3); 0278 QTest::keyClick(&testSlider, Qt::Key_Home); 0279 QCOMPARE(testSlider.value(), 0.0); 0280 QTest::keyClick(&testSlider, Qt::Key_End); 0281 QCOMPARE(testSlider.value(), 1.0); 0282 } 0283 0284 void testOrientationDefaultConstructor() 0285 { 0286 GradientSlider testSlider(m_rgbColorSpace); 0287 QCOMPARE(testSlider.orientation(), Qt::Orientation::Vertical); 0288 QCOMPARE(testSlider.sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); 0289 QCOMPARE(testSlider.sizePolicy().verticalPolicy(), QSizePolicy::Expanding); 0290 } 0291 0292 void testOrientationVerticalConstructor() 0293 { 0294 GradientSlider testSlider(m_rgbColorSpace, Qt::Orientation::Vertical); 0295 QCOMPARE(testSlider.orientation(), Qt::Orientation::Vertical); 0296 QCOMPARE(testSlider.sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); 0297 QCOMPARE(testSlider.sizePolicy().verticalPolicy(), QSizePolicy::Expanding); 0298 } 0299 0300 void testOrientationHorizontalConstructor() 0301 { 0302 GradientSlider testSlider(m_rgbColorSpace, Qt::Orientation::Horizontal); 0303 QCOMPARE(testSlider.orientation(), Qt::Orientation::Horizontal); 0304 QCOMPARE(testSlider.sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); 0305 QCOMPARE(testSlider.sizePolicy().verticalPolicy(), QSizePolicy::Fixed); 0306 } 0307 0308 void testOrientation() 0309 { 0310 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0311 QCOMPARE(testSlider.orientation(), Qt::Orientation::Vertical); 0312 QSignalSpy spy(&testSlider, &PerceptualColor::GradientSlider::orientationChanged); 0313 testSlider.setOrientation(Qt::Orientation::Horizontal); 0314 QCOMPARE(testSlider.orientation(), Qt::Orientation::Horizontal); 0315 QCOMPARE(testSlider.sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); 0316 QCOMPARE(testSlider.sizePolicy().verticalPolicy(), QSizePolicy::Fixed); 0317 QCOMPARE(spy.count(), 1); 0318 testSlider.setOrientation(Qt::Orientation::Vertical); 0319 QCOMPARE(testSlider.orientation(), Qt::Orientation::Vertical); 0320 QCOMPARE(testSlider.sizePolicy().horizontalPolicy(), QSizePolicy::Fixed); 0321 QCOMPARE(testSlider.sizePolicy().verticalPolicy(), QSizePolicy::Expanding); 0322 QCOMPARE(spy.count(), 2); 0323 } 0324 0325 void testSetOrientationWithoutSignalAndForceNewSizePolicy() 0326 { 0327 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0328 QCOMPARE(testSlider.orientation(), Qt::Orientation::Vertical); 0329 QSignalSpy spy(&testSlider, &PerceptualColor::GradientSlider::orientationChanged); 0330 testSlider.d_pointer->setOrientationWithoutSignalAndForceNewSizePolicy(Qt::Orientation::Horizontal); 0331 QCOMPARE(testSlider.orientation(), Qt::Orientation::Horizontal); 0332 QCOMPARE(testSlider.sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); 0333 QCOMPARE(testSlider.sizePolicy().verticalPolicy(), QSizePolicy::Fixed); 0334 QCOMPARE(spy.count(), 0); 0335 testSlider.d_pointer->setOrientationWithoutSignalAndForceNewSizePolicy(Qt::Orientation::Horizontal); 0336 QCOMPARE(testSlider.orientation(), Qt::Orientation::Horizontal); 0337 QCOMPARE(testSlider.sizePolicy().horizontalPolicy(), QSizePolicy::Expanding); 0338 QCOMPARE(testSlider.sizePolicy().verticalPolicy(), QSizePolicy::Fixed); 0339 QCOMPARE(spy.count(), 0); 0340 } 0341 0342 void testPhysicalPixelLength() 0343 { 0344 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0345 QVERIFY2(testSlider.d_pointer->physicalPixelLength() >= 0, "physicalPixelLength() should be ≥ 0."); 0346 } 0347 0348 void testPhysicalPixelThickness() 0349 { 0350 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0351 QVERIFY2(testSlider.d_pointer->physicalPixelThickness() >= 0, "physicalPixelLength() should be ≥ 0."); 0352 } 0353 0354 void testFromWidgetPositionToValue() 0355 { 0356 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0357 qreal value; 0358 value = testSlider.d_pointer->fromWidgetPixelPositionToValue(QPoint(0, 0)); 0359 QVERIFY2((value >= 0) && (value <= 1), "fromWidgetPixelPositionToValue() should be 0 ≤ value ≤ 1."); 0360 value = testSlider.d_pointer->fromWidgetPixelPositionToValue(QPoint(-100, -100)); 0361 QVERIFY2((value >= 0) && (value <= 1), "fromWidgetPixelPositionToValue() should be 0 ≤ value ≤ 1."); 0362 value = testSlider.d_pointer->fromWidgetPixelPositionToValue(QPoint(10000, 10000)); 0363 QVERIFY2((value >= 0) && (value <= 1), "fromWidgetPixelPositionToValue() should be 0 ≤ value ≤ 1."); 0364 } 0365 0366 void testPaintEvent() 0367 { 0368 GradientSlider testSlider(m_rgbColorSpace, Qt::Vertical); 0369 testSlider.show(); 0370 // Paint event should not crash. 0371 // repaint() will call paintEvent()… 0372 testSlider.repaint(); 0373 } 0374 0375 void testVerySmallWidgetSizes() 0376 { 0377 // Also very small widget sizes should not crash the widget. 0378 // This might happen because of divisions by 0, even when the widget 0379 // is bigger than 0 because of borders or offsets. We test this 0380 // here with various small sizes, always forcing in immediate 0381 // re-paint. 0382 GradientSlider myWidget{m_rgbColorSpace}; 0383 myWidget.show(); 0384 myWidget.resize(QSize()); 0385 myWidget.repaint(); 0386 myWidget.resize(QSize(-1, -1)); 0387 myWidget.repaint(); 0388 myWidget.resize(QSize(-1, 0)); 0389 myWidget.repaint(); 0390 myWidget.resize(QSize(0, -1)); 0391 myWidget.repaint(); 0392 myWidget.resize(QSize(0, 1)); 0393 myWidget.repaint(); 0394 myWidget.resize(QSize(1, 0)); 0395 myWidget.repaint(); 0396 myWidget.resize(QSize(1, 1)); 0397 myWidget.repaint(); 0398 myWidget.resize(QSize(2, 2)); 0399 myWidget.repaint(); 0400 myWidget.resize(QSize(3, 3)); 0401 myWidget.repaint(); 0402 myWidget.resize(QSize(4, 4)); 0403 myWidget.repaint(); 0404 myWidget.resize(QSize(5, 5)); 0405 myWidget.repaint(); 0406 myWidget.resize(QSize(6, 6)); 0407 myWidget.repaint(); 0408 myWidget.resize(QSize(7, 7)); 0409 myWidget.repaint(); 0410 myWidget.resize(QSize(8, 8)); 0411 myWidget.repaint(); 0412 myWidget.resize(QSize(9, 9)); 0413 myWidget.repaint(); 0414 myWidget.resize(QSize(10, 10)); 0415 myWidget.repaint(); 0416 myWidget.resize(QSize(11, 11)); 0417 myWidget.repaint(); 0418 myWidget.resize(QSize(12, 12)); 0419 myWidget.repaint(); 0420 myWidget.resize(QSize(13, 13)); 0421 myWidget.repaint(); 0422 myWidget.resize(QSize(14, 14)); 0423 myWidget.repaint(); 0424 } 0425 0426 void testOutOfGamutColors() 0427 { 0428 GradientSlider myWidget{m_rgbColorSpace}; 0429 myWidget.show(); 0430 myWidget.resize(QSize(100, 100)); 0431 0432 // Test that setting out-of-gamut colors works 0433 const LchaDouble myFirstColor{100, 150, 0, 1}; 0434 const LchaDouble mySecondColor{0, 150, 0, 1}; 0435 myWidget.setFirstColor(myFirstColor); 0436 myWidget.setSecondColor(mySecondColor); 0437 QVERIFY(myFirstColor.hasSameCoordinates(myWidget.firstColor())); 0438 QVERIFY(myFirstColor.hasSameCoordinates(myWidget.d_pointer->m_firstColor)); 0439 QVERIFY(mySecondColor.hasSameCoordinates(myWidget.secondColor())); 0440 QVERIFY(mySecondColor.hasSameCoordinates(myWidget.d_pointer->m_secondColor)); 0441 } 0442 0443 void testOutOfRange() 0444 { 0445 GradientSlider myWidget{m_rgbColorSpace}; 0446 myWidget.show(); 0447 myWidget.resize(QSize(100, 100)); 0448 0449 // Test that setting colors, that are not only out-of-gamut colors 0450 // but also out of a reasonable range, works. 0451 const LchaDouble myFirstColor{300, 550, -10, 1}; 0452 const LchaDouble mySecondColor{-100, -150, 890, 1}; 0453 myWidget.setFirstColor(myFirstColor); 0454 myWidget.setSecondColor(mySecondColor); 0455 QVERIFY(myFirstColor.hasSameCoordinates(myWidget.firstColor())); 0456 QVERIFY(myFirstColor.hasSameCoordinates(myWidget.d_pointer->m_firstColor)); 0457 QVERIFY(mySecondColor.hasSameCoordinates(myWidget.secondColor())); 0458 QVERIFY(mySecondColor.hasSameCoordinates(myWidget.d_pointer->m_secondColor)); 0459 } 0460 }; 0461 0462 } // namespace PerceptualColor 0463 0464 QTEST_MAIN(PerceptualColor::TestGradientSlider) 0465 0466 // The following “include” is necessary because we do not use a header file: 0467 #include "testgradientslider.moc"