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 "colorwheel.h"
0007 // Second, the private implementation.
0008 #include "colorwheel_p.h" // IWYU pragma: keep
0009 
0010 #include "constpropagatinguniquepointer.h"
0011 #include "helpermath.h"
0012 #include "rgbcolorspace.h"
0013 #include <qapplication.h>
0014 #include <qboxlayout.h>
0015 #include <qglobal.h>
0016 #include <qlineedit.h>
0017 #include <qnamespace.h>
0018 #include <qobject.h>
0019 #include <qpoint.h>
0020 #include <qsharedpointer.h>
0021 #include <qsignalspy.h>
0022 #include <qsize.h>
0023 #include <qtest.h>
0024 #include <qtestcase.h>
0025 #include <qtestkeyboard.h>
0026 #include <qtestmouse.h>
0027 #include <qwidget.h>
0028 
0029 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0030 #include <qtmetamacros.h>
0031 #else
0032 #include <qobjectdefs.h>
0033 #include <qstring.h>
0034 #endif
0035 
0036 namespace PerceptualColor
0037 {
0038 class TestColorWheel : public QObject
0039 {
0040     Q_OBJECT
0041 
0042 public:
0043     explicit TestColorWheel(QObject *parent = nullptr)
0044         : QObject(parent)
0045     {
0046     }
0047 
0048 private:
0049     QSharedPointer<PerceptualColor::RgbColorSpace> m_rgbColorSpace = RgbColorSpace::createSrgb();
0050 
0051 private Q_SLOTS:
0052     void initTestCase()
0053     {
0054         // Called before the first test function is executed
0055     }
0056 
0057     void cleanupTestCase()
0058     {
0059         // Called after the last test function was executed
0060     }
0061 
0062     void init()
0063     {
0064         // Called before each test function is executed
0065     }
0066 
0067     void cleanup()
0068     {
0069         // Called after every test function
0070     }
0071 
0072     void testConstructorDestructor()
0073     {
0074         ColorWheel temp(m_rgbColorSpace);
0075     }
0076 
0077     void testMouseFocusBehaviour()
0078     {
0079         QWidget myWindow;
0080         QHBoxLayout *myLayout = new QHBoxLayout;
0081         QLineEdit *myLineEdit = new QLineEdit;
0082         myLayout->addWidget(myLineEdit);
0083         ColorWheel *myColorWheel = new ColorWheel(m_rgbColorSpace);
0084         myLayout->addWidget(myColorWheel);
0085         myWindow.setLayout(myLayout);
0086 
0087         // It is necessary to show the widget and make it active
0088         // to make focus and widget events working within unit tests.
0089         myWindow.show();
0090         QApplication::setActiveWindow(&myWindow);
0091 
0092         myLineEdit->setFocus();
0093         QCOMPARE(myLineEdit->hasFocus(), true);
0094         QCOMPARE(myColorWheel->hasFocus(), false);
0095 
0096         // A click that is not in the wheel should not give focus:
0097         QTest::mouseClick(
0098             // Widget to click
0099             myColorWheel,
0100             // Mouse button to simulate
0101             Qt::MouseButton::LeftButton,
0102             // Modifiers
0103             Qt::KeyboardModifier::NoModifier,
0104             // Position for the mouse click
0105             QPoint(10, 10));
0106         QCOMPARE(myColorWheel->hasFocus(), false);
0107         // A click in the middle should give focus:
0108         QTest::mouseClick(
0109             // Widget to click
0110             myColorWheel,
0111             // Mouse button to simulate
0112             Qt::MouseButton::LeftButton,
0113             // Modifiers
0114             Qt::KeyboardModifier::NoModifier
0115             // Not specifying a point means: click in the center of the widget.
0116         );
0117         QCOMPARE(myColorWheel->hasFocus(), true);
0118     }
0119 
0120     void testHueProperty()
0121     {
0122         ColorWheel myWheel(m_rgbColorSpace);
0123         QSignalSpy mySpy(&myWheel, &ColorWheel::hueChanged);
0124         qreal referenceHue = 12.345;
0125 
0126         // Test if signal is emitted.
0127         myWheel.setHue(referenceHue);
0128         QCOMPARE(mySpy.count(), 1);
0129         QCOMPARE(myWheel.hue(), referenceHue);
0130 
0131         // Test that no signal is emitted for old hue.
0132         myWheel.setHue(referenceHue);
0133         QCOMPARE(mySpy.count(), 1);
0134         QCOMPARE(myWheel.hue(), referenceHue);
0135     }
0136 
0137     void testMinimumSizeHint()
0138     {
0139         ColorWheel myColorWheel(m_rgbColorSpace);
0140         QVERIFY2(myColorWheel.minimumSizeHint().width() > 0, "minimalSizeHint width is implemented.");
0141         QVERIFY2(myColorWheel.minimumSizeHint().height() > 0, "minimalSizeHint height is implemented.");
0142         // Check that the hint is a square:
0143         QCOMPARE(myColorWheel.minimumSizeHint().width(), myColorWheel.minimumSizeHint().height());
0144     }
0145 
0146     void testSizeHint()
0147     {
0148         ColorWheel myColorWheel(m_rgbColorSpace);
0149         QVERIFY2(myColorWheel.sizeHint().width() > myColorWheel.minimumSizeHint().width(), "sizeHint width is bigger than minimalSizeHint width.");
0150         QVERIFY2(myColorWheel.sizeHint().height() > myColorWheel.minimumSizeHint().height(), "sizeHint height is bigger than minimalSizeHint height.");
0151         // Check that the hint is a square:
0152         QCOMPARE(myColorWheel.minimumSizeHint().width(), myColorWheel.minimumSizeHint().height());
0153     }
0154 
0155     void testBorder()
0156     {
0157         ColorWheel myColorWheel(m_rgbColorSpace);
0158         QVERIFY2(myColorWheel.d_pointer->border() > 0, "border() is a valid value > 0.");
0159     }
0160 
0161     void testInnerDiameter()
0162     {
0163         ColorWheel myColorWheel(m_rgbColorSpace);
0164         QVERIFY2(myColorWheel.d_pointer->innerDiameter() > 0, "innerDiameter() is a valid value > 0.");
0165         QVERIFY2(myColorWheel.d_pointer->innerDiameter() < myColorWheel.size().width(), "innerDiameter() is smaller than the widget’s width.");
0166         QVERIFY2(myColorWheel.d_pointer->innerDiameter() < myColorWheel.size().height(), "innerDiameter() is smaller than the widget’s height.");
0167     }
0168 
0169     void testVerySmallWidgetSizes()
0170     {
0171         // Also very small widget sizes should not crash the widget.
0172         // This might happen because of divisions by 0, even when the widget
0173         // is bigger than 0 because of borders or offsets. We test this
0174         // here with various small sizes, always forcing in immediate
0175         // re-paint.
0176         ColorWheel myWidget{m_rgbColorSpace};
0177         myWidget.show();
0178         myWidget.resize(QSize());
0179         myWidget.repaint();
0180         myWidget.resize(QSize(-1, -1));
0181         myWidget.repaint();
0182         myWidget.resize(QSize(-1, 0));
0183         myWidget.repaint();
0184         myWidget.resize(QSize(0, -1));
0185         myWidget.repaint();
0186         myWidget.resize(QSize(0, 1));
0187         myWidget.repaint();
0188         myWidget.resize(QSize(1, 0));
0189         myWidget.repaint();
0190         myWidget.resize(QSize(1, 1));
0191         myWidget.repaint();
0192         myWidget.resize(QSize(2, 2));
0193         myWidget.repaint();
0194         myWidget.resize(QSize(3, 3));
0195         myWidget.repaint();
0196         myWidget.resize(QSize(4, 4));
0197         myWidget.repaint();
0198         myWidget.resize(QSize(5, 5));
0199         myWidget.repaint();
0200         myWidget.resize(QSize(6, 6));
0201         myWidget.repaint();
0202         myWidget.resize(QSize(7, 7));
0203         myWidget.repaint();
0204         myWidget.resize(QSize(8, 8));
0205         myWidget.repaint();
0206         myWidget.resize(QSize(9, 9));
0207         myWidget.repaint();
0208         myWidget.resize(QSize(10, 10));
0209         myWidget.repaint();
0210         myWidget.resize(QSize(11, 11));
0211         myWidget.repaint();
0212         myWidget.resize(QSize(12, 12));
0213         myWidget.repaint();
0214         myWidget.resize(QSize(13, 13));
0215         myWidget.repaint();
0216         myWidget.resize(QSize(14, 14));
0217         myWidget.repaint();
0218     }
0219 
0220     void testOutOfRange()
0221     {
0222         ColorWheel myWidget{m_rgbColorSpace};
0223         myWidget.show();
0224         myWidget.resize(QSize(400, 400));
0225 
0226         // Test setting out-of-range hues
0227 
0228         const qreal hue = 500;
0229         myWidget.setHue(hue);
0230         // Out-of-range hues should initially be preserved.
0231         QCOMPARE(myWidget.hue(), 500);
0232         // After user interaction, they should be normalized.
0233         QTest::keyClick(&myWidget, Qt::Key_Plus);
0234         QVERIFY(isInRange<qreal>(0, myWidget.hue(), 360));
0235     }
0236 };
0237 
0238 } // namespace PerceptualColor
0239 
0240 QTEST_MAIN(PerceptualColor::TestColorWheel)
0241 
0242 // The following “include” is necessary because we do not use a header file:
0243 #include "testcolorwheel.moc"