File indexing completed on 2024-10-06 03:39:57
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2010 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2012 Dawit Alemayehu <adawit@kde.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #include <kpasswdserver.h> 0010 0011 #include <KPasswordDialog> 0012 0013 #include <QApplication> 0014 #include <QSignalSpy> 0015 #include <QTest> 0016 0017 // For the retry dialog (and only that one) 0018 static QDialogButtonBox::StandardButton s_buttonYes = QDialogButtonBox::Yes; 0019 static QDialogButtonBox::StandardButton s_buttonCancel = QDialogButtonBox::Cancel; 0020 0021 Q_DECLARE_METATYPE(QDialogButtonBox::StandardButton) 0022 Q_DECLARE_METATYPE(QDialog::DialogCode) 0023 0024 static QString getUserNameFrom(const KIO::AuthInfo &auth) 0025 { 0026 if (auth.username.isEmpty() && !auth.url.userName().isEmpty()) { 0027 return auth.url.userName(); 0028 } 0029 0030 return auth.username; 0031 } 0032 0033 class KPasswdServerTest : public QObject 0034 { 0035 Q_OBJECT 0036 0037 private Q_SLOTS: 0038 void initTestCase() 0039 { 0040 qRegisterMetaType<QDialogButtonBox::StandardButton>(); 0041 qRegisterMetaType<QDialog::DialogCode>(); 0042 } 0043 0044 void simpleTest() 0045 { 0046 KPasswdServer server(this); 0047 server.setWalletDisabled(true); 0048 0049 // Check that processRequest doesn't crash when it has nothing to do 0050 server.processRequest(); 0051 0052 KIO::AuthInfo info; 0053 info.url = QUrl(QStringLiteral("http://www.example.com")); 0054 info.keepPassword = true; 0055 0056 // Make a check for that host, should say "not found" 0057 QVERIFY(noCheckAuth(server, info)); 0058 0059 // Now add auth to the cache 0060 const qlonglong windowId = 42; 0061 KIO::AuthInfo realInfo = info; 0062 realInfo.username = QStringLiteral("toto"); // you can see I'm french 0063 realInfo.password = QStringLiteral("foobar"); 0064 server.addAuthInfo(realInfo, windowId); // seqnr=2 0065 0066 // queryAuth without the ability to prompt, will just return info unmodified 0067 KIO::AuthInfo resultInfo; 0068 queryAuth(server, info, resultInfo); 0069 QCOMPARE(resultInfo.url, info.url); 0070 QCOMPARE(resultInfo.username, QString()); 0071 QCOMPARE(resultInfo.password, QString()); 0072 QCOMPARE(resultInfo.isModified(), false); 0073 0074 // Check that checkAuth finds it 0075 QVERIFY(successCheckAuth(server, info, realInfo)); 0076 0077 // Now remove auth 0078 server.removeAuthInfo(info.url.host(), info.url.scheme(), info.username); 0079 // Check we can't find that auth anymore 0080 QVERIFY(noCheckAuth(server, info)); 0081 } 0082 0083 void testCheckDuringQuery() 0084 { 0085 KPasswdServer server(this); 0086 server.setWalletDisabled(true); 0087 KIO::AuthInfo info; 0088 info.url = QUrl(QStringLiteral("http://www.kde.org")); 0089 0090 // Start a query 0091 QSignalSpy spyQuery(&server, &KPasswdServer::queryAuthInfoAsyncResult); 0092 const qlonglong windowId = 42; 0093 const qlonglong seqNr = 2; 0094 const qlonglong id = server.queryAuthInfoAsync(info, 0095 QStringLiteral("<NoAuthPrompt>"), // magic string to avoid a dialog 0096 windowId, 0097 seqNr, 0098 16 /*usertime*/); 0099 0100 // Before it is processed, do a check, it will reply delayed. 0101 QSignalSpy spyCheck(&server, &KPasswdServer::checkAuthInfoAsyncResult); 0102 const qlonglong idCheck = server.checkAuthInfoAsync(info, windowId, 17 /*usertime*/); 0103 QCOMPARE(idCheck, 0LL); // always 0104 QCOMPARE(spyCheck.count(), 0); // no reply yet 0105 0106 // Wait for the query to be processed 0107 QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000)); 0108 QCOMPARE(spyQuery.count(), 1); 0109 QCOMPARE(spyQuery[0][0].toLongLong(), id); 0110 KIO::AuthInfo result = spyQuery[0][2].value<KIO::AuthInfo>(); 0111 0112 // Now the check will have replied 0113 QCOMPARE(spyCheck.count(), 1); 0114 QCOMPARE(spyCheck[0][0].toLongLong(), id + 1); // it was the next request after the query 0115 KIO::AuthInfo resultCheck = spyCheck[0][2].value<KIO::AuthInfo>(); 0116 QCOMPARE(result.username, resultCheck.username); 0117 QCOMPARE(result.password, resultCheck.password); 0118 } 0119 0120 void testExpiry() 0121 { 0122 KPasswdServer server(this); 0123 server.setWalletDisabled(true); 0124 KIO::AuthInfo info; 0125 info.url = QUrl(QStringLiteral("http://www.example.com")); 0126 info.keepPassword = true; 0127 0128 // Add auth to the cache 0129 const qlonglong windowId = 42; 0130 KIO::AuthInfo realInfo = info; 0131 realInfo.username = QStringLiteral("toto"); 0132 realInfo.password = QStringLiteral("foobar"); 0133 server.addAuthInfo(realInfo, windowId); 0134 0135 QVERIFY(successCheckAuth(server, info, realInfo)); 0136 0137 // Close another window, shouldn't hurt 0138 server.removeAuthForWindowId(windowId + 1); 0139 QVERIFY(successCheckAuth(server, info, realInfo)); 0140 0141 // Close window 0142 server.removeAuthForWindowId(windowId); 0143 0144 // Check we can't find that auth anymore 0145 QVERIFY(noCheckAuth(server, info)); 0146 } 0147 0148 void testFillDialog() 0149 { 0150 KPasswdServer server(this); 0151 server.setWalletDisabled(true); 0152 // What the app would ask 0153 KIO::AuthInfo info; 0154 info.url = QUrl(QStringLiteral("http://www.example.com")); 0155 0156 // What the user would type 0157 KIO::AuthInfo filledInfo(info); 0158 filledInfo.username = QStringLiteral("dfaure"); 0159 filledInfo.password = QStringLiteral("toto"); 0160 0161 KIO::AuthInfo result; 0162 queryAuthWithDialog(server, info, filledInfo, result); 0163 } 0164 0165 void testRejectRetryDialog() 0166 { 0167 KPasswdServer server(this); 0168 server.setWalletDisabled(true); 0169 0170 // What the app would ask 0171 KIO::AuthInfo info; 0172 info.url = QUrl(QStringLiteral("http://www.example.com")); 0173 0174 // What the user would type 0175 KIO::AuthInfo filledInfo(info); 0176 filledInfo.username = QStringLiteral("username"); 0177 filledInfo.password = QStringLiteral("password"); 0178 0179 KIO::AuthInfo result; 0180 queryAuthWithDialog(server, info, filledInfo, result); 0181 0182 // Pretend that the returned credentials failed and initiate a retry, 0183 // but cancel the retry dialog. 0184 info.password.clear(); 0185 result = KIO::AuthInfo(); 0186 queryAuthWithDialog(server, info, filledInfo, result, s_buttonCancel, QDialog::Accepted /*unused*/, QStringLiteral("Invalid username or password")); 0187 } 0188 0189 void testAcceptRetryDialog() 0190 { 0191 KPasswdServer server(this); 0192 server.setWalletDisabled(true); 0193 0194 // What the app would ask 0195 KIO::AuthInfo info; 0196 info.url = QUrl(QStringLiteral("http://www.example.com")); 0197 0198 // What the user would type 0199 KIO::AuthInfo filledInfo(info); 0200 filledInfo.username = QStringLiteral("username"); 0201 filledInfo.password = QStringLiteral("password"); 0202 0203 KIO::AuthInfo result; 0204 queryAuthWithDialog(server, info, filledInfo, result); 0205 0206 // Pretend that the returned credentials failed and initiate a retry, 0207 // but this time continue the retry. 0208 info.password.clear(); 0209 result = KIO::AuthInfo(); 0210 0211 queryAuthWithDialog(server, info, filledInfo, result, s_buttonYes, QDialog::Accepted, QStringLiteral("Invalid username or password")); 0212 } 0213 0214 void testUsernameMistmatch() 0215 { 0216 KPasswdServer server(this); 0217 server.setWalletDisabled(true); 0218 0219 // What the app would ask. Note the username in the URL. 0220 KIO::AuthInfo info; 0221 info.url = QUrl(QStringLiteral("http://foo@www.example.com")); 0222 0223 // What the user would type 0224 KIO::AuthInfo filledInfo(info); 0225 filledInfo.username = QStringLiteral("bar"); 0226 filledInfo.password = QStringLiteral("blah"); 0227 0228 KIO::AuthInfo result; 0229 queryAuthWithDialog(server, info, filledInfo, result); 0230 0231 // Check the returned url does not match the request url because of the 0232 // username mismatch between the request URL and the filled in one. 0233 QVERIFY(result.url != filledInfo.url); 0234 0235 // Verify there is NO cached auth data if the request URL contains the 0236 // original user name (foo). 0237 QVERIFY(noCheckAuth(server, info)); 0238 0239 // Verify there is a cached auth data if the request URL contains the 0240 // new user name (bar). 0241 filledInfo.url = QUrl(QStringLiteral("http://bar@www.example.com")); 0242 QVERIFY(successCheckAuth(server, filledInfo, result)); 0243 0244 // Now the URL check should be valid too. 0245 QCOMPARE(result.url, filledInfo.url); 0246 } 0247 0248 void testCancelPasswordDialog() 0249 { 0250 KPasswdServer server(this); 0251 server.setWalletDisabled(true); 0252 0253 // What the app would ask. 0254 KIO::AuthInfo info; 0255 info.url = QUrl(QStringLiteral("http://www.example.com")); 0256 info.username = info.url.userName(); 0257 0258 KIO::AuthInfo result; 0259 queryAuthWithDialog(server, info, KIO::AuthInfo(), result, QDialogButtonBox::NoButton, QDialog::Rejected); 0260 } 0261 0262 void testVerifyPath() 0263 { 0264 KPasswdServer server(this); 0265 server.setWalletDisabled(true); 0266 0267 // Add auth to the cache 0268 const qlonglong windowId = 42; 0269 KIO::AuthInfo authInfo; 0270 authInfo.url = QUrl(QStringLiteral("http://www.example.com/test/test.html")); 0271 authInfo.username = QStringLiteral("toto"); 0272 authInfo.password = QStringLiteral("foobar"); 0273 authInfo.keepPassword = true; 0274 server.addAuthInfo(authInfo, windowId); 0275 0276 KIO::AuthInfo queryAuthInfo; 0277 queryAuthInfo.url = QUrl(QStringLiteral("http://www.example.com/test/test2/test.html")); 0278 queryAuthInfo.verifyPath = true; 0279 0280 KIO::AuthInfo expectedAuthInfo; 0281 expectedAuthInfo.username = QStringLiteral("toto"); 0282 expectedAuthInfo.password = QStringLiteral("foobar"); 0283 0284 QVERIFY(successCheckAuth(server, queryAuthInfo, expectedAuthInfo)); 0285 } 0286 0287 void testConcurrentQueryAuth() 0288 { 0289 KPasswdServer server(this); 0290 server.setWalletDisabled(true); 0291 0292 QList<KIO::AuthInfo> authInfos; 0293 for (int i = 0; i < 10; ++i) { 0294 KIO::AuthInfo info; 0295 info.url = QUrl(QLatin1String("http://www.example.com/test") + QString::number(i) + QLatin1String(".html")); 0296 authInfos << info; 0297 } 0298 0299 // What the user would type 0300 KIO::AuthInfo filledInfo; 0301 filledInfo.username = QStringLiteral("bar"); 0302 filledInfo.password = QStringLiteral("blah"); 0303 0304 QList<KIO::AuthInfo> results; 0305 concurrentQueryAuthWithDialog(server, authInfos, filledInfo, results); 0306 } 0307 0308 void testConcurrentCheckAuth() 0309 { 0310 KPasswdServer server(this); 0311 server.setWalletDisabled(true); 0312 0313 QList<KIO::AuthInfo> authInfos; 0314 for (int i = 0; i < 10; ++i) { 0315 KIO::AuthInfo info; 0316 info.url = QUrl(QLatin1String("http://www.example.com/test") + QString::number(i) + QStringLiteral(".html")); 0317 authInfos << info; 0318 } 0319 0320 // What the user would type 0321 KIO::AuthInfo filledInfo; 0322 filledInfo.username = QStringLiteral("bar"); 0323 filledInfo.password = QStringLiteral("blah"); 0324 0325 QList<KIO::AuthInfo> results; 0326 concurrentQueryAuthWithDialog(server, authInfos, filledInfo, results); 0327 } 0328 0329 private: 0330 // Checks that no auth is available for @p info 0331 bool noCheckAuth(KPasswdServer &server, const KIO::AuthInfo &info) 0332 { 0333 KIO::AuthInfo result; 0334 checkAuth(server, info, result); 0335 return (result.username == info.username) && (result.password == info.password) && !result.isModified(); 0336 } 0337 0338 // Check that the auth is available and equal to @expectedInfo 0339 bool successCheckAuth(KPasswdServer &server, const KIO::AuthInfo &info, const KIO::AuthInfo &expectedInfo) 0340 { 0341 KIO::AuthInfo result; 0342 checkAuth(server, info, result); 0343 return (result.username == expectedInfo.username) && (result.password == expectedInfo.password) && result.isModified(); 0344 } 0345 0346 void checkAuth(KPasswdServer &server, const KIO::AuthInfo &info, KIO::AuthInfo &result) 0347 { 0348 QSignalSpy spy(&server, &KPasswdServer::checkAuthInfoAsyncResult); 0349 const qlonglong windowId = 42; 0350 const qlonglong id = server.checkAuthInfoAsync(info, windowId, 17 /*usertime*/); 0351 QCOMPARE(id, 0LL); // always 0352 if (spy.isEmpty()) { 0353 QVERIFY(QSignalSpy(&server, &KPasswdServer::checkAuthInfoAsyncResult).wait(1000)); 0354 } 0355 QCOMPARE(spy.count(), 1); 0356 // kpasswdserver emits a requestId via dbus, we can't get that id here 0357 QVERIFY(spy[0][0].toLongLong() >= 0); 0358 // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr 0359 result = spy[0][2].value<KIO::AuthInfo>(); 0360 } 0361 0362 void queryAuth(KPasswdServer &server, const KIO::AuthInfo &info, KIO::AuthInfo &result) 0363 { 0364 QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult); 0365 const qlonglong windowId = 42; 0366 const qlonglong seqNr = 2; 0367 const qlonglong id = server.queryAuthInfoAsync(info, 0368 QStringLiteral("<NoAuthPrompt>"), // magic string to avoid a dialog 0369 windowId, 0370 seqNr, 0371 16 /*usertime*/); 0372 QVERIFY(id >= 0); // requestId, ever increasing 0373 if (spy.isEmpty()) { 0374 QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000)); 0375 } 0376 QCOMPARE(spy.count(), 1); 0377 QCOMPARE(spy[0][0].toLongLong(), id); 0378 // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr 0379 result = spy[0][2].value<KIO::AuthInfo>(); 0380 } 0381 0382 void queryAuthWithDialog(KPasswdServer &server, 0383 const KIO::AuthInfo &info, 0384 const KIO::AuthInfo &filledInfo, 0385 KIO::AuthInfo &result, 0386 QDialogButtonBox::StandardButton retryButton = s_buttonYes, 0387 QDialog::DialogCode code = QDialog::Accepted, 0388 const QString &errMsg = QString()) 0389 { 0390 QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult); 0391 const qlonglong windowId = 42; 0392 const qlonglong seqNr = 2; 0393 const qlonglong id = server.queryAuthInfoAsync(info, errMsg, windowId, seqNr, 16 /*usertime*/); 0394 QVERIFY(id >= 0); // requestId, ever increasing 0395 QVERIFY(spy.isEmpty()); 0396 0397 const bool hasErrorMessage = (!errMsg.isEmpty()); 0398 const bool isCancelRetryDialogTest = (hasErrorMessage && retryButton == s_buttonCancel); 0399 0400 if (hasErrorMessage) { 0401 auto checkRetryFunc = [this, retryButton]() { 0402 checkRetryDialog(retryButton); 0403 }; 0404 // Retry dialog only knows Yes/No 0405 QMetaObject::invokeMethod(this, checkRetryFunc, Qt::QueuedConnection); 0406 } 0407 0408 if (!isCancelRetryDialogTest) { 0409 auto checkFillFunc = [this, info, filledInfo, code]() { 0410 checkAndFillDialog(info, filledInfo, code); 0411 }; 0412 QMetaObject::invokeMethod(this, checkFillFunc, Qt::QueuedConnection); 0413 } 0414 // Force KPasswdServer to process the request now, otherwise the checkAndFillDialog needs a timer too... 0415 server.processRequest(); 0416 if (spy.isEmpty()) { 0417 QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000)); 0418 } 0419 QCOMPARE(spy.count(), 1); 0420 QCOMPARE(spy[0][0].toLongLong(), id); 0421 // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr 0422 result = spy[0][2].value<KIO::AuthInfo>(); 0423 const QString username = (isCancelRetryDialogTest ? QString() : filledInfo.username); 0424 const QString password = (isCancelRetryDialogTest ? QString() : filledInfo.password); 0425 QCOMPARE(result.username, username); 0426 QCOMPARE(result.password, password); 0427 QCOMPARE(result.isModified(), retryButton == s_buttonYes && code == QDialog::Accepted); 0428 } 0429 0430 void concurrentQueryAuthWithDialog(KPasswdServer &server, 0431 const QList<KIO::AuthInfo> &infos, 0432 const KIO::AuthInfo &filledInfo, 0433 QList<KIO::AuthInfo> &results, 0434 QDialog::DialogCode code = QDialog::Accepted) 0435 { 0436 QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult); 0437 const qlonglong windowId = 42; 0438 qlonglong seqNr = 0; 0439 QList<qlonglong> idList; 0440 0441 for (const KIO::AuthInfo &info : infos) { 0442 const qlonglong id = server.queryAuthInfoAsync(info, QString(), windowId, seqNr, 16 /*usertime*/); 0443 QVERIFY(id >= 0); // requestId, ever increasing 0444 idList << id; 0445 } 0446 0447 QVERIFY(spy.isEmpty()); 0448 auto checkFillFunc = [this, first = infos.first(), filledInfo, code]() { 0449 checkAndFillDialog(first, filledInfo, code); 0450 }; 0451 QMetaObject::invokeMethod(this, checkFillFunc, Qt::QueuedConnection); 0452 0453 // Force KPasswdServer to process the request now, otherwise the checkAndFillDialog needs a timer too... 0454 server.processRequest(); 0455 while (spy.count() < infos.count()) { 0456 QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000)); 0457 } 0458 0459 QCOMPARE(spy.count(), infos.count()); 0460 0461 for (int i = 0, count = spy.count(); i < count; ++i) { 0462 QCOMPARE(spy[i][0].toLongLong(), idList.at(i)); 0463 // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr 0464 KIO::AuthInfo result = spy[i][2].value<KIO::AuthInfo>(); 0465 QCOMPARE(result.username, filledInfo.username); 0466 QCOMPARE(result.password, filledInfo.password); 0467 QCOMPARE(result.isModified(), code == QDialog::Accepted); 0468 results << result; 0469 } 0470 } 0471 0472 void concurrentCheckAuthWithDialog(KPasswdServer &server, 0473 const QList<KIO::AuthInfo> &infos, 0474 const KIO::AuthInfo &filledInfo, 0475 QList<KIO::AuthInfo> &results, 0476 QDialog::DialogCode code = QDialog::Accepted) 0477 { 0478 QSignalSpy spy(&server, &KPasswdServer::queryAuthInfoAsyncResult); 0479 const qlonglong windowId = 42; 0480 qlonglong seqNr = 0; 0481 QList<qlonglong> idList; 0482 0483 QListIterator<KIO::AuthInfo> it(infos); 0484 if (it.hasNext()) { 0485 const qlonglong id = server.queryAuthInfoAsync(it.next(), QString(), windowId, seqNr, 16 /*usertime*/); 0486 QVERIFY(id >= 0); // requestId, ever increasing 0487 idList << id; 0488 } 0489 0490 while (it.hasNext()) { 0491 const qlonglong id = server.checkAuthInfoAsync(it.next(), windowId, 16 /*usertime*/); 0492 QVERIFY(id >= 0); // requestId, ever increasing 0493 idList << id; 0494 } 0495 0496 QVERIFY(spy.isEmpty()); 0497 auto checkAndFillFunc = [this, first = infos.first(), filledInfo, code]() { 0498 checkAndFillDialog(first, filledInfo, code); 0499 }; 0500 QMetaObject::invokeMethod(this, checkAndFillFunc, Qt::QueuedConnection); 0501 0502 // Force KPasswdServer to process the request now, otherwise the checkAndFillDialog needs a timer too... 0503 server.processRequest(); 0504 if (spy.isEmpty()) { 0505 QVERIFY(QSignalSpy(&server, &KPasswdServer::queryAuthInfoAsyncResult).wait(1000)); 0506 } 0507 0508 while ((spy.count() - 1) < infos.count()) { 0509 QVERIFY(QSignalSpy(&server, &KPasswdServer::checkAuthInfoAsyncResult).wait(1000)); 0510 } 0511 0512 for (int i = 0, count = spy.count(); i < count; ++i) { 0513 QCOMPARE(spy[i][0].toLongLong(), idList.at(i)); 0514 // QCOMPARE(spy[0][1].toLongLong(), 3LL); // seqNr 0515 KIO::AuthInfo result = spy[i][2].value<KIO::AuthInfo>(); 0516 QCOMPARE(result.username, filledInfo.username); 0517 QCOMPARE(result.password, filledInfo.password); 0518 QCOMPARE(result.isModified(), code == QDialog::Accepted); 0519 results << result; 0520 } 0521 } 0522 0523 protected Q_SLOTS: 0524 void checkAndFillDialog(const KIO::AuthInfo &info, const KIO::AuthInfo &filledInfo, QDialog::DialogCode code) 0525 { 0526 const QList<QWidget *> widgetsList = QApplication::topLevelWidgets(); 0527 for (QWidget *widget : widgetsList) { 0528 if (KPasswordDialog *dialog = qobject_cast<KPasswordDialog *>(widget)) { 0529 qDebug() << "Found dialog" << dialog; 0530 if (code == QDialog::Accepted) { 0531 QCOMPARE(dialog->username(), getUserNameFrom(info)); 0532 QCOMPARE(dialog->password(), info.password); 0533 dialog->setUsername(filledInfo.username); 0534 dialog->setPassword(filledInfo.password); 0535 qDebug() << "Filled dialog with" << filledInfo.username << filledInfo.password; 0536 } 0537 dialog->done(code); 0538 return; 0539 } 0540 } 0541 qWarning() << "No KPasswordDialog found!"; 0542 } 0543 0544 void checkRetryDialog(QDialogButtonBox::StandardButton code = s_buttonYes) 0545 { 0546 const QList<QWidget *> widgetsList = QApplication::topLevelWidgets(); 0547 for (QWidget *widget : widgetsList) { 0548 QDialog *dialog = qobject_cast<QDialog *>(widget); 0549 if (dialog && !dialog->inherits("KPasswordDialog")) { 0550 qDebug() << "Closing dialog" << dialog << "with code" << code; 0551 dialog->done(code); 0552 return; 0553 } 0554 } 0555 qWarning() << "No retry dialog found"; 0556 } 0557 }; 0558 0559 QTEST_MAIN(KPasswdServerTest) 0560 0561 #include "kpasswdservertest.moc"