[Ktutorial-commits] SF.net SVN: ktutorial:[343] trunk/ktutorial
Status: Alpha
Brought to you by:
danxuliu
From: <dan...@us...> - 2012-06-26 21:53:36
|
Revision: 343 http://ktutorial.svn.sourceforge.net/ktutorial/?rev=343&view=rev Author: danxuliu Date: 2012-06-26 21:53:28 +0000 (Tue, 26 Jun 2012) Log Message: ----------- Get rid of several fixed time waits that could make the tests fail in slow systems. Now, instead of waiting a fixed amount of time, it is checked if some condition was met before continuing. If the condition was not met, it is checked again after a little time. The retries continue until the timeout expires. Modified Paths: -------------- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForPropertyWidgetTest.cpp trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp Modified: trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp 2012-06-20 18:25:57 UTC (rev 342) +++ trunk/ktutorial/ktutorial-editor/tests/unit/targetapplication/TargetApplicationTest.cpp 2012-06-26 21:53:28 UTC (rev 343) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -76,74 +76,9 @@ QString mTargetApplicationStubPath; -}; + bool waitForSignalCount(const QSignalSpy* spy, int count, + int timeout) const; -/** - * Modified version of private KSignalSpy found in - * kdelibs/kdecore/util/qtest_kde.cpp. - * Original KDESignalSpy, accessed through - * QTest::kWaitForSignal(QObject*, const char*, int), can miss a signal if it is - * emitted too quickly (that is, before the connect is reached). This modified - * version, instead of starting the wait in the constructor, has a specific - * method for it. So the object can be created before executing the call that - * emits the signal, enabling it to register the signal before starting to wait - * and thus ensuring that no signal will be missed. - * - * Moreover, this modified version allows to wait for up to n emitted signals - * instead of only the first one. Note that the count isn't reseted after - * receiving all the expected signals, so if you wait for more signals use an - * absolute value (that is, the already received signals plus those you want to - * wait for now). - */ -class SignalWait: public QObject { -Q_OBJECT -public: - - SignalWait(QObject* object, const char* signal): - QObject(0), - mSignalSpy(object, signal), - mExpectedCount(0) { - connect(object, signal, this, SLOT(signalEmitted())); - } - - bool waitForCount(int expectedCount, int timeout) { - if (mSignalSpy.count() < expectedCount) { - mExpectedCount = expectedCount; - - if (timeout > 0) { - QObject::connect(&mTimer, SIGNAL(timeout()), &mLoop, SLOT(quit())); - mTimer.setSingleShot(true); - mTimer.start(timeout); - } - mLoop.exec(); - } - - if (mSignalSpy.count() >= expectedCount) { - return true; - } - return false; - } - - const QSignalSpy& spy() const { - return mSignalSpy; - } - -private Q_SLOTS: - - void signalEmitted() { - if (mSignalSpy.count() >= mExpectedCount) { - mTimer.stop(); - mLoop.quit(); - } - } - -private: - - QSignalSpy mSignalSpy; - int mExpectedCount; - QEventLoop mLoop; - QTimer mTimer; - }; void TargetApplicationTest::initTestCase() { @@ -195,10 +130,10 @@ TargetApplication targetApplication; targetApplication.setTargetApplicationFilePath(mTargetApplicationStubPath); - SignalWait startedWait(&targetApplication, SIGNAL(started())); + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); targetApplication.start(); - QVERIFY(startedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&startedSpy, 1, 10000)); QVERIFY(targetApplication.remoteEditorSupport()); QCOMPARE(targetApplication.remoteEditorSupport()->mainWindow()->name(), QString("The object name 42")); @@ -210,10 +145,10 @@ QApplication::applicationDirPath() + "/TargetApplicationStubWithDelayedRegister"); - SignalWait startedWait(&targetApplication, SIGNAL(started())); + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); targetApplication.start(); - QVERIFY(startedWait.waitForCount(1, 2000)); + QVERIFY(waitForSignalCount(&startedSpy, 1, 10000)); QVERIFY(targetApplication.remoteEditorSupport()); QCOMPARE(targetApplication.remoteEditorSupport()->mainWindow()->name(), QString("The object name 42")); @@ -223,16 +158,17 @@ TargetApplication targetApplication; targetApplication.setTargetApplicationFilePath(mTargetApplicationStubPath); - SignalWait startedWait(&targetApplication, SIGNAL(started())); + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); targetApplication.start(); - QVERIFY(startedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&startedSpy, 1, 10000)); RemoteEditorSupport* oldRemoteEditorSupport = targetApplication.remoteEditorSupport(); targetApplication.start(); - QVERIFY(!startedWait.waitForCount(2, 1000)); + //Give time to ensure that "started" signal is not sent again + QVERIFY(!waitForSignalCount(&startedSpy, 2, 1000)); QCOMPARE(targetApplication.remoteEditorSupport(), oldRemoteEditorSupport); QCOMPARE(targetApplication.remoteEditorSupport()->mainWindow()->name(), QString("The object name 42")); @@ -243,9 +179,9 @@ TargetApplication targetApplication; targetApplication.setTargetApplicationFilePath(mTargetApplicationStubPath); - SignalWait startedWait(&targetApplication, SIGNAL(started())); + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); targetApplication.start(); - QVERIFY(startedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&startedSpy, 1, 10000)); RemoteEditorSupport* oldRemoteEditorSupport = targetApplication.remoteEditorSupport(); @@ -255,7 +191,8 @@ targetApplication.start(); - QVERIFY(!startedWait.waitForCount(2, 1000)); + //Give time to ensure that "started" signal is not sent again + QVERIFY(!waitForSignalCount(&startedSpy, 2, 1000)); QCOMPARE(targetApplication.remoteEditorSupport(), oldRemoteEditorSupport); QCOMPARE(targetApplication.remoteEditorSupport()->mainWindow()->name(), QString("The object name 42")); @@ -269,28 +206,29 @@ TargetApplication targetApplication; targetApplication.setTargetApplicationFilePath(mTargetApplicationStubPath); - SignalWait startedWait(&targetApplication, SIGNAL(started())); + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); targetApplication.start(); - QVERIFY(startedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&startedSpy, 1, 10000)); QVERIFY(targetApplication.remoteEditorSupport()); targetApplication.setTargetApplicationFilePath("/other/executable"); - SignalWait finishedWait(&targetApplication, SIGNAL(finished())); + QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); //TargetApplication::Error must be registered in order to be used with //QSignalSpy qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); - SignalWait startFailedWait(&targetApplication, - SIGNAL(startFailed(TargetApplication::Error))); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); targetApplication.start(); - QVERIFY(finishedWait.waitForCount(1, 1000)); - QVERIFY(startFailedWait.waitForCount(1, 1000)); - QVariant argument = startFailedWait.spy().at(0).at(0); + QVERIFY(waitForSignalCount(&finishedSpy, 1, 10000)); + QVERIFY(waitForSignalCount(&startFailedSpy, 1, 10000)); + QVariant argument = startFailedSpy.at(0).at(0); QCOMPARE(qvariant_cast<TargetApplication::Error>(argument), TargetApplication::InvalidPath); - QVERIFY(!startedWait.waitForCount(2, 1000)); + //Give time to ensure that "started" signal is not sent again + QVERIFY(!waitForSignalCount(&startedSpy, 2, 1000)); QVERIFY(!targetApplication.remoteEditorSupport()); } @@ -298,20 +236,20 @@ TargetApplication targetApplication; targetApplication.setTargetApplicationFilePath(mTargetApplicationStubPath); - SignalWait startedWait(&targetApplication, SIGNAL(started())); + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); targetApplication.start(); - QVERIFY(startedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&startedSpy, 1, 10000)); RemoteEditorSupport* oldRemoteEditorSupport = targetApplication.remoteEditorSupport(); - SignalWait finishedWait(&targetApplication, SIGNAL(finished())); + QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); targetApplication.mProcess->kill(); - QVERIFY(finishedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&finishedSpy, 1, 10000)); targetApplication.start(); - QVERIFY(startedWait.waitForCount(2, 1000)); + QVERIFY(waitForSignalCount(&startedSpy, 2, 10000)); QVERIFY(targetApplication.remoteEditorSupport()); QVERIFY(targetApplication.remoteEditorSupport() != oldRemoteEditorSupport); QCOMPARE(targetApplication.remoteEditorSupport()->mainWindow()->name(), @@ -325,15 +263,15 @@ //TargetApplication::Error must be registered in order to be used with //QSignalSpy qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); - SignalWait startFailedWait(&targetApplication, - SIGNAL(startFailed(TargetApplication::Error))); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); targetApplication.start(); - QVERIFY(startFailedWait.waitForCount(1, 1000)); - QVariant argument = startFailedWait.spy().at(0).at(0); + QVERIFY(waitForSignalCount(&startFailedSpy, 1, 10000)); + QVariant argument = startFailedSpy.at(0).at(0); QCOMPARE(qvariant_cast<TargetApplication::Error>(argument), TargetApplication::InvalidPath); QCOMPARE(startedSpy.count(), 0); @@ -347,15 +285,15 @@ //TargetApplication::Error must be registered in order to be used with //QSignalSpy qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); - SignalWait startFailedWait(&targetApplication, - SIGNAL(startFailed(TargetApplication::Error))); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); targetApplication.start(); - QVERIFY(startFailedWait.waitForCount(1, 1000)); - QVariant argument = startFailedWait.spy().at(0).at(0); + QVERIFY(waitForSignalCount(&startFailedSpy, 1, 10000)); + QVariant argument = startFailedSpy.at(0).at(0); QCOMPARE(qvariant_cast<TargetApplication::Error>(argument), TargetApplication::InvalidPath); QCOMPARE(startedSpy.count(), 0); @@ -370,16 +308,16 @@ //TargetApplication::Error must be registered in order to be used with //QSignalSpy qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); - SignalWait startFailedWait(&targetApplication, - SIGNAL(startFailed(TargetApplication::Error))); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); targetApplication.start(); //Time out higher than the internal TargetApplication time out - QVERIFY(startFailedWait.waitForCount(1, 3500)); - QVariant argument = startFailedWait.spy().at(0).at(0); + QVERIFY(waitForSignalCount(&startFailedSpy, 1, 10000)); + QVariant argument = startFailedSpy.at(0).at(0); QCOMPARE(qvariant_cast<TargetApplication::Error>(argument), TargetApplication::InvalidApplication); QCOMPARE(startedSpy.count(), 0); @@ -390,14 +328,14 @@ TargetApplication targetApplication; targetApplication.setTargetApplicationFilePath(mTargetApplicationStubPath); - SignalWait startedWait(&targetApplication, SIGNAL(started())); + QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); targetApplication.start(); - QVERIFY(startedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&startedSpy, 1, 10000)); - SignalWait finishedWait(&targetApplication, SIGNAL(finished())); + QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); targetApplication.mProcess->kill(); - QVERIFY(finishedWait.waitForCount(1, 1000)); + QVERIFY(waitForSignalCount(&finishedSpy, 1, 10000)); QVERIFY(!targetApplication.remoteEditorSupport()); } @@ -409,8 +347,8 @@ //TargetApplication::Error must be registered in order to be used with //QSignalSpy qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); - SignalWait startFailedWait(&targetApplication, - SIGNAL(startFailed(TargetApplication::Error))); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); @@ -422,8 +360,8 @@ targetApplication.mProcess->kill(); - QVERIFY(startFailedWait.waitForCount(1, 1000)); - QVariant argument = startFailedWait.spy().at(0).at(0); + QVERIFY(waitForSignalCount(&startFailedSpy, 1, 10000)); + QVariant argument = startFailedSpy.at(0).at(0); QCOMPARE(qvariant_cast<TargetApplication::Error>(argument), TargetApplication::InvalidApplication); QVERIFY(!targetApplication.remoteEditorSupport()); @@ -433,7 +371,7 @@ QTest::qWait(3000); QCOMPARE(startedSpy.count(), 0); - QCOMPARE(startFailedWait.spy().count(), 1); + QCOMPARE(startFailedSpy.count(), 1); QCOMPARE(finishedSpy.count(), 0); } @@ -446,8 +384,8 @@ //TargetApplication::Error must be registered in order to be used with //QSignalSpy qRegisterMetaType<TargetApplication::Error>("TargetApplication::Error"); - SignalWait startFailedWait(&targetApplication, - SIGNAL(startFailed(TargetApplication::Error))); + QSignalSpy startFailedSpy(&targetApplication, + SIGNAL(startFailed(TargetApplication::Error))); QSignalSpy startedSpy(&targetApplication, SIGNAL(started())); QSignalSpy finishedSpy(&targetApplication, SIGNAL(finished())); @@ -459,8 +397,8 @@ targetApplication.mProcess->kill(); - QVERIFY(startFailedWait.waitForCount(1, 1000)); - QVariant argument = startFailedWait.spy().at(0).at(0); + QVERIFY(waitForSignalCount(&startFailedSpy, 1, 10000)); + QVariant argument = startFailedSpy.at(0).at(0); QCOMPARE(qvariant_cast<TargetApplication::Error>(argument), TargetApplication::InvalidApplication); QVERIFY(!targetApplication.remoteEditorSupport()); @@ -470,10 +408,45 @@ QTest::qWait(3000); QCOMPARE(startedSpy.count(), 0); - QCOMPARE(startFailedWait.spy().count(), 1); + QCOMPARE(startFailedSpy.count(), 1); QCOMPARE(finishedSpy.count(), 0); } +/////////////////////////////////// Helpers //////////////////////////////////// + +template <typename Class> +bool waitFor(Class &object, bool (Class::*condition)() const, int timeout) { + QElapsedTimer timer; + timer.start(); + do { + QTest::qWait(100); + } while (!(object.*condition)() && timer.elapsed() < timeout); + + if (timer.elapsed() >= timeout) { + return false; + } + + return true; +} + +class SignalCountCondition { +public: + const QSignalSpy* mSpy; + int mCount; + + bool condition() const { + return mSpy->count() == mCount; + } +}; + +bool TargetApplicationTest::waitForSignalCount(const QSignalSpy* spy, int count, + int timeout) const { + SignalCountCondition helper; + helper.mSpy = spy; + helper.mCount = count; + return waitFor(helper, &SignalCountCondition::condition, timeout); +} + QTEST_MAIN(TargetApplicationTest) #include "TargetApplicationTest.moc" Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2012-06-20 18:25:57 UTC (rev 342) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectChooserTest.cpp 2012-06-26 21:53:28 UTC (rev 343) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -82,6 +82,9 @@ void killTargetApplication(int timeToWait); + bool waitForTargetApplicationToStart(int timeout) const; + bool waitForTargetApplicationToStop(int timeout) const; + QCheckBox* showOnlyNamedObjectsCheckBox(RemoteObjectChooser* widget) const; QCheckBox* showOnlyWidgetsCheckBox(RemoteObjectChooser* widget) const; @@ -136,8 +139,7 @@ RemoteObjectChooser* chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QVERIFY(chooser->isVisible()); QVERIFY(!window.isVisible()); @@ -152,8 +154,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QWidget window(0, Qt::Window); window.show(); @@ -239,16 +240,14 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); //Queue closing the sorry message box closeSorryMessageBox(chooser, 1000); TargetApplication::self()->mProcess->kill(); - //Give the target application time to be killed - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStop(10000)); //Process deleteLater() QCoreApplication::sendPostedEvents(chooser, QEvent::DeferredDelete); @@ -309,8 +308,7 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); chooser->close(); @@ -331,8 +329,7 @@ RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QVERIFY(remoteObjectsTreeView(chooser)->model()); @@ -357,8 +354,7 @@ RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QVERIFY(remoteObjectsTreeView(chooser)->model()); @@ -386,8 +382,7 @@ RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QVERIFY(remoteObjectsTreeView(chooser)->model()); @@ -423,8 +418,7 @@ RemoteObjectChooser* chooser = new RemoteObjectChooser(&window); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QVERIFY(remoteObjectsTreeView(chooser)->model()); QModelIndex index = remoteObjectsTreeView(chooser)->model()->index(1, 0); @@ -471,8 +465,7 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QVERIFY(remoteObjectsTreeView(chooser)->model()); QModelIndex index = remoteObjectsTreeView(chooser)->model()->index(1, 0); @@ -516,8 +509,7 @@ QPointer<RemoteObjectChooser> chooser = new RemoteObjectChooser(dialog); chooser->show(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QVERIFY(remoteObjectsTreeView(chooser)->model()); QModelIndex index = remoteObjectsTreeView(chooser)->model()->index(1, 0); @@ -615,6 +607,38 @@ QTimer::singleShot(timeToWait, helper, SLOT(killTargetApplication())); } +bool waitFor(bool (*condition)(), int timeout) { + QElapsedTimer timer; + timer.start(); + do { + QTest::qWait(100); + } while (!condition() && timer.elapsed() < timeout); + + if (timer.elapsed() >= timeout) { + return false; + } + + return true; +} + +bool isTargetApplicationStarted() { + return TargetApplication::self()->remoteEditorSupport(); +} + +bool isTargetApplicationStopped() { + return !TargetApplication::self()->remoteEditorSupport(); +} + +bool RemoteObjectChooserTest::waitForTargetApplicationToStart( + int timeout) const { + return waitFor(&isTargetApplicationStarted, timeout); +} + +bool RemoteObjectChooserTest::waitForTargetApplicationToStop( + int timeout) const { + return waitFor(&isTargetApplicationStopped, timeout); +} + QCheckBox* RemoteObjectChooserTest::showOnlyNamedObjectsCheckBox( RemoteObjectChooser* widget) const { return widget->findChild<QCheckBox*>("showOnlyNamedObjectsCheckBox"); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-06-20 18:25:57 UTC (rev 342) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameRegisterTest.cpp 2012-06-26 21:53:28 UTC (rev 343) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2011 by Daniel Calviño Sánchez * + * Copyright (C) 2011-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -65,6 +65,9 @@ QString mPath; + bool waitForTargetApplicationToStart(int timeout) const; + bool waitForTargetApplicationToStop(int timeout) const; + void assertNames(const QStringList& names) const; }; @@ -88,8 +91,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -106,8 +108,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); assertNames(remoteObjectNameRegister.names()); QCOMPARE(nameAddedSpy.count(), 82); @@ -144,8 +145,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -156,8 +156,7 @@ TargetApplication::self()->mProcess->kill(); - //Give the target application time to stop - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStop(10000)); QVERIFY(remoteObjectNameRegister.names().isEmpty()); QCOMPARE(nameAddedSpy.count(), 0); @@ -174,8 +173,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -190,8 +188,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -207,8 +204,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -225,8 +221,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -242,8 +237,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -255,8 +249,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -273,8 +266,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -292,8 +284,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -311,8 +302,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -330,8 +320,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -349,8 +338,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -365,8 +353,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -385,8 +372,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameRegister remoteObjectNameRegister; @@ -399,6 +385,38 @@ /////////////////////////////////// Helpers //////////////////////////////////// +bool waitFor(bool (*condition)(), int timeout) { + QElapsedTimer timer; + timer.start(); + do { + QTest::qWait(100); + } while (!condition() && timer.elapsed() < timeout); + + if (timer.elapsed() >= timeout) { + return false; + } + + return true; +} + +bool isTargetApplicationStarted() { + return TargetApplication::self()->remoteEditorSupport(); +} + +bool isTargetApplicationStopped() { + return !TargetApplication::self()->remoteEditorSupport(); +} + +bool RemoteObjectNameRegisterTest::waitForTargetApplicationToStart( + int timeout) const { + return waitFor(&isTargetApplicationStarted, timeout); +} + +bool RemoteObjectNameRegisterTest::waitForTargetApplicationToStop( + int timeout) const { + return waitFor(&isTargetApplicationStopped, timeout); +} + void RemoteObjectNameRegisterTest::assertNames(const QStringList& names) const { QCOMPARE(names.count(), 82); QVERIFY(names.contains("The object name 42")); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2012-06-20 18:25:57 UTC (rev 342) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/RemoteObjectNameWidgetTest.cpp 2012-06-26 21:53:28 UTC (rev 343) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -82,6 +82,9 @@ KLineEdit* objectNameLineEdit(RemoteObjectNameWidget* widget) const; + bool waitForTargetApplicationToStart(int timeout) const; + bool waitForTargetApplicationToStop(int timeout) const; + void assertRemoteObjectSignal(const QSignalSpy& spy, int index, const RemoteObject* remoteObject) const; @@ -113,8 +116,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); QWidget parent; RemoteObjectNameWidget* widget = new RemoteObjectNameWidget(&parent); @@ -129,8 +131,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, @@ -151,8 +152,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, @@ -173,8 +173,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, @@ -191,8 +190,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, @@ -214,8 +212,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, @@ -252,8 +249,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; @@ -281,8 +277,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; KLineEdit* lineEdit = objectNameLineEdit(&widget); @@ -323,8 +318,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); items = objectNameLineEdit(&widget)->completionObject()->items(); assertCompletionItems(items); @@ -344,8 +338,7 @@ TargetApplication::self()->setTargetApplicationFilePath(mPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); RemoteObjectNameWidget widget; QSignalSpy remoteObjectChosenSpy(&widget, @@ -353,8 +346,7 @@ TargetApplication::self()->mProcess->kill(); - //Give the target application time to stop - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStop(10000)); QStringList items = objectNameLineEdit(&widget)->completionObject()-> items(); @@ -428,6 +420,38 @@ return widget->findChild<KLineEdit*>("objectNameLineEdit"); } +bool waitFor(bool (*condition)(), int timeout) { + QElapsedTimer timer; + timer.start(); + do { + QTest::qWait(100); + } while (!condition() && timer.elapsed() < timeout); + + if (timer.elapsed() >= timeout) { + return false; + } + + return true; +} + +bool isTargetApplicationStarted() { + return TargetApplication::self()->remoteEditorSupport(); +} + +bool isTargetApplicationStopped() { + return !TargetApplication::self()->remoteEditorSupport(); +} + +bool RemoteObjectNameWidgetTest::waitForTargetApplicationToStart( + int timeout) const { + return waitFor(&isTargetApplicationStarted, timeout); +} + +bool RemoteObjectNameWidgetTest::waitForTargetApplicationToStop( + int timeout) const { + return waitFor(&isTargetApplicationStopped, timeout); +} + //RemoteObject* must be declared as a metatype to be used in qvariant_cast Q_DECLARE_METATYPE(RemoteObject*); Modified: trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForPropertyWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForPropertyWidgetTest.cpp 2012-06-20 18:25:57 UTC (rev 342) +++ trunk/ktutorial/ktutorial-editor/tests/unit/view/WaitForPropertyWidgetTest.cpp 2012-06-26 21:53:28 UTC (rev 343) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2011 by Daniel Calviño Sánchez * + * Copyright (C) 2011-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -54,6 +54,8 @@ KLineEdit* propertyNameLineEdit(WaitForPropertyWidget* widget) const; KLineEdit* valueLineEdit(WaitForPropertyWidget* widget) const; + bool waitForTargetApplicationToStart(int timeout) const; + }; void WaitForPropertyWidgetTest::init() { @@ -116,8 +118,7 @@ mTargetApplicationStubPath); TargetApplication::self()->start(); - //Give the target application time to start - QTest::qWait(1000); + QVERIFY(waitForTargetApplicationToStart(10000)); WaitForProperty waitFor; WaitForPropertyWidget widget(&waitFor); @@ -156,6 +157,29 @@ return widget->findChild<KLineEdit*>("valueLineEdit"); } +bool waitFor(bool (*condition)(), int timeout) { + QElapsedTimer timer; + timer.start(); + do { + QTest::qWait(100); + } while (!condition() && timer.elapsed() < timeout); + + if (timer.elapsed() >= timeout) { + return false; + } + + return true; +} + +bool isTargetApplicationStarted() { + return TargetApplication::self()->remoteEditorSupport(); +} + +bool WaitForPropertyWidgetTest::waitForTargetApplicationToStart( + int timeout) const { + return waitFor(&isTargetApplicationStarted, timeout); +} + QTEST_MAIN(WaitForPropertyWidgetTest) #include "WaitForPropertyWidgetTest.moc" Modified: trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2012-06-20 18:25:57 UTC (rev 342) +++ trunk/ktutorial/ktutorial-library/tests/view/StepTextWidgetTest.cpp 2012-06-26 21:53:28 UTC (rev 343) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2010-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2010-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -64,6 +64,8 @@ QPoint centerOfText(const StepTextWidget& widget, const QString& text); + bool waitForHighlighterToStop(const QWidget* widget, int timeout) const; + void showContextMenuAndSelectFirstOption(const StepTextWidget& widget, const QPoint& position); @@ -160,10 +162,7 @@ QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, position, 500); - //Give the highlighter time to stop - QTest::qWait(500); - - QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); + QVERIFY(waitForHighlighterToStop(widgetToHighlight, 10000)); } void StepTextWidgetTest::testHighlightWidgetUsingContextMenu() { @@ -198,13 +197,11 @@ QPoint position = centerOfText(widget, "widget to highlight"); showContextMenuAndSelectFirstOption(widget, position); + //Give the highlighter time to start QTest::qWait(500); showContextMenuAndSelectFirstOption(widget, position); - //Give the highlighter time to stop - QTest::qWait(1000); - - QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); + QVERIFY(waitForHighlighterToStop(widgetToHighlight, 10000)); } void StepTextWidgetTest::testCancelContextMenu() { @@ -267,8 +264,7 @@ QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, position2, 500); - //Give the highlighter time to stop - QTest::qWait(500); + QVERIFY(waitForHighlighterToStop(widgetToHighlight1, 10000)); QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); QVERIFY(widgetToHighlight2->findChild<WidgetHighlighter*>("")); @@ -276,8 +272,7 @@ showContextMenuAndSelectFirstOption(widget, position3); - //Give the highlighter time to stop - QTest::qWait(500); + QVERIFY(waitForHighlighterToStop(widgetToHighlight2, 10000)); QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); @@ -285,8 +280,7 @@ showContextMenuAndSelectFirstOption(widget, position1); - //Give the highlighter time to stop - QTest::qWait(500); + QVERIFY(waitForHighlighterToStop(widgetToHighlight3, 10000)); QVERIFY(widgetToHighlight1->findChild<WidgetHighlighter*>("")); QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); @@ -295,8 +289,7 @@ QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, position1, 500); - //Give the highlighter time to stop - QTest::qWait(1000); + QVERIFY(waitForHighlighterToStop(widgetToHighlight1, 10000)); QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); @@ -304,11 +297,11 @@ QTest::mouseClick(widget.viewport(), Qt::LeftButton, Qt::NoModifier, position2, 500); + //Give the highlighter time to start QTest::qWait(500); showContextMenuAndSelectFirstOption(widget, position2); - //Give the highlighter time to stop - QTest::qWait(1000); + QVERIFY(waitForHighlighterToStop(widgetToHighlight2, 10000)); QVERIFY(!widgetToHighlight1->findChild<WidgetHighlighter*>("")); QVERIFY(!widgetToHighlight2->findChild<WidgetHighlighter*>("")); @@ -356,10 +349,7 @@ mainWindow.activateWindow(); widgetToHighlight->setFocus(); - //Give the highlighter time to stop - QTest::qWait(500); - - QVERIFY(!widgetToHighlight->findChild<WidgetHighlighter*>("")); + QVERIFY(waitForHighlighterToStop(widgetToHighlight, 10000)); } void StepTextWidgetTest::testSetTextWhenWidgetIsBeingHighlighted() { @@ -440,6 +430,37 @@ return widget.cursorRect(cursor).center(); } +template <typename Class> +bool waitFor(Class &object, bool (Class::*condition)() const, int timeout) { + QElapsedTimer timer; + timer.start(); + do { + QTest::qWait(100); + } while (!(object.*condition)() && timer.elapsed() < timeout); + + if (timer.elapsed() >= timeout) { + return false; + } + + return true; +} + +class HighlighterStoppedCondition { +public: + const QWidget* mWidget; + + bool condition() const { + return !mWidget->findChild<WidgetHighlighter*>(""); + } +}; + +bool StepTextWidgetTest::waitForHighlighterToStop(const QWidget* widget, + int timeout) const { + HighlighterStoppedCondition helper; + helper.mWidget = widget; + return waitFor(helper, &HighlighterStoppedCondition::condition, timeout); +} + //The context menu contains its own event loop, so it won't return to the test //code until it is closed. Thus, the commands to execute on the menu must be //"queued", as calling QTest::keyClick after showing the menu won't work. Modified: trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp =================================================================== --- trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp 2012-06-20 18:25:57 UTC (rev 342) +++ trunk/ktutorial/ktutorial-library/tests/view/StepWidgetTest.cpp 2012-06-26 21:53:28 UTC (rev 343) @@ -1,5 +1,5 @@ /*************************************************************************** - * Copyright (C) 2009-2011 by Daniel Calviño Sánchez * + * Copyright (C) 2009-2012 by Daniel Calviño Sánchez * * dan...@gm... * * * * This program is free software; you can redistribute it and/or modify * @@ -82,6 +82,9 @@ StepTextWidget* textWidget(StepWidget* stepWidget); KPushButton* closeButton(StepWidget* stepWidget); + bool waitForWidgetToMove(const QWidget* widget, + const QPoint& previousPosition, int timeout) const; + void queueAssertWidgetDragged(StepWidget* stepWidget, int timeToWait); }; @@ -232,6 +235,9 @@ QTest::mousePress(&stepWidget, Qt::LeftButton, Qt::NoModifier, QPoint()); QCursor::setPos(previousPosition + widgetCenter + QPoint(42, 23)); + + QVERIFY(waitForWidgetToMove(&stepWidget, previousPosition, 10000)); + QTest::mouseRelease(&stepWidget, Qt::LeftButton, Qt::NoModifier, QPoint(), 500); stepWidget.setMouseTracking(false); @@ -263,10 +269,14 @@ QTest::mousePress(&stepWidget, Qt::LeftButton, Qt::NoModifier, QPoint()); QCursor::setPos(previousPosition + widgetCenter + QPoint(42, 23)); + + QVERIFY(waitForWidgetToMove(&stepWidget, previousPosition, 10000)); + QTest::mouseRelease(&stepWidget, Qt::LeftButton, Qt::NoModifier, QPoint(), 500); QCursor::setPos(previousPosition + widgetCenter + QPoint(42, 23) + QPoint(16, 15)); + //Give the cursor time to move QTest::qWait(500); stepWidget.setMouseTracking(false); @@ -362,6 +372,43 @@ return stepWidget->ui->closeButton; } +template <typename Class> +bool waitFor(Class &object, bool (Class::*condition)() const, int timeout) { + QElapsedTimer timer; + timer.start(); + do { + QTest::qWait(100); + } while (!(object.*condition)() && timer.elapsed() < timeout); + + if (timer.elapsed() >= timeout) { + return false; + } + + return true; +} + +class WidgetMovedCondition { +public: + const QWidget* mWidget; + QPoint mPreviousPosition; + + bool condition() const { + return mPreviousPosition.x() + 41 <= mWidget->pos().x() && + mPreviousPosition.x() + 43 >= mWidget->pos().x() && + mPreviousPosition.y() + 22 <= mWidget->pos().y() && + mPreviousPosition.y() + 24 >= mWidget->pos().y(); + } +}; + +bool StepWidgetTest::waitForWidgetToMove(const QWidget* widget, + const QPoint& previousPosition, + int timeout) const { + WidgetMovedCondition helper; + helper.mWidget = widget; + helper.mPreviousPosition = previousPosition; + return waitFor(helper, &WidgetMovedCondition::condition, timeout); +} + //Modal dialogs don't return to the test code until they are closed. Thus, the //actions or asserts to be performed while a modal dialog is being shown (like //checking the position of the widget) must be "queued". @@ -394,16 +441,19 @@ //does if tracking isn't enabled mStepWidget->setMouseTracking(true); QTest::mousePress(mStepWidget, Qt::LeftButton, - Qt::NoModifier, QPoint(), 500); + Qt::NoModifier, QPoint()); QCursor::setPos(previousPosition + widgetCenter + QPoint(42, 23)); + + //waitForWidgetToMove is a method of StepWidgetTest, so it can not be + //called here + WidgetMovedCondition helper; + helper.mWidget = mStepWidget; + helper.mPreviousPosition = previousPosition; + QVERIFY(waitFor(helper, &WidgetMovedCondition::condition, 10000)); + QTest::mouseRelease(mStepWidget, Qt::LeftButton, Qt::NoModifier, QPoint(), 500); mStepWidget->setMouseTracking(false); - - QVERIFY(previousPosition.x() + 41 <= mStepWidget->pos().x() && - previousPosition.x() + 43 >= mStepWidget->pos().x()); - QVERIFY(previousPosition.y() + 22 <= mStepWidget->pos().y() && - previousPosition.y() + 24 >= mStepWidget->pos().y()); } private: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |