File indexing completed on 2024-05-12 05:22:37

0001 /*
0002     autotests/newkeyapprovaldialogtest.cpp
0003 
0004     This file is part of libkleopatra's test suite.
0005     SPDX-FileCopyrightText: 2021 g10 Code GmbH
0006     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include <Libkleo/Compliance>
0012 #include <Libkleo/Formatting>
0013 #include <Libkleo/KeyCache>
0014 #include <Libkleo/KeySelectionCombo>
0015 #include <Libkleo/NewKeyApprovalDialog>
0016 #include <Libkleo/Predicates>
0017 #include <Libkleo/Test>
0018 
0019 #include <QCheckBox>
0020 #include <QGroupBox>
0021 #include <QLabel>
0022 #include <QObject>
0023 #include <QPushButton>
0024 #include <QRadioButton>
0025 #include <QSignalSpy>
0026 #include <QTest>
0027 
0028 #include <gpgme++/key.h>
0029 
0030 #include <gpgme.h>
0031 
0032 #include <memory>
0033 #include <set>
0034 
0035 #include <gpgme++/gpgmepp_version.h>
0036 #if GPGMEPP_VERSION >= 0x11700 // 1.23.0
0037 #define GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE 1
0038 #else
0039 #define GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE 0
0040 #endif
0041 
0042 using namespace Kleo;
0043 
0044 namespace QTest
0045 {
0046 template<>
0047 inline char *toString(const bool &t)
0048 {
0049     return t ? qstrdup("true") : qstrdup("false");
0050 }
0051 
0052 template<>
0053 inline bool qCompare(bool const &t1, bool const &t2, const char *actual, const char *expected, const char *file, int line)
0054 {
0055     return compare_helper(t1 == t2, "Compared values are not the same", toString(t1), toString(t2), actual, expected, file, line);
0056 }
0057 
0058 template<>
0059 inline char *toString(const GpgME::Protocol &t)
0060 {
0061     return qstrdup(Formatting::displayName(t).toLocal8Bit().constData());
0062 }
0063 
0064 template<>
0065 inline bool qCompare(GpgME::Protocol const &t1, GpgME::Protocol const &t2, const char *actual, const char *expected, const char *file, int line)
0066 {
0067     return compare_helper(t1 == t2, "Compared values are not the same", toString(t1), toString(t2), actual, expected, file, line);
0068 }
0069 }
0070 
0071 namespace
0072 {
0073 
0074 // copied from NewKeyApprovalDialog::Private
0075 enum Action {
0076     Unset,
0077     GenerateKey,
0078     IgnoreKey,
0079 };
0080 
0081 auto mapValidity(GpgME::UserID::Validity validity)
0082 {
0083     switch (validity) {
0084     default:
0085     case GpgME::UserID::Unknown:
0086         return GPGME_VALIDITY_UNKNOWN;
0087     case GpgME::UserID::Undefined:
0088         return GPGME_VALIDITY_UNDEFINED;
0089     case GpgME::UserID::Never:
0090         return GPGME_VALIDITY_NEVER;
0091     case GpgME::UserID::Marginal:
0092         return GPGME_VALIDITY_MARGINAL;
0093     case GpgME::UserID::Full:
0094         return GPGME_VALIDITY_FULL;
0095     case GpgME::UserID::Ultimate:
0096         return GPGME_VALIDITY_ULTIMATE;
0097     }
0098 }
0099 
0100 // copied from gpgme; slightly modified
0101 void _gpgme_key_add_subkey(gpgme_key_t key, gpgme_subkey_t *r_subkey)
0102 {
0103     gpgme_subkey_t subkey;
0104 
0105     subkey = static_cast<gpgme_subkey_t>(calloc(1, sizeof *subkey));
0106     Q_ASSERT(subkey);
0107     subkey->keyid = subkey->_keyid;
0108     subkey->_keyid[16] = '\0';
0109 
0110     if (!key->subkeys) {
0111         key->subkeys = subkey;
0112     }
0113     if (key->_last_subkey) {
0114         key->_last_subkey->next = subkey;
0115     }
0116     key->_last_subkey = subkey;
0117 
0118     *r_subkey = subkey;
0119 }
0120 
0121 GpgME::Key createTestKey(const char *uid,
0122                          GpgME::Protocol protocol = GpgME::UnknownProtocol,
0123                          KeyCache::KeyUsage usage = KeyCache::KeyUsage::AnyUsage,
0124                          GpgME::UserID::Validity validity = GpgME::UserID::Full)
0125 {
0126     static int count = 0;
0127     count++;
0128 
0129     gpgme_key_t key;
0130     gpgme_key_from_uid(&key, uid);
0131     Q_ASSERT(key);
0132     Q_ASSERT(key->uids);
0133     if (protocol != GpgME::UnknownProtocol) {
0134         key->protocol = protocol == GpgME::OpenPGP ? GPGME_PROTOCOL_OpenPGP : GPGME_PROTOCOL_CMS;
0135     }
0136     const QByteArray fingerprint = QByteArray::number(count, 16).rightJustified(40, '0');
0137     key->fpr = strdup(fingerprint.constData());
0138     key->revoked = 0;
0139     key->expired = 0;
0140     key->disabled = 0;
0141     key->can_encrypt = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Encrypt);
0142     key->can_sign = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Sign);
0143 #if GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE
0144     key->has_encrypt = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Encrypt);
0145     key->has_sign = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Sign);
0146 #endif
0147     key->secret = 1;
0148     key->uids->validity = mapValidity(validity);
0149     key->keylist_mode = GPGME_KEYLIST_MODE_VALIDATE;
0150 
0151     // add a usable VS-NfD-compliant subkey
0152     gpgme_subkey_t subkey;
0153     _gpgme_key_add_subkey(key, &subkey);
0154     subkey->is_de_vs = 1;
0155     subkey->can_encrypt = key->can_encrypt;
0156     subkey->can_sign = key->can_sign;
0157 
0158     return GpgME::Key(key, false);
0159 }
0160 
0161 auto testKey(const char *address, GpgME::Protocol protocol = GpgME::UnknownProtocol)
0162 {
0163     const auto email = GpgME::UserID::addrSpecFromString(address);
0164     const auto keys = KeyCache::instance()->findByEMailAddress(email);
0165     for (const auto &key : keys) {
0166         if (protocol == GpgME::UnknownProtocol || key.protocol() == protocol) {
0167             return key;
0168         }
0169     }
0170     return GpgME::Key();
0171 }
0172 
0173 void waitForKeySelectionCombosBeingInitialized(const QDialog *dialog)
0174 {
0175     QVERIFY(dialog);
0176     auto combo = dialog->findChild<KeySelectionCombo *>();
0177     QVERIFY(combo);
0178 
0179     const auto spy = std::make_unique<QSignalSpy>(combo, &KeySelectionCombo::keyListingFinished);
0180     QVERIFY(spy->isValid());
0181     QVERIFY(spy->wait(10));
0182 }
0183 
0184 template<typename T>
0185 struct Widgets {
0186     std::vector<T *> visible;
0187     std::vector<T *> hidden;
0188 };
0189 
0190 template<typename T>
0191 Widgets<T> visibleAndHiddenWidgets(const QList<T *> &widgets)
0192 {
0193     Widgets<T> result;
0194     std::partition_copy(std::begin(widgets),
0195                         std::end(widgets),
0196                         std::back_inserter(result.visible),
0197                         std::back_inserter(result.hidden),
0198                         std::mem_fn(&QWidget::isVisible));
0199     return result;
0200 }
0201 
0202 enum Visibility {
0203     IsHidden,
0204     IsVisible,
0205 };
0206 
0207 enum CheckedState {
0208     IsUnchecked,
0209     IsChecked,
0210 };
0211 
0212 template<typename T>
0213 void verifyProtocolButton(const T *button, Visibility expectedVisibility, CheckedState expectedCheckedState)
0214 {
0215     QVERIFY(button);
0216     QCOMPARE(button->isVisible(), expectedVisibility == IsVisible);
0217     QCOMPARE(button->isChecked(), expectedCheckedState == IsChecked);
0218 }
0219 
0220 template<typename T>
0221 void verifyWidgetVisibility(const T *widget, Visibility expectedVisibility)
0222 {
0223     QVERIFY(widget);
0224     QCOMPARE(widget->isVisible(), expectedVisibility == IsVisible);
0225 }
0226 
0227 template<typename T>
0228 void verifyWidgetsVisibility(const QList<T> &widgets, Visibility expectedVisibility)
0229 {
0230     for (auto w : widgets) {
0231         verifyWidgetVisibility(w, expectedVisibility);
0232     }
0233 }
0234 
0235 void verifyProtocolLabels(const QList<QLabel *> &labels, int expectedNumber, Visibility expectedVisibility)
0236 {
0237     QCOMPARE(labels.size(), expectedNumber);
0238     verifyWidgetsVisibility(labels, expectedVisibility);
0239 }
0240 
0241 bool listsOfKeysAreEqual(const std::vector<GpgME::Key> &l1, const std::vector<GpgME::Key> &l2)
0242 {
0243     return std::equal(std::begin(l1), std::end(l1), std::begin(l2), std::end(l2), ByFingerprint<std::equal_to>());
0244 }
0245 
0246 void verifySolution(const KeyResolver::Solution &actual, const KeyResolver::Solution &expected)
0247 {
0248     QCOMPARE(actual.protocol, expected.protocol);
0249 
0250     QVERIFY(listsOfKeysAreEqual(actual.signingKeys, expected.signingKeys));
0251 
0252     QVERIFY(std::equal(actual.encryptionKeys.constKeyValueBegin(),
0253                        actual.encryptionKeys.constKeyValueEnd(),
0254                        expected.encryptionKeys.constKeyValueBegin(),
0255                        expected.encryptionKeys.constKeyValueEnd(),
0256                        [](const auto &kv1, const auto &kv2) {
0257                            return kv1.first == kv2.first && listsOfKeysAreEqual(kv1.second, kv2.second);
0258                        }));
0259 }
0260 
0261 void switchKeySelectionCombosFromGenerateKeyToIgnoreKey(const QList<KeySelectionCombo *> &combos)
0262 {
0263     for (auto combo : combos) {
0264         if (combo->currentData(Qt::UserRole).toInt() == GenerateKey) {
0265             const auto ignoreIndex = combo->findData(IgnoreKey);
0266             QVERIFY(ignoreIndex != -1);
0267             combo->setCurrentIndex(ignoreIndex);
0268         }
0269     }
0270 }
0271 
0272 }
0273 
0274 class NewKeyApprovalDialogTest : public QObject
0275 {
0276     Q_OBJECT
0277 
0278 private Q_SLOTS:
0279     void init()
0280     {
0281         // hold a reference to the key cache to avoid rebuilding while the test is running
0282         mKeyCache = KeyCache::instance();
0283 
0284         KeyCache::mutableInstance()->setKeys({
0285             createTestKey("sender@example.net", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::AnyUsage),
0286             createTestKey("sender@example.net", GpgME::CMS, KeyCache::KeyCache::KeyUsage::AnyUsage),
0287             createTestKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::Encrypt),
0288             createTestKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS, KeyCache::KeyCache::KeyUsage::Encrypt),
0289             createTestKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::Encrypt, GpgME::UserID::Marginal),
0290         });
0291     }
0292 
0293     void cleanup()
0294     {
0295         // verify that nobody else holds a reference to the key cache
0296         QVERIFY(mKeyCache.use_count() == 1);
0297         mKeyCache.reset();
0298     }
0299 
0300     void test__verify_test_keys()
0301     {
0302         QVERIFY(!testKey("sender@example.net", GpgME::OpenPGP).isNull());
0303         QVERIFY(!testKey("sender@example.net", GpgME::CMS).isNull());
0304         QVERIFY(!testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).isNull());
0305         QVERIFY(!testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).isNull());
0306         QVERIFY(!testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).isNull());
0307     }
0308 
0309     void test__both_protocols_allowed__mixed_not_allowed__openpgp_preferred()
0310     {
0311         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0312         const bool allowMixed = false;
0313         const QString sender = QStringLiteral("sender@example.net");
0314         const KeyResolver::Solution preferredSolution = {
0315             GpgME::OpenPGP,
0316             {testKey("sender@example.net", GpgME::OpenPGP)},
0317             {
0318                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0319                 {QStringLiteral("prefer-smime@example.net"), {}},
0320                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}},
0321             },
0322         };
0323         const KeyResolver::Solution alternativeSolution = {
0324             GpgME::CMS,
0325             {testKey("sender@example.net", GpgME::CMS)},
0326             {
0327                 {QStringLiteral("prefer-openpgp@example.net"), {}},
0328                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0329                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::CMS)}},
0330             },
0331         };
0332 
0333         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0334         dialog->show();
0335 
0336         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("openpgp button")), IsVisible, IsChecked);
0337         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("smime button")), IsVisible, IsUnchecked);
0338         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0339         QCOMPARE(signingKeyWidgets.visible.size(), 1);
0340         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
0341         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.signingKeys[0].primaryFingerprint());
0342         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::CMS), alternativeSolution.signingKeys[0].primaryFingerprint());
0343 
0344         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0345         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
0346         QCOMPARE(encryptionKeyWidgets.hidden.size(), 3);
0347 
0348         // encryption key widgets for sender come first (visible for OpenPGP, hidden for S/MIME)
0349         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0350         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0351         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
0352         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::CMS), alternativeSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0353 
0354         // encryption key widgets for other recipients follow (visible for OpenPGP, hidden for S/MIME)
0355         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
0356         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::OpenPGP),
0357                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
0358         QCOMPARE(encryptionKeyWidgets.hidden[1]->property("address").toString(), "prefer-openpgp@example.net");
0359         QVERIFY(encryptionKeyWidgets.hidden[1]->defaultKey(GpgME::CMS).isEmpty());
0360         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
0361         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::OpenPGP).isEmpty());
0362         QCOMPARE(encryptionKeyWidgets.hidden[2]->property("address").toString(), "prefer-smime@example.net");
0363         QCOMPARE(encryptionKeyWidgets.hidden[2]->defaultKey(GpgME::CMS),
0364                  alternativeSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
0365     }
0366 
0367     void test__both_protocols_allowed__mixed_not_allowed__smime_preferred()
0368     {
0369         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0370         const bool allowMixed = false;
0371         const QString sender = QStringLiteral("sender@example.net");
0372         const KeyResolver::Solution preferredSolution = {
0373             GpgME::CMS,
0374             {testKey("sender@example.net", GpgME::CMS)},
0375             {
0376                 {QStringLiteral("prefer-openpgp@example.net"), {}},
0377                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0378                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::CMS)}},
0379             },
0380         };
0381         const KeyResolver::Solution alternativeSolution = {
0382             GpgME::OpenPGP,
0383             {testKey("sender@example.net", GpgME::OpenPGP)},
0384             {
0385                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0386                 {QStringLiteral("prefer-smime@example.net"), {}},
0387                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}},
0388             },
0389         };
0390 
0391         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0392         dialog->show();
0393 
0394         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("openpgp button")), IsVisible, IsUnchecked);
0395         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("smime button")), IsVisible, IsChecked);
0396         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0397         QCOMPARE(signingKeyWidgets.visible.size(), 1);
0398         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
0399         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::CMS), preferredSolution.signingKeys[0].primaryFingerprint());
0400         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP), alternativeSolution.signingKeys[0].primaryFingerprint());
0401 
0402         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0403         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
0404         QCOMPARE(encryptionKeyWidgets.hidden.size(), 3);
0405 
0406         // encryption key widgets for sender come first (visible for S/MIME, hidden for OpenPGP)
0407         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0408         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::CMS), preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0409         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
0410         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP), alternativeSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0411 
0412         // encryption key widgets for other recipients follow (visible for OpenPGP, hidden for S/MIME)
0413         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
0414         QVERIFY(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::CMS).isEmpty());
0415         QCOMPARE(encryptionKeyWidgets.hidden[1]->property("address").toString(), "prefer-openpgp@example.net");
0416         QCOMPARE(encryptionKeyWidgets.hidden[1]->defaultKey(GpgME::OpenPGP),
0417                  alternativeSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
0418         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
0419         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::CMS),
0420                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
0421         QCOMPARE(encryptionKeyWidgets.hidden[2]->property("address").toString(), "prefer-smime@example.net");
0422         QVERIFY(encryptionKeyWidgets.hidden[2]->defaultKey(GpgME::OpenPGP).isEmpty());
0423     }
0424 
0425     void test__openpgp_only()
0426     {
0427         const GpgME::Protocol forcedProtocol = GpgME::OpenPGP;
0428         const bool allowMixed = false;
0429         const QString sender = QStringLiteral("sender@example.net");
0430         const KeyResolver::Solution preferredSolution = {
0431             GpgME::OpenPGP,
0432             {testKey("sender@example.net", GpgME::OpenPGP)},
0433             {
0434                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0435                 {QStringLiteral("prefer-smime@example.net"), {}},
0436                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}},
0437             },
0438         };
0439         const KeyResolver::Solution alternativeSolution = {};
0440 
0441         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0442         dialog->show();
0443 
0444         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("openpgp button")), IsHidden, IsChecked);
0445         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("smime button")), IsHidden, IsUnchecked);
0446         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0447         QCOMPARE(signingKeyWidgets.visible.size(), 1);
0448         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
0449         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.signingKeys[0].primaryFingerprint());
0450 
0451         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0452         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
0453         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
0454 
0455         // encryption key widget for sender comes first
0456         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0457         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0458 
0459         // encryption key widgets for other recipients follow
0460         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
0461         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::OpenPGP),
0462                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
0463         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
0464         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::OpenPGP).isEmpty());
0465     }
0466 
0467     void test__smime_only()
0468     {
0469         const GpgME::Protocol forcedProtocol = GpgME::CMS;
0470         const bool allowMixed = false;
0471         const QString sender = QStringLiteral("sender@example.net");
0472         const KeyResolver::Solution preferredSolution = {
0473             GpgME::CMS,
0474             {testKey("sender@example.net", GpgME::CMS)},
0475             {
0476                 {QStringLiteral("prefer-openpgp@example.net"), {}},
0477                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0478                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::CMS)}},
0479             },
0480         };
0481         const KeyResolver::Solution alternativeSolution = {};
0482 
0483         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0484         dialog->show();
0485 
0486         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("openpgp button")), IsHidden, IsUnchecked);
0487         verifyProtocolButton(dialog->findChild<QRadioButton *>(QStringLiteral("smime button")), IsHidden, IsChecked);
0488         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0489         QCOMPARE(signingKeyWidgets.visible.size(), 1);
0490         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
0491         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::CMS), preferredSolution.signingKeys[0].primaryFingerprint());
0492 
0493         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0494         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
0495         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
0496 
0497         // encryption key widget for sender comes first
0498         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0499         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::CMS), preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0500 
0501         // encryption key widgets for other recipients follow
0502         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
0503         QVERIFY(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::CMS).isEmpty());
0504         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
0505         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::CMS),
0506                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
0507     }
0508 
0509     void test__both_protocols_allowed__mixed_allowed()
0510     {
0511         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0512         const bool allowMixed = true;
0513         const QString sender = QStringLiteral("sender@example.net");
0514         const KeyResolver::Solution preferredSolution = {
0515             GpgME::UnknownProtocol,
0516             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
0517             {
0518                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0519                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0520                 {QStringLiteral("unknown@example.net"), {}},
0521                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
0522             },
0523         };
0524         const KeyResolver::Solution alternativeSolution = {};
0525 
0526         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0527         dialog->show();
0528 
0529         verifyProtocolButton(dialog->findChild<QCheckBox *>(QStringLiteral("openpgp button")), IsVisible, IsChecked);
0530         verifyProtocolButton(dialog->findChild<QCheckBox *>(QStringLiteral("smime button")), IsVisible, IsChecked);
0531         verifyProtocolLabels(dialog->findChildren<QLabel *>(QStringLiteral("protocol label")), 4, IsVisible);
0532         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0533         QCOMPARE(signingKeyWidgets.visible.size(), 2);
0534         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
0535         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.signingKeys[0].primaryFingerprint());
0536         QCOMPARE(signingKeyWidgets.visible[1]->defaultKey(GpgME::CMS), preferredSolution.signingKeys[1].primaryFingerprint());
0537 
0538         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0539         QCOMPARE(encryptionKeyWidgets.visible.size(), 5);
0540         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
0541 
0542         // encryption key widgets for sender come first
0543         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0544         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0545         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), sender);
0546         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::CMS), preferredSolution.encryptionKeys.value(sender)[1].primaryFingerprint());
0547 
0548         // encryption key widgets for other recipients follow
0549         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-openpgp@example.net");
0550         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol),
0551                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
0552         QCOMPARE(encryptionKeyWidgets.visible[3]->property("address").toString(), "prefer-smime@example.net");
0553         QCOMPARE(encryptionKeyWidgets.visible[3]->defaultKey(GpgME::UnknownProtocol),
0554                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
0555         QCOMPARE(encryptionKeyWidgets.visible[4]->property("address").toString(), "unknown@example.net");
0556         QVERIFY(encryptionKeyWidgets.visible[4]->defaultKey(GpgME::UnknownProtocol).isEmpty());
0557     }
0558 
0559     void test__both_protocols_allowed__mixed_allowed__openpgp_only_preferred_solution()
0560     {
0561         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0562         const bool allowMixed = true;
0563         const QString sender = QStringLiteral("sender@example.net");
0564         const KeyResolver::Solution preferredSolution = {
0565             GpgME::OpenPGP,
0566             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
0567             {
0568                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0569                 {QStringLiteral("unknown@example.net"), {}},
0570                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
0571             },
0572         };
0573         const KeyResolver::Solution alternativeSolution = {};
0574 
0575         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0576         dialog->show();
0577 
0578         verifyProtocolButton(dialog->findChild<QCheckBox *>(QStringLiteral("openpgp button")), IsVisible, IsChecked);
0579         verifyProtocolButton(dialog->findChild<QCheckBox *>(QStringLiteral("smime button")), IsVisible, IsUnchecked);
0580         verifyProtocolLabels(dialog->findChildren<QLabel *>(QStringLiteral("protocol label")), 4, IsHidden);
0581         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0582         QCOMPARE(signingKeyWidgets.visible.size(), 1);
0583         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
0584         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.signingKeys[0].primaryFingerprint());
0585         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::CMS), preferredSolution.signingKeys[1].primaryFingerprint());
0586 
0587         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0588         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
0589         QCOMPARE(encryptionKeyWidgets.hidden.size(), 1);
0590 
0591         // encryption key widgets for sender come first
0592         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0593         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP), preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0594         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
0595         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::CMS), preferredSolution.encryptionKeys.value(sender)[1].primaryFingerprint());
0596 
0597         // encryption key widgets for other recipients follow
0598         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
0599         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::UnknownProtocol),
0600                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
0601         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "unknown@example.net");
0602         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol).isEmpty());
0603     }
0604 
0605     void test__both_protocols_allowed__mixed_allowed__smime_only_preferred_solution()
0606     {
0607         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0608         const bool allowMixed = true;
0609         const QString sender = QStringLiteral("sender@example.net");
0610         const KeyResolver::Solution preferredSolution = {
0611             GpgME::CMS,
0612             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
0613             {
0614                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0615                 {QStringLiteral("unknown@example.net"), {}},
0616                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
0617             },
0618         };
0619         const KeyResolver::Solution alternativeSolution = {};
0620 
0621         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0622         dialog->show();
0623 
0624         verifyProtocolButton(dialog->findChild<QCheckBox *>(QStringLiteral("openpgp button")), IsVisible, IsUnchecked);
0625         verifyProtocolButton(dialog->findChild<QCheckBox *>(QStringLiteral("smime button")), IsVisible, IsChecked);
0626         verifyProtocolLabels(dialog->findChildren<QLabel *>(QStringLiteral("protocol label")), 4, IsHidden);
0627         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0628         QCOMPARE(signingKeyWidgets.visible.size(), 1);
0629         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
0630         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::CMS), preferredSolution.signingKeys[1].primaryFingerprint());
0631         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP), preferredSolution.signingKeys[0].primaryFingerprint());
0632 
0633         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0634         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
0635         QCOMPARE(encryptionKeyWidgets.hidden.size(), 1);
0636 
0637         // encryption key widgets for sender come first
0638         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0639         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::CMS), preferredSolution.encryptionKeys.value(sender)[1].primaryFingerprint());
0640         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
0641         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP), preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
0642 
0643         // encryption key widgets for other recipients follow
0644         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-smime@example.net");
0645         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::UnknownProtocol),
0646                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
0647         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "unknown@example.net");
0648         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol).isEmpty());
0649     }
0650 
0651     void test__both_protocols_allowed__mixed_allowed__no_sender_keys()
0652     {
0653         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0654         const bool allowMixed = true;
0655         const QString sender = QStringLiteral("sender@example.net");
0656         const KeyResolver::Solution preferredSolution = {
0657             GpgME::UnknownProtocol,
0658             {},
0659             {
0660                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0661                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0662                 {QStringLiteral("unknown@example.net"), {}},
0663                 {QStringLiteral("sender@example.net"), {}},
0664             },
0665         };
0666         const KeyResolver::Solution alternativeSolution = {};
0667 
0668         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0669         dialog->show();
0670 
0671         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0672         QCOMPARE(signingKeyWidgets.visible.size(), 2);
0673         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
0674 
0675         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0676         QCOMPARE(encryptionKeyWidgets.visible.size(), 5);
0677         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
0678 
0679         // encryption key widgets for sender come first
0680         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
0681         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), sender);
0682 
0683         // encryption key widgets for other recipients follow
0684         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-openpgp@example.net");
0685         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol),
0686                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
0687         QCOMPARE(encryptionKeyWidgets.visible[3]->property("address").toString(), "prefer-smime@example.net");
0688         QCOMPARE(encryptionKeyWidgets.visible[3]->defaultKey(GpgME::UnknownProtocol),
0689                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
0690         QCOMPARE(encryptionKeyWidgets.visible[4]->property("address").toString(), "unknown@example.net");
0691         QVERIFY(encryptionKeyWidgets.visible[4]->defaultKey(GpgME::UnknownProtocol).isEmpty());
0692     }
0693 
0694     void test__both_protocols_allowed__mixed_allowed__encrypt_only()
0695     {
0696         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0697         const bool allowMixed = true;
0698         const QString sender = QStringLiteral("sender@example.net");
0699         const KeyResolver::Solution preferredSolution = {
0700             GpgME::UnknownProtocol,
0701             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
0702             {
0703                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0704                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0705                 {QStringLiteral("unknown@example.net"), {}},
0706                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
0707             },
0708         };
0709         const KeyResolver::Solution alternativeSolution = {};
0710 
0711         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, false, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0712         dialog->show();
0713 
0714         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
0715         QCOMPARE(signingKeyWidgets.visible.size(), 0);
0716         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
0717 
0718         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0719         QCOMPARE(encryptionKeyWidgets.visible.size(), 5);
0720         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
0721     }
0722 
0723     void test__ok_button_shows_generate_if_generate_is_selected()
0724     {
0725         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0726         const bool allowMixed = true;
0727         const QString sender = QStringLiteral("sender@example.net");
0728         const KeyResolver::Solution preferredSolution = {
0729             GpgME::OpenPGP,
0730             {}, // no signing keys to get "Generate key" choice in OpenPGP combo
0731             {{QStringLiteral("sender@example.net"), {}}} // no encryption keys to get "Generate key" choice in OpenPGP combo
0732         };
0733         const KeyResolver::Solution alternativeSolution = {};
0734 
0735         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0736         dialog->show();
0737         waitForKeySelectionCombosBeingInitialized(dialog.get());
0738 
0739         const auto okButton = dialog->findChild<QPushButton *>("ok button");
0740         QVERIFY(okButton);
0741         QVERIFY(okButton->text() != "Generate");
0742 
0743         {
0744             // get the first signing key combo which is the OpenPGP one
0745             const auto signingKeyCombo = dialog->findChild<KeySelectionCombo *>("signing key");
0746             verifyWidgetVisibility(signingKeyCombo, IsVisible);
0747             const auto originalIndex = signingKeyCombo->currentIndex();
0748             const auto generateIndex = signingKeyCombo->findData(GenerateKey);
0749             QVERIFY(generateIndex != -1);
0750             signingKeyCombo->setCurrentIndex(generateIndex);
0751             QCOMPARE(okButton->text(), "Generate");
0752             signingKeyCombo->setCurrentIndex(originalIndex);
0753             QVERIFY(okButton->text() != "Generate");
0754         }
0755         {
0756             // get the first encryption key combo which is the OpenPGP one for the sender
0757             const auto encryptionKeyCombo = dialog->findChild<KeySelectionCombo *>("encryption key");
0758             verifyWidgetVisibility(encryptionKeyCombo, IsVisible);
0759             const auto originalIndex = encryptionKeyCombo->currentIndex();
0760             const auto generateIndex = encryptionKeyCombo->findData(GenerateKey);
0761             QVERIFY(generateIndex != -1);
0762             encryptionKeyCombo->setCurrentIndex(generateIndex);
0763             QCOMPARE(okButton->text(), QStringLiteral("Generate"));
0764             encryptionKeyCombo->setCurrentIndex(originalIndex);
0765             QVERIFY(okButton->text() != QStringLiteral("Generate"));
0766         }
0767     }
0768 
0769     void test__ok_button_does_not_show_generate_if_generate_is_selected_in_hidden_combos()
0770     {
0771         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0772         const bool allowMixed = true;
0773         const QString sender = QStringLiteral("sender@example.net");
0774         const KeyResolver::Solution preferredSolution = {
0775             GpgME::CMS, // enables S/MIME as default protocol, hides OpenPGP combos
0776             {}, // no signing keys to get "Generate key" choice in OpenPGP combo
0777             {{QStringLiteral("sender@example.net"), {}}} // no encryption keys to get "Generate key" choice in OpenPGP combo
0778         };
0779         const KeyResolver::Solution alternativeSolution = {};
0780 
0781         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0782         dialog->show();
0783         waitForKeySelectionCombosBeingInitialized(dialog.get());
0784 
0785         const auto okButton = dialog->findChild<QPushButton *>("ok button");
0786         QVERIFY(okButton);
0787         QVERIFY(okButton->text() != "Generate");
0788 
0789         {
0790             // get the first signing key combo which is the OpenPGP one
0791             const auto signingKeyCombo = dialog->findChild<KeySelectionCombo *>("signing key");
0792             verifyWidgetVisibility(signingKeyCombo, IsHidden);
0793             const auto originalIndex = signingKeyCombo->currentIndex();
0794             const auto generateIndex = signingKeyCombo->findData(GenerateKey);
0795             QVERIFY(generateIndex != -1);
0796             signingKeyCombo->setCurrentIndex(generateIndex);
0797             QVERIFY(okButton->text() != QStringLiteral("Generate"));
0798             signingKeyCombo->setCurrentIndex(originalIndex);
0799             QVERIFY(okButton->text() != QStringLiteral("Generate"));
0800         }
0801         {
0802             // get the first encryption key combo which is the OpenPGP one for the sender
0803             const auto encryptionKeyCombo = dialog->findChild<KeySelectionCombo *>("encryption key");
0804             verifyWidgetVisibility(encryptionKeyCombo, IsHidden);
0805             const auto originalIndex = encryptionKeyCombo->currentIndex();
0806             const auto generateIndex = encryptionKeyCombo->findData(GenerateKey);
0807             QVERIFY(generateIndex != -1);
0808             encryptionKeyCombo->setCurrentIndex(generateIndex);
0809             QVERIFY(okButton->text() != QStringLiteral("Generate"));
0810             encryptionKeyCombo->setCurrentIndex(originalIndex);
0811             QVERIFY(okButton->text() != QStringLiteral("Generate"));
0812         }
0813     }
0814 
0815     void test__ok_button_is_disabled_if_ignore_is_selected_in_all_visible_encryption_combos()
0816     {
0817         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0818         const bool allowMixed = true;
0819         const QString sender = QStringLiteral("sender@example.net");
0820         const KeyResolver::Solution preferredSolution = {
0821             GpgME::OpenPGP,
0822             {}, // no signing keys to get "Generate key" choice in OpenPGP combo
0823             {{QStringLiteral("sender@example.net"), {}}} // no encryption keys to get "Generate key" choice in OpenPGP combo
0824         };
0825         const KeyResolver::Solution alternativeSolution = {};
0826 
0827         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0828         dialog->show();
0829         waitForKeySelectionCombosBeingInitialized(dialog.get());
0830 
0831         const auto okButton = dialog->findChild<QPushButton *>(QStringLiteral("ok button"));
0832         QVERIFY(okButton);
0833         QVERIFY(okButton->isEnabled());
0834 
0835         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
0836         for (auto combo : encryptionKeyWidgets.visible) {
0837             const auto ignoreIndex = combo->findData(IgnoreKey);
0838             QVERIFY(ignoreIndex != -1);
0839             combo->setCurrentIndex(ignoreIndex);
0840         }
0841         QVERIFY(!okButton->isEnabled());
0842     }
0843 
0844     void test__vs_de_compliance__all_keys_fully_valid()
0845     {
0846         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0847         const bool allowMixed = true;
0848         const QString sender = QStringLiteral("sender@example.net");
0849         const KeyResolver::Solution preferredSolution = {
0850             GpgME::UnknownProtocol,
0851             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
0852             {
0853                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0854                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0855                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
0856             },
0857         };
0858         const KeyResolver::Solution alternativeSolution = {};
0859 
0860         Tests::FakeCryptoConfigStringValue fakeCompliance{"gpg", "compliance", QStringLiteral("de-vs")};
0861         Tests::FakeCryptoConfigIntValue fakeDeVsCompliance{"gpg", "compliance_de_vs", 1};
0862         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0863         dialog->show();
0864         waitForKeySelectionCombosBeingInitialized(dialog.get());
0865 
0866         const auto complianceLabel = dialog->findChild<QLabel *>(QStringLiteral("compliance label"));
0867         verifyWidgetVisibility(complianceLabel, IsVisible);
0868         QVERIFY(!complianceLabel->text().contains(DeVSCompliance::name(false)));
0869     }
0870 
0871     void test__vs_de_compliance__not_all_keys_fully_valid()
0872     {
0873         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0874         const bool allowMixed = true;
0875         const QString sender = QStringLiteral("sender@example.net");
0876         const KeyResolver::Solution preferredSolution = {
0877             GpgME::UnknownProtocol,
0878             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
0879             {
0880                 {QStringLiteral("marginal-openpgp@example.net"), {testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP)}},
0881                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
0882             },
0883         };
0884         const KeyResolver::Solution alternativeSolution = {};
0885 
0886         Tests::FakeCryptoConfigStringValue fakeCompliance{"gpg", "compliance", QStringLiteral("de-vs")};
0887         Tests::FakeCryptoConfigIntValue fakeDeVsCompliance{"gpg", "compliance_de_vs", 1};
0888         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0889         dialog->show();
0890         waitForKeySelectionCombosBeingInitialized(dialog.get());
0891 
0892         const auto complianceLabel = dialog->findChild<QLabel *>(QStringLiteral("compliance label"));
0893         verifyWidgetVisibility(complianceLabel, IsVisible);
0894         QVERIFY(complianceLabel->text().contains(DeVSCompliance::name(false)));
0895     }
0896 
0897     void test__vs_de_compliance__null_keys_are_ignored()
0898     {
0899         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0900         const bool allowMixed = true;
0901         const QString sender = QStringLiteral("sender@example.net");
0902         const KeyResolver::Solution preferredSolution = {
0903             GpgME::UnknownProtocol,
0904             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
0905             {
0906                 {QStringLiteral("unknown@example.net"), {}},
0907                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
0908             },
0909         };
0910         const KeyResolver::Solution alternativeSolution = {};
0911 
0912         Tests::FakeCryptoConfigStringValue fakeCompliance{"gpg", "compliance", QStringLiteral("de-vs")};
0913         Tests::FakeCryptoConfigIntValue fakeDeVsCompliance{"gpg", "compliance_de_vs", 1};
0914         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0915         dialog->show();
0916         waitForKeySelectionCombosBeingInitialized(dialog.get());
0917 
0918         const auto complianceLabel = dialog->findChild<QLabel *>(QStringLiteral("compliance label"));
0919         verifyWidgetVisibility(complianceLabel, IsVisible);
0920         QVERIFY(!complianceLabel->text().contains(DeVSCompliance::name(false)));
0921     }
0922 
0923     void test__sign_and_encrypt_to_self_only()
0924     {
0925         const GpgME::Protocol forcedProtocol = GpgME::OpenPGP;
0926         const bool allowMixed = false;
0927         const QString sender = QStringLiteral("sender@example.net");
0928         const KeyResolver::Solution preferredSolution = {
0929             GpgME::OpenPGP,
0930             {testKey("sender@example.net", GpgME::OpenPGP)},
0931             {
0932                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}},
0933             },
0934         };
0935         const KeyResolver::Solution alternativeSolution = {};
0936 
0937         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0938         dialog->show();
0939 
0940         QVERIFY(!dialog->findChild<QGroupBox *>(QStringLiteral("encrypt-to-others box")));
0941     }
0942 
0943     void test__sign_and_encrypt_to_self_and_others()
0944     {
0945         const GpgME::Protocol forcedProtocol = GpgME::OpenPGP;
0946         const bool allowMixed = false;
0947         const QString sender = QStringLiteral("sender@example.net");
0948         const KeyResolver::Solution preferredSolution = {
0949             GpgME::OpenPGP,
0950             {testKey("sender@example.net", GpgME::OpenPGP)},
0951             {
0952                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0953                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}},
0954             },
0955         };
0956         const KeyResolver::Solution alternativeSolution = {};
0957 
0958         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0959         dialog->show();
0960 
0961         QVERIFY(dialog->findChild<QGroupBox *>(QStringLiteral("encrypt-to-others box")));
0962     }
0963 
0964     void test__result_does_not_include_null_keys()
0965     {
0966         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
0967         const bool allowMixed = true;
0968         const QString sender = QStringLiteral("unknown@example.net");
0969         const KeyResolver::Solution preferredSolution = {
0970             GpgME::UnknownProtocol,
0971             {},
0972             {
0973                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0974                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
0975                 {QStringLiteral("unknown@example.net"), {}},
0976             },
0977         };
0978         const KeyResolver::Solution alternativeSolution = {};
0979 
0980         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
0981         dialog->show();
0982         waitForKeySelectionCombosBeingInitialized(dialog.get());
0983         switchKeySelectionCombosFromGenerateKeyToIgnoreKey(dialog->findChildren<KeySelectionCombo *>());
0984 
0985         const QSignalSpy dialogAcceptedSpy{dialog.get(), &QDialog::accepted};
0986         QVERIFY(dialogAcceptedSpy.isValid());
0987 
0988         const auto okButton = dialog->findChild<QPushButton *>(QStringLiteral("ok button"));
0989         QVERIFY(okButton);
0990         QVERIFY(okButton->isEnabled());
0991         okButton->click();
0992 
0993         QCOMPARE(dialogAcceptedSpy.count(), 1);
0994         verifySolution(dialog->result(),
0995                        {GpgME::UnknownProtocol,
0996                         {},
0997                         {
0998                             {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
0999                             {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
1000                         }});
1001     }
1002 
1003     void test__result_has_keys_for_both_protocols_if_both_are_needed()
1004     {
1005         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
1006         const bool allowMixed = true;
1007         const QString sender = QStringLiteral("sender@example.net");
1008         const KeyResolver::Solution preferredSolution = {
1009             GpgME::UnknownProtocol,
1010             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
1011             {
1012                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
1013                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
1014                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
1015             },
1016         };
1017         const KeyResolver::Solution alternativeSolution = {};
1018 
1019         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
1020         dialog->show();
1021         waitForKeySelectionCombosBeingInitialized(dialog.get());
1022         switchKeySelectionCombosFromGenerateKeyToIgnoreKey(dialog->findChildren<KeySelectionCombo *>());
1023 
1024         const QSignalSpy dialogAcceptedSpy{dialog.get(), &QDialog::accepted};
1025         QVERIFY(dialogAcceptedSpy.isValid());
1026 
1027         const auto okButton = dialog->findChild<QPushButton *>(QStringLiteral("ok button"));
1028         QVERIFY(okButton);
1029         QVERIFY(okButton->isEnabled());
1030         okButton->click();
1031 
1032         QCOMPARE(dialogAcceptedSpy.count(), 1);
1033         verifySolution(dialog->result(), preferredSolution);
1034     }
1035 
1036     void test__result_has_only_openpgp_keys_if_openpgp_protocol_selected()
1037     {
1038         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
1039         const bool allowMixed = true;
1040         const QString sender = QStringLiteral("sender@example.net");
1041         const KeyResolver::Solution preferredSolution = {
1042             GpgME::UnknownProtocol,
1043             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
1044             {
1045                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
1046                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
1047                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
1048             },
1049         };
1050         const KeyResolver::Solution alternativeSolution = {};
1051 
1052         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
1053         dialog->show();
1054         waitForKeySelectionCombosBeingInitialized(dialog.get());
1055         switchKeySelectionCombosFromGenerateKeyToIgnoreKey(dialog->findChildren<KeySelectionCombo *>());
1056 
1057         const auto smimeButton = dialog->findChild<QCheckBox *>(QStringLiteral("smime button"));
1058         QVERIFY(smimeButton);
1059         smimeButton->click();
1060         QVERIFY(!smimeButton->isChecked());
1061 
1062         const QSignalSpy dialogAcceptedSpy{dialog.get(), &QDialog::accepted};
1063         QVERIFY(dialogAcceptedSpy.isValid());
1064 
1065         const auto okButton = dialog->findChild<QPushButton *>(QStringLiteral("ok button"));
1066         QVERIFY(okButton);
1067         QVERIFY(okButton->isEnabled());
1068         okButton->click();
1069 
1070         QCOMPARE(dialogAcceptedSpy.count(), 1);
1071         verifySolution(dialog->result(),
1072                        {GpgME::OpenPGP,
1073                         {testKey("sender@example.net", GpgME::OpenPGP)},
1074                         {
1075                             {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
1076                             {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}},
1077                         }});
1078     }
1079 
1080     void test__result_has_only_smime_keys_if_smime_protocol_selected()
1081     {
1082         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
1083         const bool allowMixed = true;
1084         const QString sender = QStringLiteral("sender@example.net");
1085         const KeyResolver::Solution preferredSolution = {
1086             GpgME::UnknownProtocol,
1087             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
1088             {
1089                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
1090                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
1091                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}},
1092             },
1093         };
1094         const KeyResolver::Solution alternativeSolution = {};
1095 
1096         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true, true, sender, preferredSolution, alternativeSolution, allowMixed, forcedProtocol);
1097         dialog->show();
1098         waitForKeySelectionCombosBeingInitialized(dialog.get());
1099         switchKeySelectionCombosFromGenerateKeyToIgnoreKey(dialog->findChildren<KeySelectionCombo *>());
1100 
1101         const auto openPGPButton = dialog->findChild<QCheckBox *>(QStringLiteral("openpgp button"));
1102         QVERIFY(openPGPButton);
1103         openPGPButton->click();
1104         QVERIFY(!openPGPButton->isChecked());
1105 
1106         const QSignalSpy dialogAcceptedSpy{dialog.get(), &QDialog::accepted};
1107         QVERIFY(dialogAcceptedSpy.isValid());
1108 
1109         const auto okButton = dialog->findChild<QPushButton *>(QStringLiteral("ok button"));
1110         QVERIFY(okButton);
1111         QVERIFY(okButton->isEnabled());
1112         okButton->click();
1113 
1114         QCOMPARE(dialogAcceptedSpy.count(), 1);
1115         verifySolution(dialog->result(),
1116                        {GpgME::CMS,
1117                         {testKey("sender@example.net", GpgME::CMS)},
1118                         {
1119                             {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
1120                             {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::CMS)}},
1121                         }});
1122     }
1123 
1124 private:
1125     std::shared_ptr<const KeyCache> mKeyCache;
1126 };
1127 
1128 QTEST_MAIN(NewKeyApprovalDialogTest)
1129 #include "newkeyapprovaldialogtest.moc"