File indexing completed on 2024-04-28 11:34:42

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2012 Albert Astals Cid <aacid@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include <QTest>
0009 #include <QTestEvent>
0010 
0011 #include <QComboBox>
0012 #include <QDialogButtonBox>
0013 #include <QLineEdit>
0014 #include <QPushButton>
0015 #include <QSignalSpy>
0016 #include <QSpinBox>
0017 
0018 #include <KColorCombo>
0019 #include <KConfigSkeleton>
0020 #include <kconfigdialog.h>
0021 #include <kconfigdialogmanager.h>
0022 
0023 #include "signaltest.h"
0024 
0025 static const auto CONFIG_FILE = QStringLiteral("kconfigdialog_unittestrc");
0026 
0027 class TextEditUserPropertyWidget : public QWidget
0028 {
0029     Q_OBJECT
0030     // with USER parameter
0031     Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true)
0032 public:
0033     TextEditUserPropertyWidget(QWidget *parent = nullptr)
0034         : QWidget(parent)
0035     {
0036     }
0037     void setText(const QString &text)
0038     {
0039         m_text = text;
0040         Q_EMIT textChanged(m_text);
0041     }
0042     QString text() const
0043     {
0044         return m_text;
0045     }
0046 Q_SIGNALS:
0047     void textChanged(const QString &text);
0048 
0049 private:
0050     QString m_text;
0051 };
0052 
0053 class TextEditNoUserPropertyWidget : public QWidget
0054 {
0055     Q_OBJECT
0056     Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
0057     Q_PROPERTY(QString other READ other WRITE setOther NOTIFY otherChanged USER true)
0058 public:
0059     TextEditNoUserPropertyWidget(QWidget *parent = nullptr)
0060         : QWidget(parent)
0061     {
0062     }
0063     void setText(const QString &text)
0064     {
0065         m_text = text;
0066         Q_EMIT textChanged(m_text);
0067     }
0068     QString text() const
0069     {
0070         return m_text;
0071     }
0072     void setOther(const QString &other)
0073     {
0074         m_other = other;
0075         Q_EMIT textChanged(m_other);
0076     }
0077     QString other() const
0078     {
0079         return m_other;
0080     }
0081 Q_SIGNALS:
0082     void textChanged(const QString &text);
0083     void otherChanged(const QString &other);
0084 
0085 private:
0086     QString m_text;
0087     QString m_other;
0088 };
0089 
0090 class TextEditNoUserPropertyNoNotifyWidget : public QWidget
0091 {
0092     Q_OBJECT
0093     Q_PROPERTY(QString text READ text WRITE setText)
0094     Q_PROPERTY(QString other READ other WRITE setOther NOTIFY otherChanged USER true)
0095 public:
0096     TextEditNoUserPropertyNoNotifyWidget(QWidget *parent = nullptr)
0097         : QWidget(parent)
0098     {
0099     }
0100     void setText(const QString &text)
0101     {
0102         m_text = text;
0103         Q_EMIT textChanged(m_text);
0104     }
0105     QString text() const
0106     {
0107         return m_text;
0108     }
0109     void setOther(const QString &other)
0110     {
0111         m_other = other;
0112         Q_EMIT textChanged(m_other);
0113     }
0114     QString other() const
0115     {
0116         return m_other;
0117     }
0118 Q_SIGNALS:
0119     void textChanged(const QString &text);
0120     void otherChanged(const QString &other);
0121 
0122 private:
0123     QString m_text;
0124     QString m_other;
0125 };
0126 
0127 class ComboBoxPage : public QWidget
0128 {
0129 public:
0130     ComboBoxPage()
0131     {
0132         colorCombo = new KColorCombo(this);
0133         colorCombo->setObjectName(QStringLiteral("kcfg_Color"));
0134         colorCombo->setColor(Qt::red);
0135 
0136         enumCombo = new QComboBox(this);
0137         enumCombo->setObjectName(QStringLiteral("kcfg_Enum"));
0138         enumCombo->addItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C"));
0139 
0140         textCombo = new QComboBox(this);
0141         textCombo->setObjectName(QStringLiteral("kcfg_Text"));
0142         textCombo->setEditable(true);
0143         textCombo->addItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C"));
0144 
0145         numInput = new QSpinBox(this);
0146         numInput->setValue(1);
0147         numInput->setObjectName(QStringLiteral("kcfg_IntNumInput"));
0148     }
0149 
0150     KColorCombo *colorCombo;
0151     QComboBox *enumCombo;
0152     QComboBox *textCombo;
0153     QSpinBox *numInput;
0154 };
0155 
0156 class ComboSettings : public KConfigSkeleton
0157 {
0158 public:
0159     ComboSettings()
0160         : KConfigSkeleton(CONFIG_FILE)
0161     {
0162         colorItem = new ItemColor(currentGroup(), QStringLiteral("Color"), color, Qt::white);
0163         addItem(colorItem, QStringLiteral("Color"));
0164 
0165         QList<ItemEnum::Choice> textValues;
0166         {
0167             ItemEnum::Choice choice;
0168             choice.name = QStringLiteral("A");
0169             textValues.append(choice);
0170         }
0171         {
0172             ItemEnum::Choice choice;
0173             choice.name = QStringLiteral("B");
0174             textValues.append(choice);
0175         }
0176         {
0177             ItemEnum::Choice choice;
0178             choice.name = QStringLiteral("C");
0179             textValues.append(choice);
0180         }
0181         enumItem = new ItemEnum(currentGroup(), QStringLiteral("Enum"), enumIndex, textValues, 1);
0182         addItem(enumItem, QStringLiteral("Enum"));
0183 
0184         stringItem = new ItemString(currentGroup(), QStringLiteral("Text"), string, QStringLiteral("hh:mm"));
0185         addItem(stringItem, QStringLiteral("Text"));
0186 
0187         intValueItem = new ItemInt(currentGroup(), QStringLiteral("IntNumInput"), intValue, 42);
0188         addItem(intValueItem, QStringLiteral("IntNumInput"));
0189     }
0190 
0191     ItemColor *colorItem;
0192     QColor color;
0193 
0194     ItemEnum *enumItem;
0195     int enumIndex;
0196 
0197     ItemString *stringItem;
0198     QString string;
0199 
0200     ItemInt *intValueItem;
0201     int intValue;
0202 };
0203 
0204 class KConfigDialog_UnitTest : public QObject
0205 {
0206     Q_OBJECT
0207 
0208 private Q_SLOTS:
0209     void initTestCase()
0210     {
0211         QStandardPaths::setTestModeEnabled(true);
0212         // Leftover configuration breaks combosTest
0213         const QString configFile = QStandardPaths::locate(QStandardPaths::GenericConfigLocation, CONFIG_FILE);
0214         if (!configFile.isEmpty()) {
0215             if (!QFile::remove(configFile)) {
0216                 qWarning() << "Could not remove old config file:" << configFile;
0217             }
0218         }
0219     }
0220 
0221     void test()
0222     {
0223         ComboSettings *skeleton = new ComboSettings();
0224         KConfigDialog *dialog = new KConfigDialog(nullptr, QStringLiteral("settings"), skeleton);
0225         ComboBoxPage *page = new ComboBoxPage();
0226 
0227         QCOMPARE(page->colorCombo->color().name(), QColor(Qt::red).name());
0228         QCOMPARE(page->enumCombo->currentIndex(), 0);
0229         QCOMPARE(page->textCombo->currentText(), QString("A"));
0230         QCOMPARE(page->numInput->value(), 1);
0231 
0232         dialog->addPage(page, QStringLiteral("General"));
0233 
0234         QCOMPARE(page->colorCombo->color().name(), QColor(Qt::white).name());
0235         QCOMPARE(page->enumCombo->currentIndex(), 1);
0236         QCOMPARE(page->textCombo->currentText(), QLatin1String("hh:mm"));
0237         QCOMPARE(page->numInput->value(), 42);
0238 
0239         page->colorCombo->setColor(Qt::blue);
0240         page->enumCombo->setCurrentIndex(2);
0241         page->textCombo->setCurrentIndex(2);
0242         page->numInput->setValue(2);
0243 
0244         QDialogButtonBox *buttonBox = dialog->findChild<QDialogButtonBox *>();
0245         QVERIFY(buttonBox != nullptr);
0246         buttonBox->button(QDialogButtonBox::Apply)->click();
0247         QCOMPARE(skeleton->colorItem->property().value<QColor>().name(), QColor(Qt::blue).name());
0248         QCOMPARE(skeleton->enumItem->property().toInt(), 2);
0249         QCOMPARE(skeleton->stringItem->property().toString(), QLatin1String("C"));
0250         QCOMPARE(skeleton->intValueItem->property().toInt(), 2);
0251 
0252         delete dialog;
0253         delete skeleton;
0254     }
0255 
0256     void testKConfigCompilerSignalsKnownWidget()
0257     {
0258         QLineEdit *edit = new QLineEdit;
0259 
0260         testKConfigCompilerSignals<QLineEdit>(edit, QStringLiteral("settings2"));
0261     }
0262 
0263     void testKConfigCompilerSignalsWithUserProperty()
0264     {
0265         // make sure there is nothing registered for the property
0266         KConfigDialogManager::propertyMap()->remove("TextEditUserPropertyWidget");
0267 
0268 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0269         KConfigDialogManager::changedMap()->insert("TextEditUserPropertyWidget", SIGNAL(textChanged(QString)));
0270 #endif
0271 
0272         TextEditUserPropertyWidget *edit = new TextEditUserPropertyWidget;
0273 
0274         testKConfigCompilerSignals<TextEditUserPropertyWidget>(edit, QStringLiteral("settings3"));
0275     }
0276 
0277     void testKConfigCompilerSignalsWithoutUserPropertyByMap()
0278     {
0279 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0280         KConfigDialogManager::changedMap()->insert("TextEditNoUserPropertyWidget", SIGNAL(textChanged(QString)));
0281 #endif
0282         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyWidget", QByteArray("text"));
0283 
0284         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0285 
0286         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings4"));
0287     }
0288 
0289     void testKConfigCompilerSignalsWithoutUserPropertyByProperty()
0290     {
0291         // make sure there is nothing registered for the property
0292         KConfigDialogManager::propertyMap()->remove("TextEditNoUserPropertyWidget");
0293 
0294 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0295         KConfigDialogManager::changedMap()->insert("TextEditNoUserPropertyWidget", SIGNAL(textChanged(QString)));
0296 #endif
0297 
0298         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0299         edit->setProperty("kcfg_property", QByteArray("text"));
0300 
0301         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings5"));
0302     }
0303 
0304     void testKConfigCompilerSignalsWithUserPropertyAutoSignal()
0305     {
0306         // make sure there is nothing registered
0307 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0308         KConfigDialogManager::changedMap()->remove("TextEditUserPropertyWidget");
0309 #endif
0310         KConfigDialogManager::propertyMap()->remove("TextEditUserPropertyWidget");
0311 
0312         TextEditUserPropertyWidget *edit = new TextEditUserPropertyWidget;
0313 
0314         testKConfigCompilerSignals<TextEditUserPropertyWidget>(edit, QStringLiteral("settings6"));
0315     }
0316 
0317     void testKConfigCompilerSignalsWithoutUserPropertyByMapAutoSignal()
0318     {
0319 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0320         // make sure there is nothing registered for the signal
0321         KConfigDialogManager::changedMap()->remove("TextEditNoUserPropertyWidget");
0322 #endif
0323 
0324         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyWidget", QByteArray("text"));
0325 
0326         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0327 
0328         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings7"));
0329     }
0330 
0331     void testKConfigCompilerSignalsWithoutUserPropertyByPropertyAutoSignal()
0332     {
0333 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0334         // make sure there is no signal registered
0335         KConfigDialogManager::changedMap()->remove("TextEditNoUserPropertyWidget");
0336 #endif
0337         // next to USER on "other" property, this one should also be ignored
0338         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyWidget", QByteArray("other"));
0339 
0340         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0341         edit->setProperty("kcfg_property", QByteArray("text"));
0342 
0343         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings8"));
0344     }
0345 
0346     void testKConfigCompilerSignalsWithoutUserPropertyByPropertyBySignal()
0347     {
0348         // next to USER being on "other" property, this one should also be ignored
0349 #if KCONFIGWIDGETS_ENABLE_DEPRECATED_SINCE(5, 32)
0350         KConfigDialogManager::changedMap()->insert("TextEditNoUserPropertyNoNotifyWidget", SIGNAL(otherChanged(QString)));
0351 #endif
0352         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyNoNotifyWidget", QByteArray("other"));
0353 
0354         TextEditNoUserPropertyNoNotifyWidget *edit = new TextEditNoUserPropertyNoNotifyWidget;
0355         edit->setProperty("kcfg_property", QByteArray("text"));
0356         edit->setProperty("kcfg_propertyNotify", SIGNAL(textChanged(QString)));
0357 
0358         testKConfigCompilerSignals<TextEditNoUserPropertyNoNotifyWidget>(edit, QStringLiteral("settings9"));
0359     }
0360 
0361 private:
0362     template<class T>
0363     void testKConfigCompilerSignals(T *edit, const QString &configDialogTitle)
0364     {
0365         const QString defaultValue = QStringLiteral("default value");
0366         const QString changedValue = QStringLiteral("changed value");
0367         const QString someOtherValue = QStringLiteral("some other value");
0368         // set to default to ensure no old stored values make things fail
0369         SignalTest::self()->setDefaults();
0370         KConfigDialog *dialog = new KConfigDialog(nullptr, configDialogTitle, SignalTest::self());
0371         QWidget *page = new QWidget;
0372         edit->setParent(page);
0373         edit->setObjectName(QStringLiteral("kcfg_foo"));
0374         edit->setText(QStringLiteral("some text"));
0375 
0376         QSignalSpy spy(SignalTest::self(), &SignalTest::fooChanged);
0377         QVERIFY(spy.isValid());
0378         // now all the magic happens
0379         dialog->addPage(page, QStringLiteral("General"));
0380 
0381         // check that default value gets loaded
0382         QCOMPARE(spy.size(), 0);
0383         QCOMPARE(edit->text(), defaultValue);
0384         QCOMPARE(SignalTest::foo(), defaultValue);
0385 
0386         edit->setText(changedValue);
0387         // change signal should not be emitted immediately (only on save)
0388         QCOMPARE(spy.size(), 0);
0389         QCOMPARE(SignalTest::foo(), defaultValue); // should be no change to skeleton
0390 
0391         QDialogButtonBox *buttonBox = dialog->findChild<QDialogButtonBox *>();
0392         QVERIFY(buttonBox != nullptr);
0393         buttonBox->button(QDialogButtonBox::Apply)->click(); // now signal should be emitted
0394 
0395         QCOMPARE(spy.size(), 1);
0396         QVariantList args = spy.last();
0397         QCOMPARE(args.size(), 1);
0398         QCOMPARE((QMetaType::Type)args[0].type(), QMetaType::QString);
0399         QCOMPARE(args[0].toString(), changedValue);
0400         QCOMPARE(SignalTest::foo(), changedValue);
0401 
0402         // change it to a different value
0403         edit->setText(someOtherValue);
0404         QCOMPARE(spy.size(), 1);
0405         buttonBox->button(QDialogButtonBox::Apply)->click(); // now signal should be emitted
0406 
0407         QCOMPARE(spy.size(), 2);
0408         args = spy.last();
0409         QCOMPARE(args.size(), 1);
0410         QCOMPARE((QMetaType::Type)args[0].type(), QMetaType::QString);
0411         QCOMPARE(args[0].toString(), someOtherValue);
0412         QCOMPARE(SignalTest::foo(), someOtherValue);
0413     }
0414 };
0415 
0416 QTEST_MAIN(KConfigDialog_UnitTest)
0417 
0418 #include "kconfigdialog_unittest.moc"