File indexing completed on 2024-05-12 05:22:35
0001 /* 0002 autotests/keyselectioncombotest.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/Formatting> 0012 #include <Libkleo/KeyCache> 0013 #include <Libkleo/KeySelectionCombo> 0014 0015 #include <QSignalSpy> 0016 #include <QTest> 0017 0018 #include <gpgme++/key.h> 0019 #include <gpgme++/keylistresult.h> 0020 0021 #include <gpgme.h> 0022 0023 #include <memory> 0024 0025 using namespace Kleo; 0026 0027 namespace 0028 { 0029 0030 auto mapValidity(GpgME::UserID::Validity validity) 0031 { 0032 switch (validity) { 0033 default: 0034 case GpgME::UserID::Unknown: 0035 return GPGME_VALIDITY_UNKNOWN; 0036 case GpgME::UserID::Undefined: 0037 return GPGME_VALIDITY_UNDEFINED; 0038 case GpgME::UserID::Never: 0039 return GPGME_VALIDITY_NEVER; 0040 case GpgME::UserID::Marginal: 0041 return GPGME_VALIDITY_MARGINAL; 0042 case GpgME::UserID::Full: 0043 return GPGME_VALIDITY_FULL; 0044 case GpgME::UserID::Ultimate: 0045 return GPGME_VALIDITY_ULTIMATE; 0046 } 0047 } 0048 0049 GpgME::Key createTestKey(const char *uid, 0050 GpgME::Protocol protocol = GpgME::UnknownProtocol, 0051 KeyCache::KeyUsage usage = KeyCache::KeyUsage::AnyUsage, 0052 GpgME::UserID::Validity validity = GpgME::UserID::Full) 0053 { 0054 static int count = 0; 0055 count++; 0056 0057 gpgme_key_t key; 0058 gpgme_key_from_uid(&key, uid); 0059 Q_ASSERT(key); 0060 Q_ASSERT(key->uids); 0061 if (protocol != GpgME::UnknownProtocol) { 0062 key->protocol = protocol == GpgME::OpenPGP ? GPGME_PROTOCOL_OpenPGP : GPGME_PROTOCOL_CMS; 0063 } 0064 const QByteArray fingerprint = QByteArray::number(count, 16).rightJustified(40, '0'); 0065 key->fpr = strdup(fingerprint.constData()); 0066 key->revoked = 0; 0067 key->expired = 0; 0068 key->disabled = 0; 0069 key->can_encrypt = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Encrypt); 0070 key->can_sign = int(usage == KeyCache::KeyUsage::AnyUsage || usage == KeyCache::KeyUsage::Sign); 0071 key->secret = 1; 0072 key->uids->validity = mapValidity(validity); 0073 0074 return GpgME::Key(key, false); 0075 } 0076 0077 auto testKey(const char *address, GpgME::Protocol protocol = GpgME::UnknownProtocol) 0078 { 0079 const auto email = GpgME::UserID::addrSpecFromString(address); 0080 const auto keys = KeyCache::instance()->findByEMailAddress(email); 0081 for (const auto &key : keys) { 0082 if (protocol == GpgME::UnknownProtocol || key.protocol() == protocol) { 0083 return key; 0084 } 0085 } 0086 return GpgME::Key(); 0087 } 0088 0089 void waitForKeySelectionComboBeingInitialized(const KeySelectionCombo *combo) 0090 { 0091 QVERIFY(combo); 0092 0093 const auto spy = std::make_unique<QSignalSpy>(combo, &KeySelectionCombo::keyListingFinished); 0094 QVERIFY(spy->isValid()); 0095 QVERIFY(spy->wait(10)); 0096 } 0097 0098 } 0099 0100 class KeySelectionComboTest : public QObject 0101 { 0102 Q_OBJECT 0103 0104 private Q_SLOTS: 0105 void initTestCase() 0106 { 0107 // force C locale for sorting 0108 QLocale::setDefault(QLocale::c()); 0109 } 0110 0111 void init() 0112 { 0113 // hold a reference to the key cache to avoid rebuilding while the test is running 0114 mKeyCache = KeyCache::instance(); 0115 0116 KeyCache::mutableInstance()->setKeys({ 0117 createTestKey("sender@example.net", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::AnyUsage), 0118 createTestKey("CN=,EMAIL=sender@example.net", GpgME::CMS, KeyCache::KeyCache::KeyUsage::AnyUsage), 0119 createTestKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::Encrypt), 0120 createTestKey("CN=Trusted S/MIME,EMAIL=prefer-smime@example.net", GpgME::CMS, KeyCache::KeyCache::KeyUsage::Encrypt), 0121 createTestKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP, KeyCache::KeyCache::KeyUsage::Encrypt, GpgME::UserID::Marginal), 0122 }); 0123 } 0124 0125 void cleanup() 0126 { 0127 // verify that nobody else holds a reference to the key cache 0128 QVERIFY(mKeyCache.use_count() == 1); 0129 mKeyCache.reset(); 0130 } 0131 0132 void test__verify_test_keys() 0133 { 0134 QVERIFY(!testKey("sender@example.net", GpgME::OpenPGP).isNull()); 0135 QVERIFY(!testKey("sender@example.net", GpgME::CMS).isNull()); 0136 QVERIFY(!testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).isNull()); 0137 QVERIFY(!testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).isNull()); 0138 QVERIFY(!testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).isNull()); 0139 } 0140 0141 void test__certificates_are_sorted_alphabetically() 0142 { 0143 const auto combo = std::make_unique<KeySelectionCombo>(); 0144 waitForKeySelectionComboBeingInitialized(combo.get()); 0145 QCOMPARE(combo->itemText(0).left(combo->itemText(0).indexOf(" (")), "Full Trust <prefer-openpgp@example.net>"); 0146 QCOMPARE(combo->itemText(1).left(combo->itemText(1).indexOf(" (")), "Marginal Validity <marginal-openpgp@example.net>"); 0147 QCOMPARE(combo->itemText(2).left(combo->itemText(2).indexOf(" (")), "Trusted S/MIME <prefer-smime@example.net>"); 0148 // we don't check the order of the two "sender@example.net" keys 0149 } 0150 0151 void test__after_initialization_default_key_is_current_key() 0152 { 0153 const auto combo = std::make_unique<KeySelectionCombo>(); 0154 combo->setDefaultKey(QString::fromLatin1(testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint())); 0155 waitForKeySelectionComboBeingInitialized(combo.get()); 0156 0157 QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()); 0158 } 0159 0160 void test__currently_selected_key_is_retained_if_cache_is_updated() 0161 { 0162 const auto combo = std::make_unique<KeySelectionCombo>(); 0163 combo->setDefaultKey(QString::fromLatin1(testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint())); 0164 waitForKeySelectionComboBeingInitialized(combo.get()); 0165 0166 combo->setCurrentKey(testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)); 0167 0168 QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).primaryFingerprint()); 0169 0170 Q_EMIT KeyCache::mutableInstance()->keyListingDone(GpgME::KeyListResult{}); 0171 0172 QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).primaryFingerprint()); 0173 } 0174 0175 void test__default_key_is_selected_if_currently_selected_key_is_gone_after_model_update() 0176 { 0177 const auto combo = std::make_unique<KeySelectionCombo>(); 0178 combo->setDefaultKey(QString::fromLatin1(testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint())); 0179 waitForKeySelectionComboBeingInitialized(combo.get()); 0180 0181 combo->setCurrentKey(testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)); 0182 0183 QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS).primaryFingerprint()); 0184 0185 KeyCache::mutableInstance()->setKeys({ 0186 testKey("sender@example.net", GpgME::OpenPGP), 0187 testKey("sender@example.net", GpgME::CMS), 0188 testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP), 0189 testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP), 0190 }); 0191 0192 QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()); 0193 } 0194 0195 void test__currently_selected_custom_item_is_retained_if_cache_is_updated() 0196 { 0197 const auto combo = std::make_unique<KeySelectionCombo>(); 0198 combo->prependCustomItem({}, {}, QStringLiteral("custom1")); 0199 combo->appendCustomItem({}, {}, QStringLiteral("custom2")); 0200 combo->setDefaultKey(QString::fromLatin1(testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint())); 0201 waitForKeySelectionComboBeingInitialized(combo.get()); 0202 0203 combo->setCurrentIndex(combo->count() - 1); 0204 QCOMPARE(combo->currentData(), QStringLiteral("custom2")); 0205 0206 Q_EMIT KeyCache::mutableInstance()->keyListingDone(GpgME::KeyListResult{}); 0207 0208 QCOMPARE(combo->currentData(), QStringLiteral("custom2")); 0209 } 0210 0211 void test__default_key_is_selected_if_currently_selected_custom_item_is_gone_after_model_update() 0212 { 0213 const auto combo = std::make_unique<KeySelectionCombo>(); 0214 combo->prependCustomItem({}, {}, QStringLiteral("custom1")); 0215 combo->appendCustomItem({}, {}, QStringLiteral("custom2")); 0216 combo->setDefaultKey(QString::fromLatin1(testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint())); 0217 waitForKeySelectionComboBeingInitialized(combo.get()); 0218 0219 combo->setCurrentIndex(combo->count() - 1); 0220 QCOMPARE(combo->currentData(), QStringLiteral("custom2")); 0221 0222 combo->removeCustomItem(QStringLiteral("custom2")); 0223 0224 QCOMPARE(combo->currentKey().primaryFingerprint(), testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP).primaryFingerprint()); 0225 } 0226 0227 private: 0228 std::shared_ptr<const KeyCache> mKeyCache; 0229 }; 0230 0231 QTEST_MAIN(KeySelectionComboTest) 0232 #include "keyselectioncombotest.moc"