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