File indexing completed on 2024-04-14 03:51:11

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         TextEditUserPropertyWidget *edit = new TextEditUserPropertyWidget;
0269 
0270         testKConfigCompilerSignals<TextEditUserPropertyWidget>(edit, QStringLiteral("settings3"));
0271     }
0272 
0273     void testKConfigCompilerSignalsWithoutUserPropertyByMap()
0274     {
0275         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyWidget", QByteArray("text"));
0276 
0277         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0278 
0279         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings4"));
0280     }
0281 
0282     void testKConfigCompilerSignalsWithoutUserPropertyByProperty()
0283     {
0284         // make sure there is nothing registered for the property
0285         KConfigDialogManager::propertyMap()->remove("TextEditNoUserPropertyWidget");
0286 
0287         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0288         edit->setProperty("kcfg_property", QByteArray("text"));
0289 
0290         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings5"));
0291     }
0292 
0293     void testKConfigCompilerSignalsWithUserPropertyAutoSignal()
0294     {
0295         // make sure there is nothing registered
0296         KConfigDialogManager::propertyMap()->remove("TextEditUserPropertyWidget");
0297 
0298         TextEditUserPropertyWidget *edit = new TextEditUserPropertyWidget;
0299 
0300         testKConfigCompilerSignals<TextEditUserPropertyWidget>(edit, QStringLiteral("settings6"));
0301     }
0302 
0303     void testKConfigCompilerSignalsWithoutUserPropertyByMapAutoSignal()
0304     {
0305         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyWidget", QByteArray("text"));
0306 
0307         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0308 
0309         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings7"));
0310     }
0311 
0312     void testKConfigCompilerSignalsWithoutUserPropertyByPropertyAutoSignal()
0313     {
0314         // next to USER on "other" property, this one should also be ignored
0315         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyWidget", QByteArray("other"));
0316 
0317         TextEditNoUserPropertyWidget *edit = new TextEditNoUserPropertyWidget;
0318         edit->setProperty("kcfg_property", QByteArray("text"));
0319 
0320         testKConfigCompilerSignals<TextEditNoUserPropertyWidget>(edit, QStringLiteral("settings8"));
0321     }
0322 
0323     void testKConfigCompilerSignalsWithoutUserPropertyByPropertyBySignal()
0324     {
0325         // next to USER being on "other" property, this one should also be ignored
0326         KConfigDialogManager::propertyMap()->insert("TextEditNoUserPropertyNoNotifyWidget", QByteArray("other"));
0327 
0328         TextEditNoUserPropertyNoNotifyWidget *edit = new TextEditNoUserPropertyNoNotifyWidget;
0329         edit->setProperty("kcfg_property", QByteArray("text"));
0330         edit->setProperty("kcfg_propertyNotify", SIGNAL(textChanged(QString)));
0331 
0332         testKConfigCompilerSignals<TextEditNoUserPropertyNoNotifyWidget>(edit, QStringLiteral("settings9"));
0333     }
0334 
0335 private:
0336     template<class T>
0337     void testKConfigCompilerSignals(T *edit, const QString &configDialogTitle)
0338     {
0339         const QString defaultValue = QStringLiteral("default value");
0340         const QString changedValue = QStringLiteral("changed value");
0341         const QString someOtherValue = QStringLiteral("some other value");
0342         // set to default to ensure no old stored values make things fail
0343         SignalTest::self()->setDefaults();
0344         KConfigDialog *dialog = new KConfigDialog(nullptr, configDialogTitle, SignalTest::self());
0345         QWidget *page = new QWidget;
0346         edit->setParent(page);
0347         edit->setObjectName(QStringLiteral("kcfg_foo"));
0348         edit->setText(QStringLiteral("some text"));
0349 
0350         QSignalSpy spy(SignalTest::self(), &SignalTest::fooChanged);
0351         QVERIFY(spy.isValid());
0352         // now all the magic happens
0353         dialog->addPage(page, QStringLiteral("General"));
0354 
0355         // check that default value gets loaded
0356         QCOMPARE(spy.size(), 0);
0357         QCOMPARE(edit->text(), defaultValue);
0358         QCOMPARE(SignalTest::foo(), defaultValue);
0359 
0360         edit->setText(changedValue);
0361         // change signal should not be emitted immediately (only on save)
0362         QCOMPARE(spy.size(), 0);
0363         QCOMPARE(SignalTest::foo(), defaultValue); // should be no change to skeleton
0364 
0365         QDialogButtonBox *buttonBox = dialog->findChild<QDialogButtonBox *>();
0366         QVERIFY(buttonBox != nullptr);
0367         buttonBox->button(QDialogButtonBox::Apply)->click(); // now signal should be emitted
0368 
0369         QCOMPARE(spy.size(), 1);
0370         QVariantList args = spy.last();
0371         QCOMPARE(args.size(), 1);
0372         QCOMPARE((QMetaType::Type)args[0].userType(), QMetaType::QString);
0373         QCOMPARE(args[0].toString(), changedValue);
0374         QCOMPARE(SignalTest::foo(), changedValue);
0375 
0376         // change it to a different value
0377         edit->setText(someOtherValue);
0378         QCOMPARE(spy.size(), 1);
0379         buttonBox->button(QDialogButtonBox::Apply)->click(); // now signal should be emitted
0380 
0381         QCOMPARE(spy.size(), 2);
0382         args = spy.last();
0383         QCOMPARE(args.size(), 1);
0384         QCOMPARE((QMetaType::Type)args[0].userType(), QMetaType::QString);
0385         QCOMPARE(args[0].toString(), someOtherValue);
0386         QCOMPARE(SignalTest::foo(), someOtherValue);
0387     }
0388 };
0389 
0390 QTEST_MAIN(KConfigDialog_UnitTest)
0391 
0392 #include "kconfigdialog_unittest.moc"