File indexing completed on 2024-05-12 15:33:58

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <QSignalSpy>
0009 #include <QTest>
0010 #include <khistorycombobox.h>
0011 #include <klineedit.h>
0012 
0013 class KTestComboBox : public KComboBox
0014 {
0015 public:
0016     KTestComboBox(bool rw, QWidget *parent = nullptr)
0017         : KComboBox(rw, parent)
0018     {
0019     }
0020     KCompletionBase *delegate() const
0021     {
0022         return KCompletionBase::delegate();
0023     }
0024 };
0025 
0026 class KComboBox_UnitTest : public QObject
0027 {
0028     Q_OBJECT
0029 
0030 private:
0031     void testComboReturnPressed(bool ctorArg)
0032     {
0033         KComboBox w(ctorArg /*initial value for editable*/);
0034         w.setEditable(true);
0035         w.setCompletionMode(KCompletion::CompletionPopup);
0036         w.addItem(QStringLiteral("Hello world"));
0037         QVERIFY(w.lineEdit());
0038         auto lineEdit = qobject_cast<KLineEdit *>(w.lineEdit());
0039         QVERIFY(lineEdit);
0040 
0041         // set editable again, don't recreate the line edit
0042         w.setEditable(true);
0043         QCOMPARE(w.lineEdit(), lineEdit);
0044         // KLineEdit signals
0045         QSignalSpy qReturnPressedSpy(w.lineEdit(), &QLineEdit::returnPressed);
0046 
0047 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
0048         QSignalSpy kEditReturnPressedSpy(lineEdit, qOverload<const QString &>(&KLineEdit::returnPressed));
0049 #endif
0050         QSignalSpy kEditReturnKeyPressedSpy(lineEdit, &KLineEdit::returnKeyPressed);
0051 
0052         // KComboBox signals
0053 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
0054         QSignalSpy comboReturnPressedSpy(&w, qOverload<>(&KComboBox::returnPressed));
0055 #endif
0056         QSignalSpy comboReturnPressedStringSpy(&w, qOverload<const QString &>(&KComboBox::returnPressed));
0057 
0058         QSignalSpy comboActivatedSpy(&w, &QComboBox::textActivated);
0059         QTest::keyClick(&w, Qt::Key_Return);
0060         QCOMPARE(qReturnPressedSpy.count(), 1);
0061 
0062 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
0063         QCOMPARE(kEditReturnPressedSpy.count(), 1);
0064         QCOMPARE(kEditReturnPressedSpy[0][0].toString(), QString("Hello world"));
0065 #endif
0066         QCOMPARE(kEditReturnKeyPressedSpy.count(), 1);
0067         QCOMPARE(kEditReturnKeyPressedSpy.at(0).at(0).toString(), QStringLiteral("Hello world"));
0068 
0069 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
0070         QCOMPARE(comboReturnPressedSpy.count(), 1);
0071 #endif
0072         QCOMPARE(comboReturnPressedStringSpy.count(), 1);
0073         QCOMPARE(comboReturnPressedStringSpy[0][0].toString(), QString("Hello world"));
0074 
0075         QCOMPARE(comboActivatedSpy.count(), 1);
0076         QCOMPARE(comboActivatedSpy[0][0].toString(), QString("Hello world"));
0077     }
0078 
0079 private Q_SLOTS:
0080     void testComboReturnPressed()
0081     {
0082         testComboReturnPressed(false);
0083         testComboReturnPressed(true);
0084     }
0085 
0086     void testHistoryComboReturnPressed()
0087     {
0088         KHistoryComboBox w;
0089         QVERIFY(qobject_cast<KLineEdit *>(w.lineEdit()));
0090 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
0091         QSignalSpy comboReturnPressedSpy(&w, qOverload<>(&KComboBox::returnPressed));
0092 #endif
0093         QSignalSpy comboReturnPressedStringSpy(&w, qOverload<const QString &>(&KComboBox::returnPressed));
0094         connect(&w, &KHistoryComboBox::textActivated, &w, &KHistoryComboBox::addToHistory);
0095         QSignalSpy comboActivatedSpy(&w, &QComboBox::textActivated);
0096         QTest::keyClicks(&w, QStringLiteral("Hello world"));
0097         QTest::keyClick(&w, Qt::Key_Return);
0098         qApp->processEvents(); // QueuedConnection in KHistoryComboBox
0099 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
0100         QCOMPARE(comboReturnPressedSpy.count(), 1);
0101 #endif
0102         QCOMPARE(comboReturnPressedStringSpy.count(), 1);
0103         QCOMPARE(comboReturnPressedStringSpy[0][0].toString(), QString("Hello world"));
0104 
0105         QCOMPARE(comboActivatedSpy.count(), 1);
0106         QCOMPARE(comboActivatedSpy[0][0].toString(), QString("Hello world"));
0107     }
0108 
0109     void testHistoryComboKeyUp()
0110     {
0111         KHistoryComboBox w;
0112         QStringList items;
0113         items << QStringLiteral("One") << QStringLiteral("Two") << QStringLiteral("Three") << QStringLiteral("Four");
0114         w.addItems(items);
0115         QSignalSpy currentIndexChangedSpy(&w, &QComboBox::currentIndexChanged);
0116         w.completionObject()->setItems(items);
0117         QCOMPARE(w.currentIndex(), 0);
0118         QTest::keyClick(&w, Qt::Key_Up);
0119         QCOMPARE(w.currentIndex(), 1);
0120         QCOMPARE(currentIndexChangedSpy.count(), 1);
0121         QCOMPARE(currentIndexChangedSpy[0][0].toInt(), 1);
0122     }
0123 
0124     void testHistoryComboInsertItems()
0125     {
0126         KHistoryComboBox combo;
0127         // uic generates code like this, let's make sure it compiles
0128         combo.insertItems(0, QStringList() << QStringLiteral("foo"));
0129     }
0130 
0131     void testHistoryComboReset()
0132     {
0133         // It only tests that it doesn't crash
0134         // TODO: Finish
0135         KHistoryComboBox combo;
0136         QStringList items;
0137         items << QStringLiteral("One") << QStringLiteral("Two");
0138         combo.addItems(items);
0139         combo.reset();
0140     }
0141 
0142     void testDeleteLineEdit()
0143     {
0144         // Test for KCombo's KLineEdit destruction
0145         KTestComboBox *testCombo = new KTestComboBox(true, nullptr); // rw, with KLineEdit
0146         testCombo->setEditable(false); // destroys our KLineEdit, with deleteLater
0147         qApp->sendPostedEvents(nullptr, QEvent::DeferredDelete);
0148         QVERIFY(testCombo->KTestComboBox::delegate() == nullptr);
0149         delete testCombo; // not needed anymore
0150     }
0151 
0152     void testLineEditCompletion()
0153     {
0154         QFETCH(bool, editable);
0155         QPointer<KCompletion> completion;
0156         QPointer<KLineEdit> lineEdit;
0157         QPointer<KLineEdit> lineEdit2;
0158 
0159         {
0160             // Test for KCombo's KLineEdit inheriting the completion object of the parent
0161             KTestComboBox testCombo(editable, nullptr);
0162 
0163             // we only have a line edit when we are editable already
0164             QCOMPARE(static_cast<bool>(testCombo.lineEdit()), editable);
0165 
0166             // we can always get a completion object
0167             // NOTE: for an editable combo, this refers to the completion object of
0168             // the internal line edit
0169             // NOTE: for a not-yet-editable combo, this refers to the completion object
0170             // that belongs to the combo directly
0171             completion = testCombo.completionObject();
0172             QVERIFY(completion);
0173 
0174             // make editable
0175             testCombo.setEditable(true);
0176             QVERIFY(completion); // completion is still alive
0177 
0178             // verify that the completion object was set on the line edit
0179             lineEdit = qobject_cast<KLineEdit *>(testCombo.lineEdit());
0180             QVERIFY(lineEdit);
0181             QVERIFY(lineEdit->compObj());
0182             QCOMPARE(lineEdit->compObj(), completion.data());
0183             QCOMPARE(testCombo.completionObject(), completion.data());
0184 
0185             // don't lose the completion and don't crash when we set a new line edit
0186             // NOTE: only reuse the completion when it belongs to the combo box
0187             lineEdit2 = new KLineEdit(&testCombo);
0188             QVERIFY(!lineEdit2->compObj());
0189             testCombo.setLineEdit(lineEdit2);
0190             QVERIFY(!lineEdit); // first line edit got deleted now
0191             if (editable) {
0192                 QVERIFY(!completion); // got deleted with the line edit
0193                 // but we get a new one from the second line edit
0194                 completion = testCombo.completionObject();
0195             }
0196             QVERIFY(completion);
0197             QCOMPARE(lineEdit2->compObj(), completion.data());
0198             QCOMPARE(testCombo.completionObject(), completion.data());
0199         }
0200 
0201         // ensure nothing gets leaked
0202         QVERIFY(!completion);
0203         QVERIFY(!lineEdit2);
0204     }
0205 
0206     void testLineEditCompletion_data()
0207     {
0208         QTest::addColumn<bool>("editable");
0209         QTest::newRow("deferred-editable") << false;
0210         QTest::newRow("editable") << true;
0211     }
0212 
0213     void testSelectionResetOnReturn()
0214     {
0215         // void QComboBoxPrivate::_q_returnPressed() calls lineEdit->deselect()
0216         KHistoryComboBox *testCombo = new KHistoryComboBox(true, nullptr);
0217         QCOMPARE(testCombo->insertPolicy(), QComboBox::NoInsert); // not the Qt default; KHistoryComboBox changes that
0218         QTest::keyClicks(testCombo, QStringLiteral("Hello world"));
0219         testCombo->lineEdit()->setSelection(5, 3);
0220         QVERIFY(testCombo->lineEdit()->hasSelectedText());
0221         QTest::keyClick(testCombo, Qt::Key_Return);
0222         // Changed in Qt5: it only does that if insertionPolicy isn't NoInsert.
0223         // Should we add a lineEdit()->deselect() in KHistoryComboBox? Why does this matter?
0224         QEXPECT_FAIL("", "Qt5: QComboBox doesn't deselect text anymore on returnPressed", Continue);
0225         QVERIFY(!testCombo->lineEdit()->hasSelectedText());
0226     }
0227 };
0228 
0229 QTEST_MAIN(KComboBox_UnitTest)
0230 
0231 #include "kcombobox_unittest.moc"